Recording video with Opencv Python Multithreading - python

I am trying to record a video using opencv in python when doing multithreading within the thread that displays the stream on the window. I m fairly new to multithreading and I am not sure what is the reason I am unable to get a video recorded. I save a file but it does not have the stream in it. Pointers greatly appreciated.This is my code:
import cv2
import os
import threading
import shutil
import json
import re
import datetime
import time
now=datetime.datetime.now()
class camThread(threading.Thread):
def __init__(self, previewName, camID):
threading.Thread.__init__(self)
self.previewName = previewName
self.camID = camID
def run(self):
print("Starting " + self.previewName)
camPreview(self.previewName, self.camID)
def camPreview(previewName, camID):
cv2.namedWindow(previewName)
cam = cv2.VideoCapture(camID)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 480)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
if cam.isOpened():
rval, frame = cam.read()
frame_width = int(cam.get(3))
frame_height = int(cam.get(4))
else:
rval = False
while rval:
cv2.namedWindow(previewName, cv2.WINDOW_NORMAL)
if (camID == 2):
frame= cv2.flip(frame,-1)
# cv2.setWindowProperty(previewName, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.imshow(previewName, frame)
# cam.set(CV_CAP_PROP_SETTINGS, 0)
rval, frame = cam.read()
key = cv2.waitKey(20)
if key == 115 :
Cam1="Cam"+str(camID)+"_"+timestr
ts=datetime.datetime.now()
filename="{}.avi".format(Cam1+ts.strftime("%Y%m%d_%H-%M-%S"))
out=cv2.VideoWriter(filename,cv2.VideoWriter_fourcc('M','J', 'P','G'),10,(480,720))
out.write(frame)
if key == 27:
print("Stopping recording")
break
if key == 27: # exit on ESC
break
cv2.destroyWindow(previewName)
# Create threads as follows
thread1 = camThread("Camera 1", 0)
thread2 = camThread("Camera 2", 2)
thread3 = camThread("Camera 3", 3)
timestr=str(now.strftime("%Y%m%d_%H-%M-%S"))
print("Working Directory:")
print(timestr)
#thread1.start()
thread2.start()
thread3.start()
print()
print("Active threads", threading.activeCount())

from threading import Thread
import cv2
import time
class VideoWriterWidget(object):
def __init__(self, video_file_name, src=0):
# Create a VideoCapture object
self.frame_name = str(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)
# Default resolutions of the frame are obtained (system dependent)
self.frame_width = int(self.capture.get(3))
self.frame_height = int(self.capture.get(4))
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))
# 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.capture.isOpened():
(self.status, self.frame) = self.capture.read()
def show_frame(self):
# Display frames in main program
if self.status:
cv2.imshow(self.frame_name, self.frame)
# Press Q on keyboard to stop recording
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.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 = 'Your link1'
video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
src2 = 'Your link2'
video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
src3 = 'Your link3'
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(5)

I think you're on the right track but I was unable to save a file with your code. Here's a video-stream-to-video widget using multithreading to obtain the frames. There are two threads for each camera stream:
Thread #1 - Dedicated to only reading frames from the camera stream.
Thread #2 - Dedicated for processing frames (showing and writing).
We separate reading frames from showing/writing because cv2.VideoCapture.read() is a blocking operation. Thus we read frames in its own independent thread to 'improve' FPS by reducing latency due to I/O operations. In addition, by isolating frame capture to its own thread, there will always be a frame ready to be processed instead of having to wait for the I/O operation to complete and return a new frame. In our second thread dedicated to processing, we are now freely able to show and save each frame to our output file.
Also by encapsulating all this into a single object, we can create a set of threads for each camera which scales easily no matter how many cameras are being used. Since each camera stream is spawned in a background thread, we must keep the main thread alive. Be sure to change the src string to your own camera. Here's an example of recording three video streams simultaneously.
from threading import Thread
import cv2
import time
class VideoWriterWidget(object):
def __init__(self, video_file_name, src=0):
# Create a VideoCapture object
self.frame_name = str(src)
self.video_file = video_file_name
self.video_file_name = video_file_name + '.avi'
self.capture = cv2.VideoCapture(src)
# Default resolutions of the frame are obtained (system dependent)
self.frame_width = int(self.capture.get(3))
self.frame_height = int(self.capture.get(4))
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))
# 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.capture.isOpened():
(self.status, self.frame) = self.capture.read()
def show_frame(self):
# Display frames in main program
if self.status:
cv2.imshow(self.frame_name, self.frame)
# Press Q on keyboard to stop recording
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.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 = 'Your link1'
video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
src2 = 'Your link2'
video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
src3 = 'Your link3'
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(5)

