无法使用 multiprocessing + cv2 将帧写入视频
Can't write frames to a video with multiprocessing + cv2
我有一个代码可以将视频分解成帧并编辑图像并将其放回视频中,但我意识到它真的很慢......所以我研究了多处理以加速代码,它有效!正如我所见,它处理图像的速度要快得多,但问题是,当我将这些帧添加到新视频时,它不起作用,视频仍然是空的!
这是我的代码:
# 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
我的代码修复了一个水印并使用 PIL 添加了另一个水印。
如果我不使用 multiprocessing
,代码就可以工作。但是如果我确实使用 multiprocessing
,它会给出一个空视频。
我对 OpenCV
不太熟悉,但您的代码中似乎有几处需要更正。首先,如果你在 Windows 下 运行,因为你有 if __name__ == '__main__':
保护创建新进程的代码(顺便说一句,当你用 [= 标记问题时15=],你还应该用正在使用的平台来标记问题),那么全局范围内的任何代码都将由为实现你的池而创建的每个进程执行。这意味着您应该按如下方式移动 if __name__ == '__main__':
:
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
如果你不这样做,在我看来,池中的每个子进程都会首先尝试并行创建一个空视频(function
worker 函数和 out.write
永远不会被这些进程调用),只有这样主进程才能使用 map
调用 function
辅助函数。这并不能完全解释为什么在所有这些无意义的尝试之后主进程没有成功。但是...
您还有:
while cap.isOpened():
文档指出 isOpened()
returns True
如果前面的 VideoCapture
构造函数成功。那么如果这个 returns True
一次,为什么在下一次测试时不 return True
而你最终会无限期地循环? while
不应该改为 if
吗?这是否表明 isOpened()
可能 returning False
否则你会无限期地循环?或者如果 len(frames) < 8
怎么办?看来你最终也会得到一个空的输出文件。
我的建议是进行上述更改并重试。
更新
我更仔细地查看了代码,它似乎正在循环读取输入 (before2.mp4) 一次一帧,当它累积时8 帧或更多帧,它创建一个池并处理它积累的帧并将它们写出到输出(after.mp4)。但这意味着如果还有 8 个帧,它将创建一个全新的处理池(非常浪费和昂贵),然后写出这 8 个额外处理的帧。但是如果只有 7 个额外的帧,它们将永远不会被处理和写出。我建议使用以下代码(当然未经测试):
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()
我有一个代码可以将视频分解成帧并编辑图像并将其放回视频中,但我意识到它真的很慢......所以我研究了多处理以加速代码,它有效!正如我所见,它处理图像的速度要快得多,但问题是,当我将这些帧添加到新视频时,它不起作用,视频仍然是空的!
这是我的代码:
# 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
我的代码修复了一个水印并使用 PIL 添加了另一个水印。
如果我不使用 multiprocessing
,代码就可以工作。但是如果我确实使用 multiprocessing
,它会给出一个空视频。
我对 OpenCV
不太熟悉,但您的代码中似乎有几处需要更正。首先,如果你在 Windows 下 运行,因为你有 if __name__ == '__main__':
保护创建新进程的代码(顺便说一句,当你用 [= 标记问题时15=],你还应该用正在使用的平台来标记问题),那么全局范围内的任何代码都将由为实现你的池而创建的每个进程执行。这意味着您应该按如下方式移动 if __name__ == '__main__':
:
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
如果你不这样做,在我看来,池中的每个子进程都会首先尝试并行创建一个空视频(function
worker 函数和 out.write
永远不会被这些进程调用),只有这样主进程才能使用 map
调用 function
辅助函数。这并不能完全解释为什么在所有这些无意义的尝试之后主进程没有成功。但是...
您还有:
while cap.isOpened():
文档指出 isOpened()
returns True
如果前面的 VideoCapture
构造函数成功。那么如果这个 returns True
一次,为什么在下一次测试时不 return True
而你最终会无限期地循环? while
不应该改为 if
吗?这是否表明 isOpened()
可能 returning False
否则你会无限期地循环?或者如果 len(frames) < 8
怎么办?看来你最终也会得到一个空的输出文件。
我的建议是进行上述更改并重试。
更新
我更仔细地查看了代码,它似乎正在循环读取输入 (before2.mp4) 一次一帧,当它累积时8 帧或更多帧,它创建一个池并处理它积累的帧并将它们写出到输出(after.mp4)。但这意味着如果还有 8 个帧,它将创建一个全新的处理池(非常浪费和昂贵),然后写出这 8 个额外处理的帧。但是如果只有 7 个额外的帧,它们将永远不会被处理和写出。我建议使用以下代码(当然未经测试):
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()