OpenCV提取视频帧至2.5万张时触发imwrite断言失败报错求解
视频帧提取报错排查
问题表现
待处理视频总帧数约9万帧,帧提取程序运行至成功提取25000帧时抛出运行错误,中断执行。
原实现代码
import cv2 import time import os def video_to_frames(input_loc, output_loc): """Function to extract frames from input video file and save them as separate frames in an output directory. Args: input_loc: Input video file. output_loc: Output directory to save the frames. Returns: None """ try: os.mkdir(output_loc) except OSError: pass # Log the time time_start = time.time() # Start capturing the feed cap = cv2.VideoCapture(input_loc) # Find the number of frames video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) - 1 print ("Number of frames: ", video_length) count = 0 print ("Converting video..n") # Start converting the video while cap.isOpened(): # Extract the frame ret, frame = cap.read() # Write the results back to output location. cv2.imwrite(output_loc + "/%#05d.jpg" % (count+1), frame) count = count + 1 # If there are no more frames left if (count > (video_length-1)): # Log the time again time_end = time.time() # Release the feed cap.release() # Print stats print ("Done extracting frames.n%d frames extracted" % count) print ("It took %d seconds forconversion." % (time_end-time_start)) break if __name__=="__main__": input_loc = 'E:/Fish Project DataSet/06-30/D01_20210630002157.mp4' output_loc = 'E:/Fish Project DataSet/06-30 pictures/D01_20210630002157 frames/' video_to_frames(input_loc, output_loc)
报错信息
error: OpenCV(4.5.5) D:\a\opencv-python\opencv-python\opencv\modules\imgcodecs\src\loadsave.cpp:801: error: (-215:Assertion failed) !_img.empty() in function 'cv::imwrite'
根因分析
- 报错直接触发点:
cv2.imwrite接口接收到的待写入图像为空矩阵,触发内置断言校验失败。OpenCV的图像写入接口要求传入的参数必须是非空的有效图像矩阵,传入空值时会直接抛出该错误。 - 代码逻辑漏洞:原代码调用
cap.read()读取帧后,未校验读取返回状态就直接执行写入操作。cap.read()会返回两个值:第一个是布尔类型的状态标记ret,只有ret=True时代表成功解码得到有效帧;当视频存在损坏帧、解码失败、读取到视频末尾时,ret会返回False,此时第二个返回值frame为空,直接传入写入接口就会触发崩溃。本次程序跑到25000帧时报错,就是该位置存在损坏/无法解码的帧,读取返回空值导致的。 - 额外逻辑隐患:原代码完全依赖
cv2.CAP_PROP_FRAME_COUNT获取的总帧数判断读取终止条件,部分编码格式的视频该属性返回值存在计数偏差,容易导致逻辑判断失效;另外代码中控制台输出的换行符书写错误,将\n写为n,会导致输出文本格式错乱;硬编码拼接路径的写法在不同操作系统下存在兼容性问题。
修复方案
修复逻辑覆盖以下几点:
- 每次读取帧后优先校验
ret状态,读取失败时直接终止循环(如果需要跳过损坏帧继续读取,可加计数后直接进入下一轮循环,不执行写入操作) - 修正换行符书写错误,将提示文本中写错的
n替换为正确的转义换行符\n - 路径拼接改用
os.path.join实现,兼容不同操作系统的路径规则 - 不将预获取的总帧数作为唯一终止判断条件,以帧读取的返回状态作为核心终止依据,避免总帧数计数不准导致的异常
- 替换原有的
os.mkdir异常捕获写法,用os.makedirs加exist_ok=True参数自动处理目录已存在的场景,逻辑更简洁
修复后可直接运行的代码如下:
import cv2 import time import os def video_to_frames(input_loc, output_loc): """Function to extract frames from input video file and save them as separate frames in an output directory. Args: input_loc: Input video file. output_loc: Output directory to save the frames. Returns: None """ os.makedirs(output_loc, exist_ok=True) time_start = time.time() cap = cv2.VideoCapture(input_loc) video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) - 1 print("Number of frames: ", video_length) count = 0 print("Converting video...\n") while cap.isOpened(): ret, frame = cap.read() # 读取失败直接终止,避免空帧写入 if not ret: break # 路径拼接改用os.path.join,兼容多系统 save_path = os.path.join(output_loc, f"{count+1:05d}.jpg") cv2.imwrite(save_path, frame) count += 1 # 达到预获取总帧数时提前终止 if count > (video_length -1): break time_end = time.time() cap.release() print(f"Done extracting frames.\n{count} frames extracted") print(f"It took {int(time_end-time_start)} seconds for conversion.") if __name__=="__main__": input_loc = 'E:/Fish Project DataSet/06-30/D01_20210630002157.mp4' output_loc = 'E:/Fish Project DataSet/06-30 pictures/D01_20210630002157 frames/' video_to_frames(input_loc, output_loc)
如果需要跳过视频中间的损坏帧、不中断整体提取流程,可以把
if not ret: break修改为if not ret: count +=1; continue,即可跳过无法解码的帧继续读取后续内容。
内容的提问来源于stack exchange,提问作者Rana Saeed
相关产品推荐
相关产品推荐