Related

Displaying video frames through streamlit.empty() not working on Google Colab

I have a simple code that reads video stream from youtube for processing with OpenCV. I want to display these video frames in Streamlit app with st.empty(). On localhost it works fine, but when I run same code in Google Colab, only first frame is shown and then the video freezes. Is there some workaround?
Here is my code:
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 = 'YOUTUBE .m3u8 STREAM FILE'
threaded_camera = ThreadedCamera(src)
st.title("Real Time Face Emotion Detection Application")
st.header("Webcam Live Feed")
image_place = st.empty()
while True:
try:
image_place.image(threaded_camera.frame)
except AttributeError:
pass

How can I switch Python Tkinter GUI camera source?

First of all, I wanted to put a camera switch option on a piece of code found here. First, I found the possible camera indexes and tried to change the camera sources with the push of a button. Even though the function worked, the camera source was not changed. I am looking for a solution for this.
In other words, how can I change the camera indexes through the function. The "returnCameraIndexes ()" function that I wrote before starting the 'main' section in this topic creates the total camera indexes. The "ChangeCam(" function is the function that performs the exchange between all the resources I have acquired.
import numpy as np
from multiprocessing import Process, Queue
from Queue import Empty
import cv2
from PIL import Image, ImageTk
import time
import Tkinter as tk
#tkinter GUI functions----------------------------------------------------------
def quit_(root, process):
process.terminate()
root.destroy()
def update_image(image_label, queue):
frame = queue.get()
im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
a = Image.fromarray(im)
b = ImageTk.PhotoImage(image=a)
image_label.configure(image=b)
image_label._image_cache = b # avoid garbage collection
root.update()
def update_all(root, image_label, queue):
update_image(image_label, queue)
root.after(0, func=lambda: update_all(root, image_label, queue))
#multiprocessing image processing functions-------------------------------------
def image_capture(queue,change_camera_flag):
vidFile = cv2.VideoCapture(change_camera_flag)
while True:
try:
flag, frame=vidFile.read()
if flag==0:
break
queue.put(frame)
cv2.waitKey(20)
except:
continue
#check all camera Sources
def returnCameraIndexes():
# checks the first 10 indexes.
index = 0
arr = []
i = 10
while i > 0:
cap = cv2.VideoCapture(index)
if cap.read()[0]:
arr.append(index)
cap.release()
index += 1
i -= 1
return arr
camera_list = returnCameraIndexes()
change_camera_flag = 0
# Switch camera sourcess
def ChangeCam():
global change_camera_flag,cap_1
if(len(camera_list)-1 == change_camera_flag):
change_camera_flag = 0
else:
change_camera_flag = change_camera_flag + 1
print(change_camera_flag)
if __name__ == '__main__':
queue = Queue()
print ('queue initialized...')
root = tk.Tk()
print ('GUI initialized...')
image_label = tk.Label(master=root)# label for the video frame
image_label.pack()
print ('GUI image label initialized...')
p = Process(target=image_capture, args=(queue,change_camera_flag))
p.start()
print ('image capture process has started...')
change_source_button = tk.Button(master=root, text='ChangeCamera',command=lambda: ChangeCam())
change_source_button.pack()
# quit button
quit_button = tk.Button(master=root, text='Quit',command=lambda: quit_(root,p))
quit_button.pack()
print ('quit button initialized...')
# setup the update callback
root.after(0, func=lambda: update_all(root, image_label, queue))
print ('root.after was called...')
root.mainloop()
print ('mainloop exit')
p.join()
print ('image capture process exit')

How to read multi-camera streams with OpenCV seamlessly?

