multi-threading, what is wrong with my code - python

I was trying to make faster my frames in opencv, it was so slow using it normal, so I decided to ask it here Make faster videocapture opencv the answer was to use multi threading to make it faster, so I code it like this
# The same genderrecognition.py code but with multi-threading to make it faster and fix the the lag of the other one
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
import numpy as np
import cv2
import os
import cvlib as cv
# open webcam and initiate the cam
webcam = cv2.VideoCapture(0, cv2.CAP_DSHOW)
# opencv class
class VideoStream:
def __init__(self):
# read frame from webcam
self.status, self.frame = webcam.read()
webcam.set(cv2.CAP_PROP_FPS, 1000)
self.frame = cv2.flip(self.frame, 1)
print("videostream working")
# face detection class
class face_detection:
def __init__(self):
# use VideoStream Class variables
self.videostream = VideoStream()
self.frame = self.videostream.frame
# apply face detection
self.face, self.confidence = cv.detect_face(self.frame)
# loop through detected faces
for self.idx, self.f in enumerate(self.face):
# get the corner point of the rectangle
self.startX, self.startY = self.f[0], self.f[1]
self.endX, self.endY = self.f[2], self.f[3]
cv2.rectangle(self.frame, (self.startX, self.startY), (self.endX, self.endY), (0,255,0), 2)
self.face_crop = np.copy(self.frame[self.startY:self.endY, self.startX:self.endX])
if self.face_crop.shape[0] < 10 or self.face_crop.shape[1] < 10:
continue
# preprocessing for gender detection model
self.face_crop = cv2.resize(self.face_crop, (96,96))
self.face_crop = self.face_crop.astype("float") / 255.0
self.face_crop = img_to_array(self.face_crop)
self.face_crop = np.expand_dims(self.face_crop, axis=0)
GFR()
print("face_detection working")
# gender recognition class
class GFR:
def __init__(self):
self.model = load_model("C:/Users/berna/Desktop/Programming/AI_ML_DL/Projects/FaceGenderRecognition/gender_detection.model")
self.facedetection = face_detection()
self.face_crop = self.facedetection.face_crop
self.classes = ['hombre', 'mujer']
self.startX, self.startY = self.facedetection.startX, self.facedetection.startY
self.endX, self.endY = self.facedetection.endX, self.facedetection.endY
self.frame = self.facedetection.frame
# apply the gender detection face with the model
self.conf = model.predict(self.face_crop)[0]
# get label with max acc
self.idx = np.argmax(self.conf)
self.label = self.classes[self.idx]
self.label = "{}: {:.2f}".format(self.label, self.conf[self.idx] * 100)
self.Y = self.startY - 10 if self.startY - 10 > 10 else self.startY + 10
# write label and confidence above the face rectangle
cv2.putText(self.frame, self.label, (self.startX, self.Y), cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0,255,0), 2)
print("gender recognition working!")
# classes and webcam while loop
gender_detection = GFR()
# loop through frames
while webcam.isOpened():
VideoStream()
face_detection()
# display output
cv2.imshow("Gender Detection", gender_detection.frame)
# press "Q" to stop
if cv2.waitKey(1) & 0xFF == ord('q'):
break
webcam.release()
cv2.destroyAllWindows()
it give me no errors, but compared to my other code that is on the other question, the webcam open and on this one no, any idea?

