使用multiprocessing与cv2时遇pickle错误及多进程实现咨询
问题分析与修复方案
错误原因
你遇到的TypeError: cannot pickle 'cv2.VideoCapture' object,本质是Python多进程启动时会序列化(pickle)传递给子进程的参数,但cv2.VideoCapture对象不支持序列化,无法跨进程传递。
代码中的其他问题
除了序列化错误,你的代码还有几处需要修正的地方:
- 创建
filtered_process时,args参数格式错误,必须传入元组,否则会导致函数参数不匹配 - 子进程中的
cv2.waitKey(1)无效,因为子进程没有OpenCV的GUI上下文 - 灰度图绘制用了
plt.plot(画曲线),而非plt.imshow(显示图像),导致保存结果错误 - 锁的使用不够安全,未考虑异常场景下的释放问题
saveFramesFiltered中currentFrame递增时机错误,可能导致重复处理或跳过帧
修复后的完整代码
import cv2 import os import time import matplotlib.image as pltim from multiprocessing import Process, Lock import matplotlib.pyplot as plt def saveFramesUnfiltered(lock): # 子进程内部创建VideoCapture,避免跨进程传递不可序列化对象 vid = cv2.VideoCapture("maxwell cat.mp4") currentFrame = 0 while True: success, frame = vid.read() if not success: # 视频读取完毕自动退出循环 break # 使用with上下文管理器自动管理锁,避免异常导致锁无法释放 with lock: cv2.imwrite(f"./framesBefore/frame{currentFrame}.jpg", frame) currentFrame += 1 time.sleep(0.01) vid.release() def saveFramesFiltered(lock): currentFrame = 0 # 从视频获取总帧数,替代硬编码的215 vid = cv2.VideoCapture("maxwell cat.mp4") framemax = int(vid.get(cv2.CAP_PROP_FRAME_COUNT)) vid.release() while currentFrame < framemax: frame_path = f"./framesBefore/frame{currentFrame}.jpg" if os.path.exists(frame_path): with lock: image = pltim.imread(frame_path) # 计算灰度图 r, g, b = image[:, :, 0], image[:, :, 1], image[:, :, 2] grayImage = 0.299 * r + 0.587 * g + 0.114 * b # 正确绘制灰度图像 plt.imshow(grayImage, cmap="gray") plt.axis("off") with lock: plt.savefig(f"./framesAfter/grayImage{currentFrame}.jpg", bbox_inches='tight', pad_inches=0) plt.clf() # 清除画布,避免后续帧重叠 time.sleep(0.01) currentFrame += 1 # 处理完成后再递增帧序号 else: # 帧未生成时短暂等待重试 time.sleep(0.005) def main(): # 简化目录创建逻辑 os.makedirs('framesBefore', exist_ok=True) os.makedirs('framesAfter', exist_ok=True) lock = Lock() # 修正args参数为元组格式 unfiltered_process = Process(target=saveFramesUnfiltered, args=(lock,)) filtered_process = Process(target=saveFramesFiltered, args=(lock,)) unfiltered_process.start() filtered_process.start() unfiltered_process.join() filtered_process.join() cv2.destroyAllWindows() if __name__ == '__main__': main()
关于多进程实现思路的说明
你的核心思路(一个进程提取原始帧,一个进程处理帧)是合理的,能利用多进程并行提升效率。但需要注意:
- 多进程间不能传递不可序列化的对象(如OpenCV的VideoCapture、窗口句柄等)
- 进程间同步可以用文件存在性判断,也可以用
multiprocessing.Queue直接传递帧数据,效率更高 - 硬编码的帧数量建议改为从视频中动态获取,提升代码通用性
内容的提问来源于stack exchange,提问作者MR. potato35
相关产品推荐
相关产品推荐

