添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

不能用多处理+cv2将帧写入视频中

1 人关注

我有一个代码,它将视频分解成帧,编辑图像并将其放回视频中,但我意识到它真的很慢......所以我研究了多处理技术来加快代码的速度,结果成功了!正如我所看到的,它处理图像的速度要快得多,但问题是,当我把这些帧添加到一个新的视频时,它不起作用,视频仍然是空的

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

我的代码是用PIL对一个水印进行内画,并添加另一个水印。

如果我不使用multiprocessing,代码就能工作。但如果我使用了multiprocessing,它就会给出一个空的视频。

python
opencv
video
multiprocessing
U12-Forward
U12-Forward
发布于 2021-04-23
1 个回答
Booboo
Booboo
发布于 2021-04-24
已采纳
0 人赞同

我对 OpenCV 不是很熟悉,但在你的代码中似乎有几处需要纠正。首先,如果你是在Windows下运行,因为你有 if __name__ == '__main__': 来保护创建新进程的代码(顺便说一下,当你用 multiprocessing 来标记一个问题时,你也应该用所使用的平台来标记这个问题),那么任何全局范围的代码都会被为实现你的池而创建的每个进程执行。这意味着你应该将 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工作函数和out.write将永远不会被这些进程调用。被这些进程调用),然后主进程才能够使用map调用function工作函数。这并不能完全解释为什么主进程在进行了所有这些浪费的尝试后还没有成功。但是...

while cap.isOpened():

文档中指出,如果之前的VideoCapture构造函数成功,isOpened()会返回True。那么,如果这一次返回True,为什么它在下一次测试时不返回True,而最终无限循环呢?难道不应该把while改为if吗?这难道不表明isOpened()也许正在返回False,否则你就会无限期地循环下去?或者,如果len(frames) < 8呢?那么你似乎也会以一个空的输出文件而告终。

我的建议是进行上述修改并再次尝试。

我更仔细地看了一下代码,似乎它是在循环读取输入(before2.mp4)一次一帧,当它积累了8帧或更多时,它就创建一个池子,处理它所积累的帧,并将它们写到输出 (后.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()