Your VideoStream class's init looks ok, but I think you might have better luck creating a cv2 VideoCapture object in the init as well:
self.stream = cv2.VideoCapture(0)
I'm not really as familiar with webcam.set() but if you want to incorporate that, I'm sure you can.
Here you have grabbed the initial frames:
self.status, self.frame = webcam.read()
(Or using the new self.stream variable):
self.status, self.frame = self.stream.read()
Yet this will only grab a frame when it's initialized, not in a loop. To achieve a loop, you have to make a few more class methods. One will be for continuously getting frames (I added a self.stopped attribute, although it's not in your code. It might be a good idea to have a True/False stop flag):
def read_stream(self):
while not self.stopped:
(self.grabbed, self.frame) = self.stream.read()
Then if you want to use multithreading, you can make a thread pointing to the read_stream method:
def start(self):
Thread(target=self.read_stream, args=()).start()
return self
You will have to call the start() method on the VideoStream before you start your CV2 imshow() loop.
video_stream = VideoStream().start(). #<------Here--------
while webcam.isOpened():
face_detection()
# display output
cv2.imshow("Gender Detection", gender_detection.frame)
# press "Q" to stop
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Hopefully this helps getting the CV2 display to show. Whether your GFR class or face detection is grabbing the right frames from the VideoStream class... that's something else, and I can't debug all that code.

Related

Strange behavior with cv2 VideoWriter with a thread. Can anyone explain it to Me?