I went through this beautifully explained Q/A and I want something similar into a much stripped down version of the code I found there which includes PyQt4 and qdarkstyle.
My code is as follows:
import pafy
import base64
import zmq
from threading import Thread
import cv2
import time
class IPCamera(object):
def __init__(self, src=0):
self.frame = None
self.status = None
self.capture = cv2.VideoCapture(src)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)
self.FPS = 1/25
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()
# self.frame = cv2.resize(self.frame, (640, 480)) # resize the frame
time.sleep(self.FPS)
def show_frame(self):
cv2.imshow('frame', self.frame)
cv2.waitKey(self.FPS_MS)
if __name__ == '__main__':
# Sample youtube video starts
youtube_link = 'https://www.youtube.com/watch?v=QgaUKlAuqn8'
vPafy = pafy.new(youtube_link)
play = vPafy.getbest(preftype="mp4")
src = play.url
# Sample youtube video ends
# Creating ZMQ context starts
context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://localhost:5555')
# cv2.namedWindow("Client Started!")
# Creating ZMQ context ends
threaded_camera = IPCamera(src)
while threaded_camera.capture.isOpened():
try:
if threaded_camera.status is True:
encoded, buffer = cv2.imencode('.jpg', threaded_camera.frame)
jpg_as_text = base64.b64encode(buffer)
footage_socket.send(jpg_as_text)
# key = cv2.waitKey(1)
# threaded_camera.show_frame()
# if key == 27: # exit on ESC
# break
# else:
# break
except AttributeError:
pass
The above code is running fine, I want to implement the code from the Q/A on the aforementioned link with those libraries removed. Can you help me in that?

Picamera multiprocess

I'm trying to use a separate process to take pictures. This code was modified from being a Thread to being a multiprocess (by me, this is why it doesn't work). When i create an instance of this class and then run it with obj.start() a popup appears the program is already running, do you want to stop it? I don't understand what am I doing wrong?
P.S ("GO" output is never shown on the screen)
# import the necessary packages
from picamera.array import PiRGBArray
from picamera import PiCamera
import multiprocessing
class MultiProcess(multiprocessing.Process):
def __init__(self):
print("INIT")
multiprocessing.Process.__init__(self)
# initialize the camera and stream
self.camera = PiCamera()
self.camera.resolution = (640, 480)
self.camera.framerate = 32
self.rawCapture = PiRGBArray(self.camera, size=(320, 240))
self.stream = self.camera.capture_continuous(self.rawCapture, format="bgr", use_video_port=True)
# initialize the frame and the variable used to indicate
# if the thread should be stopped
self.frame = None
self.stopped = False
def start(self):
# start the thread to read frames from the video stream
p = multiprocessing.Process(target=self.update, args=())
print("1")
p.daemon = True
print("2")
p.start()
print("3")
p.join()
print("GO")
return self
def update(self):
# keep looping infinitely until the thread is stopped
for f in self.stream:
print("NEVER REACH HERE")
# grab the frame from the stream and clear the stream in
# preparation for the next frame
self.frame = f.array
self.rawCapture.truncate(0)
# if the thread indicator variable is set, stop the thread
# and resource camera resources
if self.stopped:
self.stream.close()
self.rawCapture.close()
self.camera.close()
return
def read(self):
# return the frame most recently read
return self.frame
def stop(self):
# indicate that the thread should be stopped
self.stopped = True

How can i fetch live stream from url?

I am using RPi-cam-web-interface and from pi camera, rasperry pi continuously throws image to a link, I want to process that image using opencv on my computer.
Here is the link software I'm using suggested by sentdex.
Here's a link to sentdex's video.
Streaming URL to extract image from
is what it looks like, the url from which image is to be extracted.
Have you tried passing your url in VideoCapture like below:
cap = cv2.VideoCapture()
cap.opne("Your_URL")
If you can put your RPI link into VLC player, it should work for this widget. You can stream from a url using cv2.videoCapture(). Change rtsp_stream_link to your RPI url.
from threading import Thread
import cv2
class RTSPVideoWriterObject(object):
def __init__(self, src=0):
# Create a VideoCapture object
self.capture = cv2.VideoCapture(src)
# Default resolutions of the frame are obtained (system dependent)
self.frame_width = int(self.capture.get(3))
self.frame_height = int(self.capture.get(4))
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
self.output_video = cv2.VideoWriter('output.avi', self.codec, 30, (self.frame_width, self.frame_height))
# 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()
def show_frame(self):
# Display frames in main program
if self.status:
cv2.imshow('frame', self.frame)
# Press Q on keyboard to stop recording
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.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)
if __name__ == '__main__':
rtsp_stream_link = 'your stream link!'
video_stream_widget = RTSPVideoWriterObject(rtsp_stream_link)
while True:
try:
video_stream_widget.show_frame()
#video_stream_widget.save_frame()
except AttributeError:
pass

Categories