I am a newbie in Image processing area and in fact, I got into trouble with a small code related to Classification. During streaming, I just want to get a specific frame in every 3 seconds and then, I use that frame with a classification algorithm. Here is my current coding with a thread: (it got total frames and I do not want to take them all)
class ThreadCapture():
def __init__(self, knn):
self.frame = []
self.status = False
self.isStop = False
self.knn = knn
self.cap = cv2.VideoCapture(0)
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 480)
def start(self):
threading.Thread(target=self.current_frame, daemon=True, args=()).start()
def stop(self):
self.isStop = True
def get_frame(self):
return self.status, self.frame
def current_frame(self):
while(not self.isStop):
self.status, self.frame = self.cap.read()
self.crop_frame()
self.cap.release()
def run_knn(self):
img_resize = cv2.resize(self.frame, (224, 224))
img = cv2.cvtColor(img_resize, cv2.COLOR_BGR2RGB)
img_pil = Image.fromarray(img)
return self.knn.classify(img_pil)
def main(arg):
.....
stream = ThreadCapture(EagleEyes)
stream.start()
time.sleep(1)
while(True):
status, frame = stream.get_frame()
if status:
info, res = stream.run_knn()
print(info)
cv2.putText(frame, info, (10,40), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 1, cv2.LINE_AA)
cv2.imshow('abc', frame)
if cv2.waitKey(1)==ord('q'):
break
stream.stop()
cv2.destroyAllWindows()
I am not good at coding so I would like to know if anyone could support me to improve above codings.
Thank you all.
The simplest way may be to continue along the lines you have above but only take any action on frames at an interval.
For example, if you want to do something with a frame every 10 seconds and your video frame rate is 25fps, then you can count until you reach 250 frames and then do your work on that frame.
.....
stream = ThreadCapture(EagleEyes)
stream.start()
time.sleep(1)
frameCounter = 0
while(True):
status, frame = stream.get_frame()
//Increment the frame counter
frameCounter += 1
if frameCounter == 250
//Do whatever work you want on the frame here
//Reset the counter
frameCounter = 0
// Add some condition to break out of the loop,
// e.g. end of file or status check.
cap.release()
cv2.destroyAllWindows()
Related
I want to analyse image from IP cameras (from 2 to 6 cameras) with a Raspberry 3b+.
I'm using python opencv but there is a increasing delay (as the video was in x0.5 so the delay accumulates)
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)
# FPS = 1/X
# X = desired FPS
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://user:psword#192.168.8.108:554/Streaming/Channels/1401'
threaded_camera = ThreadedCamera(src)
while True:
try:
threaded_camera.show_frame()
except AttributeError:
pass
I try without the FPS logic and the result is the same (I try to reduce FPS and it doesnt work). I don't need 30 FPS but I want minimum 3 FPS.
What can I do ? Is there an good alternative to opencv ? Do I have to use another language
You have to remove the time.sleep after cv2.capture.
I don't know why but it works for me.
I need some help in speeding up the merging process from two camera sources. The merge_fn is like kind of a panorama generation function and it takes a very minimal time. The frame rate is reduced because I am reading the camera sources one by one and I need to speed up that process (like separate threads?).
how to parallelize that?
my code:
import numpy as np
import cv2
leftStream = cv2.VideoCapture(0)
rightStream = cv2.VideoCapture(1)
def merge_fn(left, right):
pass
while True:
_, left = leftStream.read()
_, right = rightStream.read()
merge = merge_fn(left, right)
cv2.imshow("Merge", merge)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Release the windows and the cameras
leftStream.release()
rightStream.release()
cv2.destroyAllWindows()
Using multiprocessing
from multiprocessing import Process, Pipe
import cv2
import numpy as np
def start_camera_1(chi_c, camera):
cam = cv2.VideoCapture(camera)
while True:
ret, frame = cam.read()
if ret:
chi_c.send(frame)
def start_camera_2(chi_c, camera):
cam = cv2.VideoCapture(camera)
while True:
ret, frame = cam.read()
if ret:
chi_c.send(frame)
def start_stream():
par_c_1, chi_c_1 = Pipe()
par_c_2, chi_c_2 = Pipe()
process = Process(target=start_camera_1,
args=(chi_c_1, 0))
process.start()
process = Process(target=start_camera_2,
args=(chi_c_2, 1))
process.start()
while True:
result_1 = par_c_1.recv()
result_2 = par_c_2.recv()
frame = np.hstack((result_1, result_2))
frame = cv2.resize(frame, (1600, 900)) # resize according to your need
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
if __name__ == '__main__':
start_stream()
Here's a threaded version that acquires from two separate cameras in two separate threads and then stacks the images side-by-side:
#!/usr/bin/env python3
import cv2
import time
import threading
import numpy as np
class VideoStream():
"""
Class that acquires video frames continuously, as fast as possible on a
separate thread and makes them available to caller, via 'latestFrame()'
method.
"""
def __init__(self, width=1920, height=1080, stream=0):
self.width = width
self.height = height
self.stream = stream
self.frame = None
self.thread = threading.Thread(target=self.acquireFrames, args=())
self.thread.daemon = True
self.thread.start()
def acquireFrames(self):
cap = cv2.VideoCapture(self.stream)
if cap.isOpened():
cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
while True:
ret, self.frame = cap.read()
if not ret:
print(f'ERROR: Stream: {stream}, error reading frame')
def latestFrame(self):
if not self.frame is None:
return self.frame
# Return blank frame if none available
return np.zeros((self.height, self.width, 3), dtype=np.uint8)
def main():
# Start streaming from two cameras
Stream0 = VideoStream(stream=0)
Stream1 = VideoStream(stream=1)
frames = 0
t0 = time.time()
while True:
# Grab latest frame from each camera
frame0 = Stream0.latestFrame()
frame1 = Stream1.latestFrame()
# Stack frames side-by-side
combined = np.hstack((frame0, frame1))
cv2.imshow('Streams', combined)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Show frame rate in case anyone is interested
frames += 1
if frames % 100 == 0:
elapsed = time.time() - t0
print(f'FPS: {int(frames/elapsed)}')
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
I'm trying to capture a video of a duration of X seconds every Y hours with python3.8 and opencv(4.2.0) in Manjaro XFCE. I'd like to get at least 20-30fps with a resolution of 720p or 1080p. I'm using a Logitech c920, but with a good resolution (eg 1920x1080) I'm stack at 5fps. I can set the fps at 30 but then the video is about 6 times shorter (python takes video at ~5fps but records it as it was 30fps). Thus, to improve the fps I should use multithreading, although I can't get to implement in my code any of the examples I've seen. Any idea how to do it?
Here is my code:
import numpy as np
import cv2
import time
#choose resolution and fps:
x=1920
y=1080
fps=30
# The duration in seconds of the video captured (s)
capture_duration = 5
# Lapse of time between videos (s), from beginning to beginning
vids_lapse=10
# Name of videos
data_string= time.strftime("%m-%d-%H-%M-%S")
for i in range (0,2): #Let's say I only want to do this process twice, to simplify
cap = cv2.VideoCapture(2) #my camera is 2, but could be 0
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
#set resolution and fps
cap.set(3,int(x))
cap.set(4,int(y))
cap.set(cv2.CAP_PROP_FPS, int(fps))
#set name of video
out = cv2.VideoWriter(data_string+"_vid"+str(i).zfill(2)+".avi",fourcc, fps, (x,y))
start_time = time.time()
#start recording for a determined duration
while( int(time.time() - start_time) < capture_duration+1 ):
ret, frame = cap.read()
if ret==True:
#frame = cv2.flip(frame,0)
out.write(frame)
cv2.imshow('frame',frame)
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
#Here I added time.sleep to wait for the next capture
time.sleep(vids_lapse-capture_duration)
With this code above, my video of 30fps is shorter than expected, as it cannot take more than ~5fps.
I've seen I can use multithread, and I'm able to record a video with 30fps (at least that's what ffprobe says, although I'm not sure it is) with the following code:
from threading import Thread
import cv2, time
#again define resolution and fps
x=1920
y=1080
fps=30
class VideoStreamWidget(object):
def __init__(self, src=2):
self.capture = cv2.VideoCapture(src)
self.capture.set(3,int(x))
self.capture.set(4,int(y))
self.capture.set(cv2.CAP_PROP_FPS, int(fps))
# Start the thread to read frames from the video stream
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
# Read the next frame from the stream in a different thread
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
time.sleep(.03)
def show_frame(self):
out.write(self.frame)
# Display frames in main program
cv2.imshow('frame', self.frame)
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.release()
out.release()
cv2.destroyAllWindows()
exit(1)
if __name__ == '__main__':
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
out = cv2.VideoWriter("output.avi",fourcc, fps, (x,y))
video_stream_widget = VideoStreamWidget()
while True:
try:
video_stream_widget.show_frame()
except AttributeError:
pass
The problem of this second code is that I don't really know how to introduce my arguments of (recording during 5 seconds, wait 10 seconds, and so on) or how many threads I'm using (I can specify number of buffers from my camera using V4L2 tools, don't know how relevant is it).
So my question is: Any idea how could I do it? Is it possible to merge both codes? Or is there a better way?
Here are some guides I've checked but couldn't get it working with my code:
https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/
https://github.com/gilbertfrancois/video-capture-async
Thanks a lot in advance!
cheers!
EDIT
I finally got something to work. Nevertheless, even if my video is 30fps, it still looks like a 5fps video... Seems like all the pictures captured are almost the same... Any idea how to solve this?
Here the code:
rom threading import Thread, Lock
import cv2, time
#choose resolution
x=1920
y=1080
fps=60
# The duration in seconds of the video captured (s)
capture_duration = 5
# Days
Days=1
# Lapse of time between videos (s), from beginning to beginning
vids_lapse=10
data_string= time.strftime("%m-%d-%H-%M-%S")
class CameraStream(object):
def __init__(self, src=0):
self.stream = cv2.VideoCapture(src)
self.stream.set(3,int(x))
self.stream.set(4,int(y))
self.stream.set(cv2.CAP_PROP_FPS,int(fps))
self.video_file_name = data_string+"_vid"+str(i).zfill(2)+".avi"
(self.grabbed, self.frame) = self.stream.read()
self.started = False
self.read_lock = Lock()
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
self.out = cv2.VideoWriter(self.video_file_name, self.codec, fps, (x, y))
def start(self):
if self.started:
print("already started!!")
return None
self.started = True
self.thread = Thread(target=self.update, args=())
self.thread.start()
return self
def update(self):
while self.started:
(grabbed, frame) = self.stream.read()
self.read_lock.acquire()
self.grabbed, self.frame = grabbed, frame
self.read_lock.release()
# ~ time.sleep(1/fps)
def read(self):
self.read_lock.acquire()
frame = self.frame.copy()
self.read_lock.release()
self.save_frame()
return frame
def save_frame(self):
# Save obtained frame into video output file
self.out.write(self.frame)
def stop(self):
self.started = False
self.thread.join()
self.stream.release()
self.out.release()
def __exit__(self, exc_type, exc_value, traceback):
self.stream.release()
if __name__ == "__main__" :
for i in range (0,2):
start_time = time.time()
cap = CameraStream(0).start()
while (int(time.time() - start_time) < capture_duration):
frame = cap.read()
cv2.imshow('webcam', frame)
# ~ if cv2.waitKey(1) == 27 :
cap.stop()
cv2.destroyAllWindows()
time.sleep(vids_lapse-capture_duration)
The issue is with FOURCC, you should set cv2.CAP_PROP_FOURCC, try this example:
import cv2
HIGH_VALUE = 10000
WIDTH = HIGH_VALUE
HEIGHT = HIGH_VALUE
capture = cv2.VideoCapture(-1)
capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
capture.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = capture.get(cv2.CAP_PROP_FPS)
print(width, height, fps)
while True:
ret, frame = capture.read()
if ret:
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
capture.release()
cv2.destroyAllWindows()
my problem is the camera so slow and not smooth and that makes me a problem to realtime extract text from the label. I want to know how to make a preview of the camera more smooth like a video camera, there is a big lag between live and stream?
here is my code
index = 0
#while test_vid.isOpened():
#make_480p()
while True:
frames += 1
test_vid = cv2.VideoCapture(0)
test_vid.set(cv2.CAP_PROP_FRAME_WIDTH, 720)
test_vid.set(cv2.CAP_PROP_FRAME_HEIGHT, 640)
test_vid.set(cv2.CAP_PROP_FPS,1)
fps = test_vid.get(cv2.CAP_PROP_FPS)
print ("Frames per second using video.get(cv2.cv.CV_CAP_PROP_FPS): {0}".format(fps))
ret,frame = test_vid.read()
start = time.time()
ret, frame = test_vid.read()
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
end = time.time()
print ("time to read a frame : {} seconds".format(end-start))
print(frame)
frame = cv2.flip(frame, 10, 0)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow("LIVE", gray)
key = cv2.waitKey(1)
if key == ord('q'):
break
name = './image_frames/frame' + str(index) + '.png'
print ('Extracting frames...' + name)
cv2.imshow('frame',frame)
cv2.imwrite(name, frame)
test_vid.release()
cv2.destroyAllWindows()
demo = Image.open("./image_frames/frame0.png")
text = pytesseract.image_to_string(demo, lang = 'eng')
print(text)
One issue is that you're recreating the VideoCapture object every frame. Move that setup code outside the loop and you should see a speedup.
# setup the video capture device
test_vid = cv2.VideoCapture(0)
test_vid.set(cv2.CAP_PROP_FRAME_WIDTH, 720)
test_vid.set(cv2.CAP_PROP_FRAME_HEIGHT, 640)
test_vid.set(cv2.CAP_PROP_FPS,1)
fps = test_vid.get(cv2.CAP_PROP_FPS)
print ("Frames per second using video.get(cv2.cv.CV_CAP_PROP_FPS): {0}".format(fps))
while test_vid.isOpened():
ret,frame = test_vid.read()
# do frame processing...
# calculate wait time based on the camera fps
# (as an extension, measure and subtract the image processing time)
key = cv2.waitKey(int(1000/fps))
if key == ord('q'):
break
Recently i working on raspberry pi 3(OS raspbian) with OPENCV + PYTHON to detect face on live camera. And I see detected face on raspberry pi3 use opencv very slow, about 4 - 5 FPS/s. So I wonder can I use MultiThread or MultiProcessing to speed up the FPS and if yes, how can i do that?
Please help me, any idea will appreciate. Thanks
Yes, you can multithread the process.
Use the threading library in python 2, or thread in python 3.
Here is a simple example.
Besides your main thread,
Have one thread only operating the camera and constantly updating the latest frame, which is global in this case.
def camera_thread():
cam = cv2.VideoCapture(1)
_ret, self.image = cam.read()
cv2.imshow('camera', self.image)
A second thread can run the inference from the model using the latest frame.
An optional third could perhaps draw a bounding box over the face, or perform other operations etc.
You might need mutex locks in between 2nd and 3rd threads in this case as you can only begin drawing boxes after you obtain an output from the model. This allows your 2nd thread to begin inference of the next frame without waiting for other threads.
The above example will result in a smooth video output with the inference lagging behind slightly. If you're not sure how multithreading works, I suggest reading up on the basics first.
update. I use separate thread to capture image from camera and I don't see any speed up of FPS compare to serial... Please see my code.
import threading
import time
import cv2
import numpy as np
class myThread (threading.Thread):
def __init__(self, src):
print("thread -------------init-------------")
threading.Thread.__init__(self)
self.cap = cv2.VideoCapture(src)
self.stop = False
def run(self):
while(self.stop == False):
self.ret, self.frame = self.cap.read()
def Stop(self):
self.cap.release()
self.stop = True
def read(self):
return self.ret, self.frame
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('trainer/trainer.yml')
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath);
thread = myThread(0)
thread.start()
time.sleep(1)
start = time.time()
frames = 0
font = cv2.FONT_HERSHEY_SIMPLEX
cap = cv2.VideoCapture(0)
while(True):
ret, frame = thread.read()
frame = cv2.resize(frame, (640, 480))
frames += 1
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, 1.2, 5)
for(x,y,w,h) in faces:
# Create rectangle around the face
cv2.rectangle(frame, (x-20,y-20), (x+w+20,y+h+20), (0,255,0), 4)
Id, con = recognizer.predict(gray[y:y+h,x:x+w])
print(Id, con)
# Check the ID if exist
if(con < 60):
if(Id == 1):
Id = "HUY"
if(Id == 2):
Id = "HOA"
#If not exist, then it is Unknown
else:
#print(Id)
Id = "Unknow"
# Put text describe who is in the picture
cv2.rectangle(frame, (x-22,y-90), (x+w+22, y-22), (0,255,0), -1)
cv2.putText(frame, str(Id), (x,y-40), font, 2, (255,255,255), 3)
if cv2.waitKey(10) & 0xFF == ord('q'):
thread.Stop()
break
cv2.imshow("frame", frame)
end = time.time()
second = end - start
print("second:", + second)
print(frames/second)
cv2.destroyAllWindows()