I have a code which breaks down a video into frames and edits the image and puts it back into a video, but I am realizing that it's really slow... So I looked into multiprocessing for speeding up the code, and it works! As I can see it processes the images much faster, but the problem is, when I add those frames to a new video, it doesn't work, the video remains empty!
Here is my code:
# Imports
import cv2, sys, time
import numpy as np
from scipy.ndimage import rotate
from PIL import Image, ImageDraw, ImageFont, ImageOps
import concurrent.futures
def function(fullimg):
img = np.array(Image.fromarray(fullimg).crop((1700, 930, 1920-60, 1080-80)))
inpaintRadius = 10
inpaintMethod = cv2.INPAINT_TELEA
textMask = cv2.imread('permanentmask.jpg', 0)
final_result = cv2.inpaint(img.copy(), textMask, inpaintRadius, inpaintMethod)
text = Image.fromarray(np.array([np.array(i) for i in final_result]).astype(np.uint8)).convert('RGBA')
im = np.array([[tuple(x) for x in i] for i in np.zeros((70, 160, 4))])
im[1:-1, 1:-1] = (170, 13, 5, 40)
im[0, :] = (0,0,0,128)
im[1:-1, [0, -1]] = (0,0,0,128)
im[-1, :] = (0,0,0,128)
im = Image.fromarray(im.astype(np.uint8))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype('arialbd.ttf', 57)
draw.text((5, 5),"TEXT",(255,255, 255, 128),font=font)
text.paste(im, mask=im)
text = np.array(text)
fullimg = Image.fromarray(fullimg)
fullimg.paste(Image.fromarray(text), (1700, 930, 1920-60, 1080-80))
fullimg = cv2.cvtColor(np.array(fullimg), cv2.COLOR_BGR2RGB)
return fullimg
cap = cv2.VideoCapture('before2.mp4')
_fourcc = cv2.VideoWriter_fourcc(*'MPEG')
out = cv2.VideoWriter('after.mp4', _fourcc, 29.97, (1280,720))
frames = []
lst = []
while cap.isOpened():
ret, fullimg = cap.read()
if not ret:
break
frames.append(fullimg)
if len(frames) >= 8:
if __name__ == '__main__':
with concurrent.futures.ProcessPoolExecutor() as executor:
results = executor.map(function, frames)
for i in results:
print(type(i))
out.write(i)
frames.clear()
cap.release()
out.release()
cv2.destroyAllWindows() # destroy all opened windows
My code inpaints a watermark and adds another watermark using PIL.
If I don't use multiprocessing the code works. But if I do use multiprocessing, it gives an empty video.
I am not that familiar with OpenCV, but there seems to be a few things that should be corrected in your code. First, if you are running under Windows, as you appear to be because you have if __name__ == '__main__': guarding the code that creates new processes (by the way, when you tag a question with multiprocessing, you should also tag the question with the platform being used), then any code at global scope will be executed by every process created to implement your pool. That means you should move if __name__ == '__main__': as follows:
if __name__ == '__main__':
cap = cv2.VideoCapture('before2.mp4')
_fourcc = cv2.VideoWriter_fourcc(*'MPEG')
out = cv2.VideoWriter('after.mp4', _fourcc, 29.97, (1280,720))
frames = []
lst = []
while cap.isOpened():
ret, fullimg = cap.read()
if not ret:
break
frames.append(fullimg)
if len(frames) >= 8:
with concurrent.futures.ProcessPoolExecutor() as executor:
results = executor.map(function, frames)
for i in results:
print(type(i))
out.write(i)
frames.clear()
cap.release()
out.release()
cv2.destroyAllWindows() # destroy all opened windows
If you do not do this it seems to me that every sub-process in the pool will first attempt in parallel to create an empty video (the function worker function and out.write will never be called by these processes) and only then will the main process be able to invoke the function worker function using map. This doesn't quite explain why the main process doesn't succeed after all of these wasteful attempts. But...
You also have:
while cap.isOpened():
The documentations states that isOpened() returns True if the previous VideoCapture constructor succeeded. Then if this returns True once, why wouldn't it return True the next time it is tested and you end up looping indefinitely? Shouldn't the while be changed to an if? And doesn't this suggest that isOpened() is perhaps returning False or else you would be looping indefinitely? Or what if len(frames) < 8? It seems then you would also end up with an empty output file.
My suggestion would be to make the above changes and try again.
Update
I took a closer look at the code more closely and it appears that it is looping reading the input (before2.mp4) one frame at a time and when it has accumulated 8 frames or more it creates a pool and processes the frames it has accumulated and writing them out to the output (after.mp4). But that means that if there are, for example, 8 more frames, it will create a brand new processing pool (very wasteful and expensive) and then write out the 8 additional processed frames. But if there were only 7 additional frames, they would never get processed and written out. I would suggest the following code (untested, of course):
def main():
import os
cap = cv2.VideoCapture('before2.mp4')
if not cap.isOpened():
return
_fourcc = cv2.VideoWriter_fourcc(*'MPEG')
out = cv2.VideoWriter('after.mp4', _fourcc, 29.97, (1280,720))
FRAMES_AT_A_TIME = 8
pool_size = min(FRAMES_AT_A_TIME, os.cpu_count())
with concurrent.futures.ProcessPoolExecutor(max_workers=pool_size) as executor:
more_frames = True
while more_frames:
frames = []
for _ in range(FRAMES_AT_A_TIME):
ret, fullimg = cap.read()
if not ret:
more_frames = False
break
frames.append(fullimg)
if not frames:
break # no frames
results = executor.map(function, frames)
for i in results:
print(type(i))
out.write(i)
cap.release()
out.release()
cv2.destroyAllWindows() # destroy all opened windows
if __name__ == '__main__':
main()
I have a simple program for reading webcams, but the reading results are very slow, so I lower the quality of reading images from the webcam, but the reading is still slow, so I try to use multiprocessing, so I'm testing a simple program to find out if my multiprocessing program is running correctly or not. but I don't know why the variable "cap" cannot be read. and I don't know how to solve it.
this is my program :
import cv2
import numpy as np
import multiprocessing
def get():
global cap
cap = cv2.VideoCapture(0)
return cap
def video(cap):
_, frame = cap.read()
frame = cv2.flip(frame, 1)
return frame
if __name__ == "__main__":
p1 = multiprocessing.Process(target = get)
p1.start()
p1.join()
while True:
frame = video(cap)
cv2.imshow("frame", frame)
key = cv2.waitKey(1)
if key == 27: #Key 'S'
break
cv2.waitKey(0)
cv2.destroyAllWindows()
Actually, cap has never been declared. Try to insert this line after your import satements:
cap = None
This will take care of the missing cap. Of course this will then lead to other problems in your code, but it is a stating point.
Good luck
Andreas
I have code like this. I know it's not correct, but I'm new at this and not sure how to fix it. How do I make it so cv2 runs on every video I have in a folder, taking a frame every 3 seconds, and saving the frame images in a new folder within /data/ --- ex. /data/rise_of_skywalker?
Thanks
import cv2
import numpy as np
import os
def frame_capture(file):
cap = cv2.VideoCapture(file)
try:
if not os.path.exists('data'):
os.makedirs('data')
except OSError:
print('Error: Creating directory of data')
currentFrame = 0
while(True):
# Capture frame by frame
ret, frame = cap.read()
'''
(((how do I change this block here to get it for every 3 seconds?)))
'''
if currentFrame == 5:
name = './data/frame' + str(currentFrame) + '.jpg'
print ('Creating...' + name)
cv2.imwrite(name, frame)
# To stop duplicate images
currentFrame += 1
cap.release()
cv2.destroyAllWindows()
print ("Done!")
for file in os.listdir("/users/x/Desktop/y/videos"):
if file.endswith(".mp4"):
path=os.path.join("/users/x/Desktop/y/videos", file))
frame_capture(path)
If I understand correctly, what you want to do is to change the line if currentFrame == 5: to if currentFrame % (3 * fps) == 0:
And before that line, outside the loop, add fps = cap.get(cv2.CAP_PROP_FPS)
import cv2, time
import numpy as np
I want to make this code not overwrite the previously saved video
video = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*"XVID")
This line of code makes the video save as output.avi all the time while I want to make it so that it saves the date of that day
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640,480))
a = 0
while True:
a = a + 1
check, frame = video.read()
print(check)
print(frame)
out.write(frame)
cv2.imshow("Capturing", frame)
I feel as if I used an if statement somewhere around here then it could make the video no overwrite the previous video however I have tried but it did not work
key = cv2.waitKey(1)
if key == ord('q') :
break
print(a)
video.release()
out.release()
cv2.destroyAllWindows()
I also want to make this code save the video as the current date of day if that is possible
You simply format the filename and put the date in it:
import datetime
filename = 'output_{0}.avi'.format(datetime.datetime.now().strftime("%Y-%m-%d"))
print (filename)
Output:
output_2018-02-25.avi
Then you supply it to your video-create method:
out = cv2.VideoWriter(filename, fourcc, 20.0, (640,480))
Look here for formatting tips for datetime.strftime
Probably best to put it inside a function that gives you the correct name, also checking if that file already exists and eventually adding a running number as well:
import datetime
import os
def getAviNameWithDate(nameIn="output.avi"):
"""Needs a file ending on .avi, inserts _<date> before .avi.
If file exists, it appends a additional _number after the <date>
ensuring filename uniqueness at this time."""
if not nameIn.endswith(".avi"):
raise ValueError("filename must end on .avi")
filename = nameIn.replace(".avi","_{0}.avi")
.format(datetime.datetime.now().strftime("%Y-%m-%d"))
if os.path.isfile(filename): # if already exists
fn2 = filename[0:-4]+'_{0}.avi' # modify pattern to include a number
count = 1
while os.path.isfile(fn2.format(count)): # increase number until file not exists
count += 1
return fn2.format(count) # return file with number in it
else: # filename ok, return it
return filename
# test it
for _ in range(5):
with open(getAviNameWithDate("a.avi"),"w") as w:
w.write("1")
Execute the snippit to see how it creates:
a_2018-02-25.avi
a_2018-02-25_1.avi
a_2018-02-25_2.avi
a_2018-02-25_3.avi
a_2018-02-25_4.avi
Aim : Detect the motion and save only the motion periods in files with names of the starting time.
Now I met the issue about how to save the video to the files with video starting time.
What I tested :
I tested my program part by part. It seems that each part works well except the saving part.
Running status: No error. But in the saving folder, there is no video. If I use a static saving path instead, the video will be saved successfully, but the video will be override by the next video. My codes are below:
import cv2
import numpy as np
import time
cap = cv2.VideoCapture( 0 )
bgst = cv2.createBackgroundSubtractorMOG2()
fourcc=cv2.VideoWriter_fourcc(*'DIVX')
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
n = "start_time"
while True:
ret, frame = cap.read()
dst = bgst.apply(frame)
dst = np.array(dst, np.int8)
if np.count_nonzero(dst)>3000: # use this value to adjust the "Sensitivity“
print('something is moving %s' %(time.ctime()))
path = r'E:\OpenCV\Motion_Detection\%s.avi' %n
out = cv2.VideoWriter( path, fourcc, 50, size )
out.write(frame)
key = cv2.waitKey(3)
if key == 32:
break
else:
out.release()
n = time.ctime()
print("No motion Detected %s" %n)
What I meant is:
import cv2
import numpy as np
import time
cap = cv2.VideoCapture( 0 )
bgst = cv2.createBackgroundSubtractorMOG2()
fourcc=cv2.VideoWriter_fourcc(*'DIVX')
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
path = r'E:\OpenCV\Motion_Detection\%s.avi' %(time.ctime())
out = cv2.VideoWriter( path, fourcc, 16, size )
while True:
ret, frame = cap.read()
dst = bgst.apply(frame)
dst = np.array(dst, np.int8)
for i in range(number of frames in the video):
if np.count_nonzero(dst)<3000: # use this value to adjust the "Sensitivity“
print("No Motion Detected")
out.release()
else:
print('something is moving %s' %(time.ctime()))
#label each frame you want to output here
out.write(frame(i))
key = cv2.waitKey(1)
if key == 32:
break
cap.release()
cv2.destroyAllWindows()
If you see the code there will be a for loop, within which the process of saving is done.
I do not know the exact syntax involving for loop with frames, but I hope you have the gist of it. You have to find the number of frames present in the video and set that as the range in the for loop.
Each frame gets saved uniquely (see the else condition.) As I said I do not know the syntax. Please refer and follow this procedure.
Cheers!