I have a function that replay some steps from a .json file and another fuction which record those steps. I need the recordingScreen function to stop once the playActions function finishes simultaneously (by using the flag) and create a video for each iteration, but it only creates the video for the last file (iteration)
I have tried with a flag that changes from false when the playActions function finishes I have also tried with queue from this example link and using using a threadsafe threading.Event() from this example link. But as I am a beginner I have not been able to implement any of them correctly within my code, which is as follow:
files= ["actions_test_10-07-2020_15-56-43.json", "actions_test_10-08-2020_14-59-00.json"]
date = datetime.today().strftime("%m-%d-%Y_%H-%M-%S")
Stop_recording = False
def main():
initializePyAutoGUI()
countdownTimer()
for i in range(len(files)):
global Stop_recording
Stop_recording = False
t1 = threading.Thread(target=playActions, args=[files[i]])
t2 = threading.Thread(target=recordScreen)
t1.start()
t2.start()
t1.join()
t2.join()
print("Done")
def recordScreen():
output = '{}.avi'.format(date)
img = pyautogui.screenshot()
img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
# get info from img
height, width, channels = img.shape
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output, fourcc, 20.0, (width, height))
while not Stop_recording:
img = pyautogui.screenshot()
image = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
out.write(image)
StopIteration(0.5)
out.release()
cv2.destroyAllWindows()
def playActions(filename):
# Read the file
script_dir = os.path.dirname(__file__)
filepath = os.path.join(script_dir, 'recordings', filename)
with open(filepath, 'r') as jsonfile:
# parse the json
data = json.load(jsonfile)
# loop over each action
# Because we are not waiting any time before executing the first action, any delay before the initial
# action is recorded will not be reflected in the playback.
for index, action in enumerate(data):
action_start_time = time()
# look for escape input to exit
if action['button'] == 'Key.esc':
break
# perform the action
if action['type'] == 'keyDown':
key = convertKey(action['button'])
pyautogui.keyDown(key)
print("keyDown on {}".format(key))
elif action['type'] == 'keyUp':
key = convertKey(action['button'])
pyautogui.keyUp(key)
print("keyUp on {}".format(key))
elif action['type'] == 'click' and action['button'] == "Button.right":
pyautogui.rightClick(action['pos'][0], action['pos'][1], duration=0.25)
print("right click on {}".format(action['pos']))
elif action['type'] == 'click' and action['button'] == "Button.left":
# Check if the period between clicks is short and perform a double click then, otherwise
# it performs a single click
if index > 0:
if (data[index]['time']) - (data[index - 1]['time']) < 0.5:
pyautogui.doubleClick(action['pos'][0], action['pos'][1])
print("Double click on {}".format(action['pos']))
pyautogui.leftClick(action['pos'][0], action['pos'][1], duration=0.25)
print("left click on {}".format(action['pos']))
# then sleep until next action should occur
try:
next_action = data[index + 1]
except IndexError:
# this was the last action in the list
break
elapsed_time = next_action['time'] - action['time']
# if elapsed_time is negative, that means our actions are not ordered correctly. throw an error
if elapsed_time < 0:
raise Exception('Unexpected action ordering.')
# adjust elapsed_time to account for our code taking time to run
elapsed_time -= (time() - action_start_time)
if elapsed_time < 0:
elapsed_time = 0
print('sleeping for {}'.format(elapsed_time))
sleep(elapsed_time)
global Stop_recording
Stop_recording = True
Related
I want to capture image and store it on my local system after every n seconds, I can not set frame(5) as I want video and detection to run completely. Currently my code is capturing image whenever a condition is failed.
def create_alert(self):
count = 0
cap = cv2.VideoCapture(0)
while cap.isOpened():
r,f = cap.read()
try:
info = ppe.detection(f)
x,y,w,h,label,conf = info[0]
if label == "lineman_fail":
# engine.say("Warning")
# engine.runAndWait()
ppe.take_screenshot(f,count)
count+=1
print(count)
print("Something wrong")
# cv2.imwrite("img_"+str(count)+".jpg",f)
except Exception as e:
print("_______-",e)
cv2.imshow("image",f)
if cv2.waitKey(1) & 0xFF == ord("q") :
break
cap.release()
cv2.destroyAllWindows()
def take_screenshot(self,frame,count):
prev = time.time()
cv2.imwrite("screen_shot/img_"+str(count)+".jpg",frame)
In order to capture image every n seconds try using datetime library, and find the difference between current time and elapsed time. Then use cv2's imwrite
while True:
current = time()
yolo_v4.delta += current - previous
previous = current
frame = camera.get_frame()
if yolo_v4.delta > 10:
ct=datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
cv2.imwrite("screen_shot/img_"+str(ct)+".jpg",frame)
at the moment I am reading an ip cameras live image by using the following code:
def livestream(self):
print("start")
stream = urlopen('http://192.168.4.1:81/stream')
bytes = b''
while True:
try:
bytes += stream.read(1024)
a = bytes.find(b'\xff\xd8')
b = bytes.find(b'\xff\xd9')
if a != -1 and b != -1:
jpg = bytes[a:b+2]
bytes = bytes[b+2:]
getliveimage = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
livestreamrotated1 = cv2.rotate(getliveimage, cv2.ROTATE_90_CLOCKWISE) #here I am rotating the image
print(type(livestreamrotated1)) #type at this point is <class 'numpy.ndarray'>
cv2.imshow('video',livestreamrotated1)
if cv2.waitKey(1) ==27: # if user hit esc
exit(0) # exit program
except Exception as e:
print(e)
print("failed at this point")
Now I want to integrate the result-image into Kivy-GUI and want to get rid of the while-loop since it freezes my GUI. Unfortunately the loop is necessary to recreate the image byte-by-byte. I would like to use cv2.VideoCapture instead and schedule this multiple times per second. This is not working at all, I am not able to capture the image from the live stream this way...where am I wrong?
cap = cv2.VideoCapture('http://192.168.4.1:81/stream?dummy.jpg')
ret, frame = cap.read()
cv2.imshow('stream',frame)
I read in some other post that a file-ending like "dummy.jpg" would be necessary at this point, but it is still not working, the program freezes.
Please help. Thank you in advance!
If you want to decouple your reading loop from your GUI loop you can use multithreading to separate the code. You can have a thread running your livestream function and dumping the image out to a global image variable where your GUI loop can pick it up and do whatever to it.
I can't really test out the livestream part of the code, but something like this should work. The read function is an example of how to write a generic looping function that will work with this code.
import cv2
import time
import threading
import numpy as np
# generic threading class
class Reader(threading.Thread):
def __init__(self, func, *args):
threading.Thread.__init__(self, target = func, args = args);
self.start();
# globals for managing shared data
g_stop_threads = False;
g_lock = threading.Lock();
g_frame = None;
# reads frames from vidcap and stores them in g_frame
def read():
# grab globals
global g_stop_threads;
global g_lock;
global g_frame;
# open vidcap
cap = cv2.VideoCapture(0);
# loop
while not g_stop_threads:
# get a frame from camera
ret, frame = cap.read();
# replace the global frame
if ret:
with g_lock:
# copy so that we can quickly drop the lock
g_frame = np.copy(frame);
# sleep so that someone else can use the lock
time.sleep(0.03); # in seconds
# your livestream func
def livestream():
# grab globals
global g_stop_threads;
global g_lock;
global g_frame;
# open stream
stream = urlopen('http://192.168.4.1:81/stream')
bytes = b''
# process stream into opencv image
while not g_stop_threads:
try:
bytes += stream.read(1024)
a = bytes.find(b'\xff\xd8')
b = bytes.find(b'\xff\xd9')
if a != -1 and b != -1:
jpg = bytes[a:b+2]
bytes = bytes[b+2:]
getliveimage = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
livestreamrotated1 = cv2.rotate(getliveimage, cv2.ROTATE_90_CLOCKWISE) #here I am rotating the image
# acquire lock and replace image
with g_lock:
g_frame = livestreamrotated1;
# sleep to allow other threads to get the lock
time.sleep(0.03); # in seconds
except Exception as e:
print(e)
print("failed at this point")
def main():
# grab globals
global g_stop_threads;
global g_lock;
global g_frame;
# start a thread
# reader = Reader(read);
reader = Reader(livestream);
# show frames from g_frame
my_frame = None;
while True:
# grab lock
with g_lock:
# show
if not g_frame is None:
# copy # we copy here to dump the lock as fast as possible
my_frame = np.copy(g_frame);
# now we can do all the slow manipulation / gui stuff here without the lock
if my_frame is not None:
cv2.imshow("Frame", my_frame);
# break out if 'q' is pressed
if cv2.waitKey(1) == ord('q'):
break;
# stop the threads
g_stop_threads = True;
if __name__ == "__main__":
main();
I am trying to save 10 seconds of buffered video using Python, in particular '.h264' format.
In order to do so, I have been using a PiCamera connected to a Raspberry Pi and the script shown below. The main road block I am facing right now is that instead of saving the file directly to a location [stream.copy_to(str(time)+'.h264')] I would like to save it to a variable in order to perform certain operations (e.g. change video resolution) before finally saving it. Any idea how this can be achieve?
Thanks in advance!
import time
import io
import os
import picamera
import datetime as dt
from PIL import Image
import cv2
#obtain current time
def return_currentTime():
return dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
#trigger event declaration
def motion_detected():
while True:
print ("Trigger event(y)?")
trigger = input ()
if trigger =="y":
time = return_currentTime()
print ("Buffering...")
stream.copy_to(str(time)+'.h264')
else:
camera.stop_recording()
break
#countdown timer
def countdown (t):
while t:
mins, secs = divmod (t,60)
timer = '{:02d}:{:02d}'.format(mins, secs)
print(timer, end="\r")
time.sleep(1)
t-=1
print('Buffer available!')
camera = picamera.PiCamera()
camera.resolution = (640, 480)
stream = picamera.PiCameraCircularIO(camera, seconds = 5)
#code will work using h264 as format
camera.start_recording (stream, format = 'h264')
countdown(5)
motion_detected()
I don't have a Raspberry Pi, but I have an idea of how you can do it.
I used VideoStream which also supports PiCamera so you can use the below code.
stream = VideoStream(usePiCamera=False,
resolution=(640, 480),
framerate=32).start()
# Wait for a two-second for warming the webcam.
time.sleep(2.0)
Start getting frames
while True:
frame = stream.read()
countdown(5)
motion_detected()
Modify motion_detected() for saving frames.
while True:
frame = stream.read()
countdown(5)
motion_detected(frame)
Now we need to store frames either using array or dictionary.
Dictionary is faster than the array. (source)
We need to initialize a global dictionary on top of the project file.
import time
import datetime as dt
from imutils.video import VideoStream
dictionary = {}
count = 0
We need to modify the motion_detected method, we start by initializing the input parameter
# trigger event declaration
def motion_detected(input_frame):
Second, we define the global variables inside motion_detected
# trigger event declaration
def motion_detected(input_frame):
global dictionary
global count
Unfortunately, VideoStream object has no copy_to attribute, therefore I have to directly assign frame to the dictionary:
def motion_detected(input_frame):
global dictionary
global count
while True:
print("Trigger event(y)?")
trigger = input()
if trigger == "y":
current_time = return_current_time()
print("Buffering...")
# stream.copy_to(str(current_time) + '.h264')
dictionary[count] = input_frame
count += 1
if count == 10:
print("\n10 frames are stored\n")
else:
stream.stop()
break
Now we can perform certain operations like detecting edges.
while True:
frame = stream.read()
countdown(5)
motion_detected(frame)
for stored_frame in dictionary.values():
result = cv2.Canny(image=stored_frame,
threshold1=50,
threshold2=100)
Output:
Saving the frames
To save the frames, you need to enumerate over the stored frames.
for count, stored_frame in enumerate(dictionary.values()):
Then, apply your operation:
for count, stored_frame in enumerate(dictionary.values()):
result = cv2.Canny(image=stored_frame,
threshold1=50,
threshold2=100)
Save it to a folder.
for count, stored_frame in enumerate(dictionary.values()):
result = cv2.Canny(image=stored_frame,
threshold1=50,
threshold2=100)
cv2.imwrite("output/frame_{}.png".format(count), result)
If you want to loop through multiple times, the above code won't work. In this case, you need to iniitialize loop above the while loop.
counter = 0
while True:
frame = stream.read()
countdown(5)
motion_detected(frame)
for stored_frame in dictionary.values():
result = cv2.Canny(image=stored_frame,
threshold1=50,
threshold2=100)
cv2.imwrite("output/frame_{}.png".format(counter), result)
counter += 1
Code:
import cv2
import time
import datetime as dt
from imutils.video import VideoStream
dictionary = {}
count = 0
# obtain current time
def return_current_time():
return dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# trigger event declaration
def motion_detected(input_frame):
global dictionary
global count
while True:
print("Trigger event(y)?")
trigger = input()
if trigger == "y":
current_time = return_current_time()
print("Buffering...")
# stream.copy_to(str(current_time) + '.h264')
dictionary[count] = input_frame
count += 1
if count == 10:
print("\n10 frames are stored\n")
else:
stream.stop()
break
# countdown timer
def countdown(t):
while t:
mins, secs = divmod(t, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
print(timer, end="\r")
time.sleep(1)
t -= 1
print('Buffer available!')
stream = VideoStream(usePiCamera=False,
resolution=(640, 480),
framerate=32).start()
time.sleep(2.0)
counter = 0
while True:
frame = stream.read()
countdown(5)
motion_detected(frame)
for stored_frame in dictionary.values():
result = cv2.Canny(image=stored_frame,
threshold1=50,
threshold2=100)
cv2.imwrite("output/frame_{}.png".format(counter), result)
counter += 1
I am reading a video file such that out for every 20 frames I'm storing first frames in Input Queue. Once I get all the required frames in Input Queue, then I run multiple processes to perform some operation on these frames and store the results in output queue.
But the code always stuck at join, I tried different solutions proposed for such problems but none of them seems to work.
import numpy as np
import cv2
import timeit
import face_recognition
from multiprocessing import Process, Queue, Pool
import multiprocessing
import os
s = timeit.default_timer()
def alternative_process_target_func(input_queue, output_queue):
while not output_queue.full():
frame_no, small_frame, face_loc = input_queue.get()
print('Frame_no: ', frame_no, 'Process ID: ', os.getpid(), '----', multiprocessing.current_process())
#canny_frame(frame_no, small_frame, face_loc)
#I am just storing frame no for now but will perform something else later
output_queue.put((frame_no, frame_no))
if output_queue.full():
print('Its Full ---------------------------------------------------------------------------------------')
else:
print('Not Full')
print(timeit.default_timer() - s, ' seconds.')
print('I m not reading anymore. . .', os.getpid())
def alternative_process(file_name):
start = timeit.default_timer()
cap = cv2.VideoCapture(file_name)
frame_no = 1
fps = cap.get(cv2.CAP_PROP_FPS)
length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print('Frames Per Second: ', fps)
print('Total Number of frames: ', length)
print('Duration of file: ', int(length / fps))
processed_frames = 1
not_processed = 1
frames = []
process_this_frame = True
frame_no = 1
Input_Queue = Queue()
while (cap.isOpened()):
ret, frame = cap.read()
if not ret:
print('Size of input Queue: ', Input_Queue.qsize())
print('Total no of frames read: ', frame_no)
end1 = timeit.default_timer()
print('Time taken to fetch useful frames: ', end1 - start)
threadn = cv2.getNumberOfCPUs()
Output_Queue = Queue(maxsize=Input_Queue.qsize())
process_list = []
#quit = multiprocessing.Event()
#foundit = multiprocessing.Event()
for x in range((threadn - 1)):
# print('Process No : ', x)
p = Process(target=alternative_process_target_func, args=(Input_Queue, Output_Queue))#, quit, foundit
#p.daemon = True
p.start()
process_list.append(p)
#p.join()
# for proc in process_list:
# print('---------------------------------------------------------------', proc.p)
i = 1
for proc in process_list:
print('I am hanged here')
proc.join()
print('I am done')
i += 1
end = timeit.default_timer()
print('Time taken by face verification: ', end - start)
break
if process_this_frame:
print(frame_no)
small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
rgb_small_frame = small_frame[:, :, ::-1]
face_locations = face_recognition.face_locations(rgb_small_frame)
# frames.append((rgb_small_frame, face_locations))
Input_Queue.put((frame_no, rgb_small_frame, face_locations))
frame_no += 1
if processed_frames < 5:
processed_frames += 1
not_processed = 1
else:
if not_processed < 15:
process_this_frame = False
not_processed += 1
else:
processed_frames = 1
process_this_frame = True
print('-----------------------------------------------------------------------------------------------')
cap.release()
cv2.destroyAllWindows()
alternative_process('user_verification_2.avi')
As the documentation on Process.join() says, hanging (or "blocking") is exactly what is expected to happen:
Block the calling thread until the process whose join() method is
called terminates or until the optional timeout occurs.
join() stops current thread until the target process finishes. Target process is calling alternative_process_target_func, so the problem is obviously in that function. It never finishes. There may be more than one reason for that.
Problem 1
alternative_process_target_func runs until output_queue.full(). What if it is never full? It never ends? It is really better to determine the end some other way, e.g. run until the input queue is empty.
Problem 2
input_queue.get() will block if the input queue is empty. As the documentation says:
Remove and return an item from the queue. If optional args block is true and timeout is None (the default), block if necessary until an item is available.
You are running multiple processes, so do not expect that there is something in input just because output_queue.full() was False a moment ago, and because input size is the same as output size. A lot could have happened in the meantime.
What you want to do is:
try:
input_queue.get(False) # or input_queue.get_nowait()
except Empty:
break # stop when there is nothing more to read from the input
Problem 3
output_queue.put((frame_no, frame_no)) will block if there is no room in the output to store the data.
Again, you are assuming that there is room in output, just because you checked output_queue.full() a few moments ago, and because input size is equal to output size. Never rely on such things.
You want to do the same thing as for input:
try:
output_queue.put((frame_no, frame_no), False)
# or output_queue.put_nowait((frame_no, frame_no))
except Empty:
# deal with this somehow, e.g.
raise Exception("There is no room in the output queue to write to.")
I made this script:
import cv2
import pyaudio
import wave
import threading
import time
import subprocess
import os
import keyboard
class VideoRecorder():
# Video class based on openCV
def __init__(self):
self.open = True
self.fps = 6 # fps should be the minimum constant rate at which the camera can
self.fourcc = "MJPG" # capture images (with no decrease in speed over time; testing is required)
self.frameSize = (640,480) # video formats and sizes also depend and vary according to the camera used
self.video_filename = "temp_video.avi"
self.video_cap = cv2.VideoCapture(0)
self.video_writer = cv2.VideoWriter_fourcc(*self.fourcc)
self.video_out = cv2.VideoWriter(self.video_filename, self.video_writer, self.fps, self.frameSize)
self.frame_counts = 1
self.start_time = time.time()
# Video starts being recorded
def record(self):
timer_start = time.time()
timer_current = 0
while(self.open==True):
ret, video_frame = self.video_cap.read()
if self.frame_counts > 10:
break
if (ret==True):
self.video_out.write(video_frame)
self.frame_counts += 1
print self.frame_counts
time.sleep(0.16)
else:
#threading.Thread(target=self.stop).start()
break
# 0.16 delay -> 6 fps
time.sleep(1)
self.video_out.release()
cv2.VideoCapture(0).release()
cv2.destroyAllWindows()
dwhuiadhuiahdwia = raw_input("Testtidhwuia?")
# Finishes the video recording therefore the thread too
def stop(self):
print "You made it"
if self.open==True:
self.open=False
self.video_out.release()
self.video_cap.release()
cv2.destroyAllWindows()
hduwahduiwahdiu = raw_input("Press enter to continue...")
else:
pass
# Launches the video recording function using a thread
def start(self):
video_thread = threading.Thread(target=self.record)
video_thread.start()
def start_video_recording():
global video_thread
video_thread = VideoRecorder()
video_thread.start()
def stop_AVrecording():
frame_counts = video_thread.frame_counts
elapsed_time = time.time() - video_thread.start_time
recorded_fps = frame_counts / elapsed_time
print "total frames " + str(frame_counts)
print "elapsed time " + str(elapsed_time)
print "recorded fps " + str(recorded_fps)
video_thread.stop()
# Makes sure the threads have finished
time.sleep(1)
video_thread.stop()
print ".."
start_video_recording()
duiwhaiudhwauidhwa = raw_input("hello")
It should record a video with the camera for about 10 seconds, then save it and then turn off the camera and close the script when the user presses enter.
But it doesn't really work.
It will record a video, and it does save the video but the camera only turns off when I close the script (the camera is on, but isn't recording.)
I know this because the led next to my camera doesn't turn off when I'm prompted to press enter to continue, but does when I press enter and the script closes.
I found this, and I haven't tested it yet. But if I were to use that solution and it worked, I'd have to do it on every computer I run the script on manually.