For Context: I'm trying to write a program that is able to record video from a varying amount of webcams.
I got everything working except that I am getting a strange behavior in the video file itself.
After reading about other questions, fixing all my codecs and so forth, I figured it's an issue relating to threading. The strange part is when I tried it with a code I found around here, I get the opposite problem !
The Issue: My own code creates super-speedy videos (5 recorded seconds plays in 1 second). While the other code creates super-slow videos (5 recorded seconds plays in 25 seconds).
The Question: Can anyone explain this behavior to Me?
My simplified code:
import cv2
import threading
import os
import time
class Cameras():
def __init__(self, parent=None):
super().__init__()
self.cam1 = cv2.VideoCapture(0)
self.cam1check = self.cam1.isOpened()
newfolder = "./Unnamed"
os.makedirs(newfolder, exist_ok=True)
full_path = os.path.abspath(newfolder)
self.path = os.path.join(full_path,"Speedy")
self.namecam = self.path + " Example 0.avi"
self.Format = cv2.VideoWriter_fourcc(*'H264')
self.FPS = 60
self.cam1.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.cam1.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
self.cam1.set(cv2.CAP_PROP_FPS, self.FPS)
self.width1 = int(self.cam1.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height1 = int(self.cam1.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.size1 = (self.width1,self.height1)
def capturecam1(self):
while self.cam1check is True:
rect1, frame1 = self.cam1.read(cv2.CAP_FFMPEG)
if rect1:
self.video_writer_cam1.write(frame1)
def start_rec(self):
self.video_writer_cam1 = cv2.VideoWriter(self.namecam, self.Format, self.FPS, self.size1, cv2.CAP_FFMPEG)
if self.cam1check is True:
thread = threading.Thread(target=self.capturecam1, daemon=True)
thread.start()
if __name__ == '__main__':
Launch = Cameras()
Launch.start_rec()
while True:
time.sleep(1)
The other code:
from threading import Thread
import cv2
import time
import os
class VideoWriterWidget(object):
def __init__(self, video_file_name, src=0):
# Create a VideoCapture object
print(src)
self.frame_name = src # if using webcams, else just use src as it is.
self.video_file = video_file_name
# self.video_file_name = video_file_name + '.avi'
# self.capture = cv2.VideoCapture(src)
newfolder = "./Unnamed"
os.makedirs(newfolder, exist_ok=True)
full_path = os.path.abspath(newfolder)
self.path = os.path.join(full_path,"Slowmo")
self.video_file_path = self.path + " Example {}.avi".format(self.video_file)
# Default resolutions of the frame are obtained (system dependent)
self.cam1 = cv2.VideoCapture(0)
self.cam1check = self.cam1.isOpened()
print(self.cam1check)
self.cam1.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.cam1.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
self.cam1.set(cv2.CAP_PROP_FPS, 60)
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc(*'H264')
self.output_video = cv2.VideoWriter(self.video_file_path, self.codec, 60, (1280, 720))
# Start the thread to read frames from the video stream
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
# Start another thread to show/save frames
self.start_recording()
print('initialized {}'.format(self.video_file))
def update(self):
# Read the next frame from the stream in a different thread
while True:
if self.cam1check is True:
(self.status, self.frame) = self.cam1.read()
def show_frame(self):
# Display frames in main program
print(self.status)
if self.status:
print("Showing!")
cv2.imshow(self.frame_name, self.frame)
# Press Q on keyboard to stop recording
key = cv2.waitKey(1)
if key == ord('q'):
self.cam1.release()
self.output_video.release()
cv2.destroyAllWindows()
exit(1)
def save_frame(self):
# Save obtained frame into video output file
self.output_video.write(self.frame)
def start_recording(self):
# Create another thread to show/save frames
def start_recording_thread():
while True:
try:
# self.show_frame()
self.save_frame()
except AttributeError:
pass
self.recording_thread = Thread(target=start_recording_thread, args=())
self.recording_thread.daemon = True
self.recording_thread.start()
if __name__ == '__main__':
src1 = '0'
video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
# src2 = '1'
# video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
# src3 = '2'
# video_writer_widget3 = VideoWriterWidget('Camera 3', src3)
# Since each video player is in its own thread, we need to keep the main thread alive.
# Keep spinning using time.sleep() so the background threads keep running
# Threads are set to daemon=True so they will automatically die
# when the main thread dies
while True:
time.sleep(1)
Credit for the other code here: https://stackoverflow.com/a/71624807/9599824

Live Feed from raspicam on canvas covering everything even cursor

I am working on an app that basically turns the raspberry pi 4 into a camera, since I've been learning opencv i thought it would be a cool way to show off everything that I've done so far,
I do manage to retrieve the feed of the raspicam, but it's on the top layer so it covers up everything, including my cursor and i can't even stop the app.
I retrieve the image with this code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# camera_pi.py
#
#
#
import time
import io
import threading
import picamera
class Camera(object):
thread = None # background thread that reads frames from camera
frame = None # current frame is stored here by background thread
last_access = 0 # time of last client access to the camera
def initialize(self):
if Camera.thread is None:
# start background frame thread
Camera.thread = threading.Thread(target=self._thread)
Camera.thread.start()
# wait until frames start to be available
while self.frame is None:
time.sleep(0)
def get_frame(self):
Camera.last_access = time.time()
self.initialize()
return self.frame
#classmethod
def _thread(cls):
with picamera.PiCamera() as camera:
# camera setup
camera.resolution = (1920, 1080)
camera.hflip = True
camera.vflip = True
#camera.zoom = (0.22,0,0.7,0.7) # (x, y, w, h)
# let camera warm up
camera.start_preview()
time.sleep(2)
stream = io.BytesIO()
for foo in camera.capture_continuous(stream, 'jpeg',
use_video_port=True):
# store frame
stream.seek(0)
cls.frame = stream.read()
# reset stream for next frame
stream.seek(0)
stream.truncate()
# if there hasn't been any clients asking for frames in
# the last 10 seconds stop the thread
#if time.time() - cls.last_access > 10:
#break
cls.thread = None
And then make it into a full screen Tkinter widget with this code:
import cv2
from camera_pi import *
import sys, os
if sys.version_info[0] == 2: # the tkinter library changed it's name from Python 2 to 3.
import Tkinter
tkinter = Tkinter #I decided to use a library reference to avoid potential naming conflicts with people's programs.
else:
import tkinter
from PIL import Image, ImageTk
import time
camera = Camera()
feed = camera.get_frame()
frame = cv2.imread(feed)
#cv2.imshow(frame)
#cv2.waitKey(0)
#cv2.destroyAllWindows()
root = tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
canvas = tkinter.Canvas(root,width=w,height=h)
canvas.pack()
canvas.configure(background='black')
SetButton = Button(root,text="Settings", command=root.destroy)
SetButton.place(x=0,y=0)
def showPIL(pilImage):
imgWidth, imgHeight = pilImage.size
# resize photo to full screen
ratio = min(w/imgWidth, h/imgHeight)
imgWidth = int(imgWidth*ratio)
imgHeight = int(imgHeight*ratio)
pilImage = pilImage.resize((imgWidth,imgHeight), Image.ANTIALIAS)
image = ImageTk.PhotoImage(pilImage)
imagesprite = canvas.create_image(w/2,h/2,image=image)
imagesprite.lower()
root.update_idletasks()
root.update()
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
try:
showPIL(frame)
except KeyboardInterrupt:
root.destroy
I'd like to add a button on the corner to open up a settings window where i could modify the camera parameters, or start one of the opencv modes ive been working on but being that i can't see the cursor that doesn't work, i also tried to use the canvas function to lower, and tried moving it around in the code but i think its because the image keeps refreshing, or i simply did it wrong.

Tkinter program works fine in IDE (Visual Studio) but when using pyinstaller to compile to .exe threading does not work same like it does in IDE

When I run my python project in my IDE the GUI and everything is responsive and works great. But when I run as .exe my threading components don't work like they do in IDE. The program's goal is to grab a live feed via RTSP and using opencv to display the images. This is done in its own thread here.
import time
import threading
import cv2
import PIL.Image
"""TODO: add docstring"""
class VideoCapture:
def __init__(self, xmlDict=None, width=None, height=None, fps=None):
"""TODO: add docstring"""
self.xmlDict = xmlDict
self.width = width
self.height = height
self.fps = int(self.xmlDict['FPS'])
self.running = False
# Open the video source
self.vid = cv2.VideoCapture(self.xmlDict['IpAddress'])
if not self.vid.isOpened():
raise ValueError("[MyVideoCapture] Unable to open video source", xmlDict['IpAddress'])
# Get video source width and height
if not self.width:
self.width = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # convert float to int
if not self.height:
self.height = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # convert float to int
if not self.fps:
self.fps = int(self.vid.get(cv2.CAP_PROP_FPS)) # convert float to int
# default value at start
self.ret = False
self.frame = None
self.convert_color = cv2.COLOR_BGR2RGB
#self.convert_color = cv2.COLOR_BGR2GRAY
self.convert_pillow = True
# start thread
self.running = True
self.thread = threading.Thread(target=self.process)
self.thread.start()
def process(self):
"""TODO: add docstring"""
while self.running:
ret, frame = self.vid.read()
if ret:
# process image
frame = cv2.resize(frame, (self.width, self.height))
# it has to record before converting colors
if self.convert_pillow:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = PIL.Image.fromarray(frame)
else:
print('[MyVideoCapture] stream end:', self.video_source)
# TODO: reopen stream
self.running = False
if self.recording:
self.stop_recording()
break
# assign new frame
self.ret = ret
self.frame = frame
# sleep for next frame
#if self.fps != "FULL":
# time.sleep(1/int(self.fps))
I have a button setup called start that infers an image every 2 seconds and prints out the label and confidence. When I do this in .exe the live feed and GUI freeze while inference is being made but when I use program in IDE it does not freeze. Here is the code that does this.
#Button to start inference
self.btn_snapshot = tk.Button(self.btnFrame,width = 10,height = 2, text="Start", command=lambda:threading.Thread(target = self.snapshot).start())
self.btn_snapshot.grid(row = 1,column = 0)
#snapshot function
def snapshot(self):
self.recording = True
while self.recording:
filename = self.vid.snapshot()
result = self.predictImage(filename)
output = self.calculatePassFail(result)
if self.manager:
self.manager.onClick(output)
else:
print('something')
time.sleep(2)
The other two methods that the snapshot function calls are predictImage and calculatePassFail.
def predictImage(self,imageName):
onnxModel = ImageModel.load(self.xmlDict['ModelPath'])
result = onnxModel.predict_from_file(imageName)
return result
def calculatePassFail(self,result):
calcResult = result.labels[0]
self.labelName = calcResult[0]
self.imgScore = calcResult[1]*100
return f"{self.labelName} with score{self.imgScore}"
So I found a fix around this, not sure if its a proper fix but it works. So for some reason when I use pyinstaller to create .exe and there is a console window I have the issue but when I use the flag --noconsole when using pyinstaller to create w/out console the issue goes away and my inference on images acts in its own thread like it does in my IDE. Not sure why it does but it works I guess.

How can I preview streaming images in tkinter with Allied Vision camera that uses Vimba SDK?

I want to display images from an Allied Vision camera inside a tkinter frame using OpenCV and the SDK for the camera, VimbaPython.
The only possible way to initialize the camera is with a Python with statement:
with Vimba.get_instance() as vimba:
cams = vimba.get_all_cameras()
with cams[0] as camera:
camera.get_frame()
# Convert frame to opencv image, then use Image.fromarray and ImageTk.PhotoImage to
# display it on the tkinter GUI
Everything works fine so far. But I don't only need a single frame. Instead, I need to continuously get frames and display them on the screen so that it is streaming.
I found that one way to do it is to call the .after(delay, function) method from a tkinter Label widget.
So, after obtaining one frame, I want to call the same function to get a new frame and display it again. The code would look like that:
with Vimba.get_instance() as vimba:
cams = vimba.get_all_cameras()
with cams[0] as camera:
def show_frame():
frame = camera.get_frame()
frame = frame.as_opencv_image()
im = Image.fromarray(frame)
img = Image.PhotoImage(im)
lblVideo.configure(image=img) # this is the Tkinter Label Widget
lblVideo.image = img
show_frame()
lblVideo.after(20, show_frame)
Then this shows the first frame and stops, throwing an error saying that Vimba needs to be initialized with a with statement. I don't know much about Python, but it looks like when I call the function with the .after() method it ends the with statement.
I would like to know if it is possible to execute this show_frame() function without ending the with. Also, I can't initialize the camera every time because the program goes really slow.
Thank you
I know this question is pretty old, but I ran into a similar problem with the Allied Vision cameras and found the solution to be relatively robust. So I hope this helps someone, even if not the OP.
An alternative to using with statements is using __enter__ and __exit__ (see sample here). With this, I created a class for the Vimba camera and during the __init__ I used these functions twice: once to initialize the Vimba instance, and once to open the camera itself. An example as follows...
vimba_handle = Vimba.get_instance().__enter__()
camera = vimba_handle.get_all_cameras()[0].__enter__()
I'll include a longer snippet as code as well, but please note my purpose was slightly different the OP's intent. Hopefully, it is still useful.
class VimbaCam:
def __init__(self, device_id=0):
# Variables
self.current_frame = np.array([])
self.device = None
self.device_id = device_id
self.vimba_handle = Vimba.get_instance().__enter__()
self.is_streaming = False
self.scale_window = 4
self.stream_thread = threading.Thread(target=self.thread_stream, daemon=True)
# Default settings
self.auto_exposure = "Off"
self.auto_gain = "Off"
self.acquisition = "Continuous"
self.exposure_us = 200000
self.fps = 6.763
self.gain = 0
self.gamma = 1
self.open()
def close(self):
if self.device is not None:
if self.is_streaming:
self.stop_stream()
time.sleep(1)
self.device.__exit__(None, None, None)
self.vimba_handle.__exit__(None, None, None)
def open(self):
cams = self.vimba_handle.get_all_cameras()
if not cams:
error_check(151, currentframe())
else:
self.device = cams[self.device_id].__enter__()
self.set_defaults()
self.start_stream()
def start_stream(self):
if self.device is not None:
self.is_streaming = True
self.stream_thread.start()
time.sleep(1)
def thread_stream(self):
while self.is_streaming:
current_frame = self.device.get_frame().as_opencv_image()
h, w, _ = current_frame.shape
self.current_frame = current_frame.reshape((h, w))
self.stream_thread = threading.Thread(target=self.thread_stream, daemon=True)
def stop_stream(self):
if self.device is not None:
self.is_streaming = False
def live_video(self):
if self.device is not None:
window_name = "Allied Vision"
h, w = self.current_frame.shape
w = int(w / self.scale_window)
h = int(h / self.scale_window)
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(window_name, w, h)
while 1:
cv2.imshow(window_name, self.current_frame)
cv2.waitKey(1)
if cv2.getWindowProperty(window_name, cv2.WND_PROP_VISIBLE) < 1:
break
cv2.destroyAllWindows()
def set_defaults(self):
if self.device is not None:
# Exposure time settings
self.device.ExposureAuto.set(self.auto_exposure)
self.device.ExposureTimeAbs.set(self.exposure_us)
# Gain settings
self.device.GainAuto.set(self.auto_gain)
self.device.Gain.set(self.gain)
# Gamma settings
self.device.Gamma.set(self.gamma)
self.device.AcquisitionMode.set(self.acquisition)
self.device.AcquisitionFrameRateAbs.set(self.fps)
# Try to adjust GeV packet size (available for GigE only)
try:
self.device.GVSPAdjustPacketSize.run()
while not self.device.GVSPAdjustPacketSize.is_done():
pass
except (AttributeError, VimbaFeatureError):
pass
# Color formatting (tries mono first, then color)
cv_formats = intersect_pixel_formats(self.device.get_pixel_formats(), OPENCV_PIXEL_FORMATS)
mono_formats = intersect_pixel_formats(cv_formats, MONO_PIXEL_FORMATS)
color_formats = intersect_pixel_formats(cv_formats, COLOR_PIXEL_FORMATS)
if mono_formats:
self.device.set_pixel_format(mono_formats[0])
elif color_formats:
self.device.set_pixel_format(color_formats[0])
if __name__ == "__main__":
dev = VimbaCam()
dev.live_video()
dev.close()
You need to use thread to run the capture code and pass the frames read via queue. Then the main tkinter application reads the queue and show the frames periodically using .after().
Below is an example based on your posted code:
import threading
from queue import SimpleQueue
import tkinter as tk
from PIL import Image, ImageTk
from vimba import Vimba
def camera_streaming(queue):
global is_streaming
is_streaming = True
print("streaming started")
with Vimba.get_instance() as vimba:
with vimba.get_all_cameras()[0] as camera:
while is_streaming:
frame = camera.get_frame()
frame = frame.as_opencv_image()
im = Image.fromarray(frame)
img = ImageTk.PhotoImage(im)
queue.put(img) # put the capture image into queue
print("streaming stopped")
def start_streaming():
start_btn["state"] = "disabled" # disable start button to avoid running the threaded task more than once
stop_btn["state"] = "normal" # enable stop button to allow user to stop the threaded task
show_streaming()
threading.Thread(target=camera_streaming, args=(queue,), daemon=True).start()
def stop_streaming():
global is_streaming, after_id
is_streaming = False # terminate the streaming thread
if after_id:
lblVideo.after_cancel(after_id) # cancel the showing task
after_id = None
stop_btn["state"] = "disabled" # disable stop button
start_btn["state"] = "normal" # enable start button
# periodical task to show frames in queue
def show_streaming():
global after_id
if not queue.empty():
image = queue.get()
lblVideo.config(image=image)
lblVideo.image = image
after_id = lblVideo.after(20, show_streaming)
queue = SimpleQueue() # queue for video frames
after_id = None
root = tk.Tk()
lblVideo = tk.Label(root, image=tk.PhotoImage(), width=640, height=480)
lblVideo.grid(row=0, column=0, columnspan=2)
start_btn = tk.Button(root, text="Start", width=10, command=start_streaming)
start_btn.grid(row=1, column=0)
stop_btn = tk.Button(root, text="Stop", width=10, command=stop_streaming, state="disabled")
stop_btn.grid(row=1, column=1)
root.mainloop()
Note that I don't have the camera and the SDK installed, the above code may not work for you. I just demonstrate how to use thread, queue and .after().
Below is a testing vimba module (saved as vimba.py) I use to simulate VimbaPython module using OpenCV and a webcam:
import cv2
class Frame:
def __init__(self, frame):
self.frame = frame
def as_opencv_image(self):
return self.frame
class Camera:
def __init__(self, cam_id=0):
self.cap = cv2.VideoCapture(cam_id, cv2.CAP_DSHOW)
def __enter__(self):
return self
def __exit__(self, *args):
self.cap.release()
return self
def get_frame(self):
ret, frame = self.cap.read()
if ret:
return Frame(frame)
class Vimba:
_instance = None
#classmethod
def get_instance(self):
if self._instance is None:
self._instance = Vimba()
return self._instance
def __enter__(self):
return self
def __exit__(self, *args):
return self
def get_all_cameras(self):
return (Camera(),)
I tried to read the frames in openCV and display them in tkinter label. I was able to do so using the below code:
import tkinter as tk
import cv2
from PIL import ImageTk, Image
video_path = "SAMPLE/STORED_VIDEO/PATH"
root = tk.Tk()
base_img = Image.open("PATH/TO/DEFAULT/LABLE/IMAGE")
img_obj = ImageTk.PhotoImage(base_img)
lblVideo = tk.Label(root, image=img_obj)
lblVideo.pack()
cap = cv2.VideoCapture(video_path)
if cap.isOpened():
def show_frame():
_, frame = cap.read()
im = Image.fromarray(frame)
img = ImageTk.PhotoImage(im)
lblVideo.configure(image=img)
lblVideo.image = img
lblVideo.after(1, show_frame) # Need to create callback here
show_frame()
root.mainloop()
Although this doesnot contain the with statement, you can try replacing the after() callback inside the show_frame function itself.

I try to get 2 frames from picamera. Why it works very slowly?

From picamera.PiCamera() Thread I try to get cls.frameCV2 = snap.array for handling in opencv, and another cls.frame = stream.read() for web streaming. Both work, but very slowly.
What is the problem? If Use only frameCV2 it works quite fast, or if I only Use stream.read, it also ok.
Here is my code snippet:
import time
import io
import threading
import cv2
import picamera
from picamera.array import PiRGBArray
class Camera(object):
thread = None # background thread that reads frames from camera
frame = None # current frame is stored here by background thread
frameCV2 = None # same, but as a numpy array
last_access = 0 # time of last client access to the camera
def initialize(self):
if Camera.thread is None:
# start background frame thread
Camera.thread = threading.Thread(target=self._thread)
Camera.thread.start()
# wait until frames start to be available
while self.frame is None:
time.sleep(0)
def get_frame(self):
Camera.last_access = time.time()
self.initialize()
return self.frame
def get_frame_for_internal_proc(self): # store frame for cv2 я добавил
Camera.last_access = time.time()
self.initialize()
return self.frameCV2
#classmethod
def _thread(cls):
with picamera.PiCamera() as camera:
# camera setup
camera.resolution = (800, 600)
camera.hflip = True
camera.vflip = True
# let camera warm up
camera.start_preview()
time.sleep(2)
rawCapture = PiRGBArray(camera) # я добавил
stream = io.BytesIO()
for snap in camera.capture_continuous(rawCapture, 'bgr',
use_video_port=True):
cls.frameCV2 = snap.array # frame for cv2
# store frame for web
camera.capture(stream, format='jpeg')
stream.seek(0)
cls.frame = stream.read()
# reset stream for next frame
stream.seek(0)
stream.truncate()
rawCapture.truncate(0)
# if there hasn't been any clients asking for frames in
# the last 10 seconds stop the thread
if time.time() - cls.last_access > 10:
break
cls.thread = None
So, I didn't find the way to make it working faster. The main reason of low speed is doubled capturing inside of camera.capture_continuous circle.
Anyhow I solved the problem using 1 thread for both generation frame and frameCV2, just converting second one to the first. I just put self.frame = cv2.imencode('.jpg', self.frameCV2)[1].tobytes() in get_frame() method which converts numpy matrix to format, fit for web streaming.
Now it works well.

Categories

Resources