Camera in Kivy with OpenCV takes a lot of time to load - python

The title says it all, the loading of camera video takes a lot of time and stays in "Not Responding" at that time.
It takes almost 1 to 2 minutes to load the camera.
This is my code.
class Attendance(Screen):
def on_enter(self, *args):
self.image = Image()
self.capture = cv2.VideoCapture(0)
Clock.schedule_interval(self.load_video, 1.0/30.0)
self.add_widget(self.image)
return super().on_enter(*args)
def load_video(self, *args):
frame = self.capture.read()
self.iamge_frame = frame
buffer = cv2.flip(frame, 0).tobytes()
texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
texture.blit_buffer(buffer, colorfmt='bgr',bufferfmt = 'ubyte')
self.image.texture = texture

I fixed this by changing self.capture to the following:
self.capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)

Related

Numpy array sharing between multiprocessing instances

I am running a GUI with kivy, and simultaneously processing some realtime 3D scanner data. I'm using a multiprocessing instance to process the scan data, while the kivy GUI runs. The scanner data is read in by OpenCV, frame by frame.
My issue is that I can share the scanner data with the kivy instance, in order to display it within the GUI. I have tried making the the array "frame" global, but that doesn't seem to be doing the trick.
I have read about the multiprocessing manager, but im unclear on how to use it to manage a numpy array.
frame = np.zeros((480,640))
class CamApp(App):
def update_slider(self, instance, value):
#print(value)
self.slider_value = value*3
def build(self):
self.img1=Image()
layout = GridLayout(cols = 1)
layout.add_widget(self.img1)
Clock.schedule_interval(self.update, 1.0/33.0)
return layout
def update(self, dt):
global frame
#format as texture
buf1 = cv2.flip(frame, 0)
buf = buf1.tobytes()
texture1 = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
texture1.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
# display image from the texture
self.img1.texture = texture1
def cam(message):
print(message)
dc = DepthCamera()
global frame
while True:
ret, depth_frame, colour_frame = dc.get_frame()
frame = cv2.applyColorMap(cv2.convertScaleAbs(depth_frame, alpha=0.2), cv2.COLORMAP_BONE)
#cv2.imshow("Depth frame", frame)
#cv2.imshow("Color frame", colour_frame)
#key = cv2.waitKey(1)
if __name__ == '__main__':
p = Process(target=cam, args=('beginning capture',))
p.start()
CamApp().run()
cv2.destroyAllWindows()

Frame delayed in OpenCV and IP cam Hikvision

I have a task to process a streaming video from Hikvision IP cam using OpenCV
I try this
RTSP
"""
cap = cv2.VideoCapture()
cap.open("rtsp://yourusername:yourpassword#172.16.30.248:555/Streaming/channels/1/")
and this
using API Hikvision
"""
cam = Client('http://192.168.1.10', 'admin', 'password', timeout=30)
cam.count_events = 2
response = cam.Streaming.channels[101].picture(method='get', type='opaque_data')
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
img = cv2.imread('screen.jpg')
cv2.imshow("show", img)
cv2.waitKey(1)
In the first case, I have a delay between realtime and cap.read() about 9-20 seconds.
I solved it with such a "hack", but no results.
"""
class CameraBufferCleanerThread(threading.Thread):
def __init__(self, camera, name='camera-buffer-cleaner-thread'):
self.camera = camera
self.last_frame = None
super(CameraBufferCleanerThread, self).__init__(name=name)
self.start()
def run(self):
while True:
ret, self.last_frame = self.camera.read()
"""
The second case shows frames with a delay of 1-2 seconds, which is acceptable, but fps = 1, which is not very good.
Are there any options that can help you get a stream with low latency and normal fps?
In nathancys's post I finded working solutuion. it is done.
I used simple modification his code for my case for getting frame in main function.
from threading import Thread
import cv2, time
from threading import Thread
import cv2, time
class ThreadedCamera(object):
def __init__(self, src=0):
self.capture = cv2.VideoCapture(src)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 1)
self.FPS = 1/100
self.FPS_MS = int(self.FPS * 1000)
# First initialisation self.status and self.frame
(self.status, self.frame) = self.capture.read()
# Start frame retrieval thread
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
time.sleep(self.FPS)
if __name__ == '__main__':
src = 'rtsp://admin:password#192.168.7.100:554/ISAPI/Streaming/Channels/101'
threaded_camera = ThreadedCamera(src)
while True:
try:
cv2.imshow('frame', threaded_camera.frame)
cv2.waitKey(threaded_camera.FPS_MS)
except AttributeError:
pass

Kivy Video Player delay/lag

