【问题标题】:Can't write frames to a video with multiprocessing + cv2无法使用多处理 + cv2 将帧写入视频
【发布时间】:2021-07-17 18:03:47
【问题描述】:

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

这是我的代码:

# 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


    【解决方案1】:

    我对@9​​87654321@ 不是很熟悉,但您的代码中似乎有几处需要更正。首先,如果您在 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 帧或更多帧时创建一个池并处理它积累的帧并将它们写入输出(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()
    

    【讨论】:

    • 为了我的启迪,您采纳了哪些建议?
    • Moving if __name__ == '__main__' 解决了它
    • 那么我推断cap.read() 之后cap.isOpened() 不再是True 是否正确?否则,我看不出你为什么不无限循环。
    • 可能。我对多处理和 cv2 不太熟悉。
    • 我再次查看了代码,认为我对它的理解更好,并且认为它的性能和正确性存在问题。我有一个建议的更新(当然,使用风险自负,但供您查看)。
    猜你喜欢
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    • 2011-03-29
    • 2017-07-07
    • 2016-11-07
    • 2015-08-28
    相关资源
    最近更新 更多