I'm trying to display an rtsp stream via kivy video player, this runs fine but in my video I get a 2 or 3 second delay in the stream which I would ideally like to eliminate to 0.5 to 1 seconds.
Here's what I have:
from kivy.app import App
from kivy.uix.video import Video
class TestApp(App):
def build(self):
video = Video(source='rtsp://my-stream-address', state='play')
video.size = (720, 320)
video.opacity = 0
video.state = 'play'
video.bind(texture=self._play_started)
return video
def _play_started(self, instance, value):
instance.opacity = 1
if __name__ == '__main__':
TestApp().run()
EDIT
I have a working solution to the video streaming BUT I don't know how to get this into my kivy gui.
Here's my streaming solution:
from threading import Thread
import cv2, time
class ThreadedCamera(object):
def __init__(self, src=0):
self.capture = cv2.VideoCapture(src)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)
self.FPS = 1/30
self.FPS_MS = int(self.FPS * 1000)
# Start frame retrieval thread
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
time.sleep(self.FPS)
def show_frame(self):
cv2.imshow('frame', self.frame)
cv2.waitKey(self.FPS_MS)
if __name__ == '__main__':
src = 'rtsp://my-stream-address'
threaded_camera = ThreadedCamera(src)
while True:
try:
threaded_camera.show_frame()
except AttributeError:
pass
EDIT 2
I have also found this implementation of a kivy video widget not using the built in Video widget. I'm still unsure how to combine my working solution with a Kivy widget but perhaps this can help someone help me:
class KivyCamera(Image):
source = ObjectProperty()
fps = NumericProperty(30)
def __init__(self, **kwargs):
super(KivyCamera, self).__init__(**kwargs)
self._capture = None
if self.source is not None:
self._capture = cv2.VideoCapture(self.source)
Clock.schedule_interval(self.update, 1.0 / self.fps)
def on_source(self, *args):
if self._capture is not None:
self._capture.release()
self._capture = cv2.VideoCapture(self.source)
#property
def capture(self):
return self._capture
def update(self, dt):
ret, frame = self.capture.read()
if ret:
buf1 = cv2.flip(frame, 0)
buf = buf1.tostring()
image_texture = Texture.create(
size=(frame.shape[1], frame.shape[0]), colorfmt="bgr"
)
image_texture.blit_buffer(buf, colorfmt="bgr", bufferfmt="ubyte")
self.texture = image_texture
My initial question was for Kivy Video player widget. But now the solution I am finding is using threading with OpenCV, so I have changed the tags on this question and will accept any Kivy implementation of this solution.

How to fix problem with save a video frame by frame

The problem is that I try to save a video file I get a one frame each time from another function (I check this is not the same frame...), the video created but only with one frame. I run with a loop outside the class Video_utility and send frame to the function save_and_display_video.
import cv2
class Video_utility:
def __init__(self, name_video, format_video, display, fps, size):
self.name_video = name_video
self.format_video = format_video
self.display = display
self.fps = fps
self.size = size
self.stream_frame = None
self.flag_update = True
self.display = True
self.fourcc = cv2.VideoWriter_fourcc(*'XVID')
self.out = cv2.VideoWriter(name_video, self.fourcc, fps, self.size)
self.i = 0
def save_and_display_video(self, frame):
frame = cv2.resize(frame,(self.size))
self.out.write(frame)
self.out.release()
cv2.destroyAllWindows()
Don't close the file after every frame
You have self.out.release() at your save_and_display_video() function.
You'd need to do that only after you've received the whole video.

webcam recording with python tkinter

I want to record video (not audio) from webcam.
I have put two buttons for start recording & stop recording.
As program starts it pick image from camera & shows on screen.works perfect.
My problem is when i click start recording & after some times stop recording,
only avi file created ,with 0K or 6K size found. No further recording found.
import tkinter
import cv2
import PIL.Image, PIL.ImageTk
stopb = None
class App():
def __init__(self, window, window_title):
self.window = window
self.window.title = window_title
self.ok = False
self.video = cv2.VideoCapture(0)
self.width = self.video.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.video.get(cv2.CAP_PROP_FRAME_HEIGHT)
#create videowriter
self.fourcc = cv2.VideoWriter_fourcc(*'XVID')
self.out = cv2.VideoWriter('output.avi',self.fourcc,10,(640,480))
# Create a canvas that can fit the above video source size
self.canvas = tkinter.Canvas(window, width=self.width, height=self.height)
self.canvas.pack()
self.opencamera = tkinter.Button(window, text="open camera", command=self.open_camera)
self.opencamera.pack()
self.closecamera = tkinter.Button(window, text="close camera", command=self.close_camera)
self.closecamera.pack()
self.delay = 10
self.update()
# After it is called once, the update method will be automatically called every delay milliseconds
self.window.mainloop()
def update(self):
ret, frame = self.video.read()
if self.ok == 'T':
self.out.write(frame)
if ret:
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
self.window.after(self.delay, self.update)
def open_camera(self):
self.ok = True
print("camera opened")
print(self.ok)
def close_camera(self):
print("camera closed")
self.ok = False
self.video.release()
self.out.release()
def __del__(self):
if self.video.isOpened():
self.video.release()
self.out.release()
App(tkinter.Tk(), "mywindow")
Your problem is that you're never writing anything to your output, as if self.ok == 'T' will never evaluate to true. You should change it to just if self.ok, the same thing you did with ret.
def update(self):
ret, frame = self.video.read()
if self.ok:
self.out.write(frame)
if ret:
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
self.window.after(self.delay, self.update)
One more issue I found was- after clicking the close camera button followed by the open camera button, the new recording won't start.
Meaning you will just be able to record the first video captured before clicking the close camera button.
This is because the close camera function releases the video object and output object thus video.read() of the update function won't work.
A quick fix is to create a method of set_camera() which creates the video object. Call this function every time in open camera.
https://replit.com/#AmitYadav4/video-record-in-tkinker-canvas

Categories

Resources