批量处理视频目录触发除零错误,替换fmod后遇数学域错误求修复
批量处理视频时浮点数除零错误的修复方案
问题描述
我在批量处理目录下所有视频时触发了「Float division by zero(浮点数除零)」错误,但单独处理单个视频文件时无此问题。尝试用fmod函数替代除法运算符/后,又出现数学域错误,求修复方法。
原始代码
依赖安装命令
! pip install python-opencv moviepy
Python代码
from google.colab import drive root = '/content/gdrive/' drive.mount( root ) from datetime import timedelta import cv2 import numpy as np import os # i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total SAVING_FRAMES_PER_SECOND = 10 def format_timedelta(td): """Utility function to format timedelta objects in a cool way (e.g 00:00:20.05) omitting microseconds and retaining milliseconds""" result = str(td) try: result, ms = result.split(".") except ValueError: return (result + ".00").replace(":", "-") ms = int(ms) ms = round(ms / 1e4) return f"{result}.{ms:02}".replace(":", "-") def get_saving_frames_durations(cap, saving_fps): """A function that returns the list of durations where to save the frames""" s = [] # get the clip duration by dividing number of frames by the number of frames per second clip_duration = cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS) # use np.arange() to make floating-point steps for i in np.arange(0, clip_duration, 1 / saving_fps): s.append(i) return s def main(video_file): filename, _ = os.path.splitext(video_file) filename += "-opencv" # make a folder by the name of the video file if not os.path.isdir(filename): os.mkdir(filename) # read the video file cap = cv2.VideoCapture(video_file) # get the FPS of the video fps = cap.get(cv2.CAP_PROP_FPS) # if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum) saving_frames_per_second = min(fps, SAVING_FRAMES_PER_SECOND) # get the list of duration spots to save saving_frames_durations = get_saving_frames_durations(cap, saving_frames_per_second) #start the loop count = 0 while True: is_read, frame = cap.read() if not is_read: # break out of the loop if there are no frames to read break # get the duration by dividing the frame count by the FPS frame_duration = count / fps try: # get the earliest duration to save closest_duration = saving_frames_durations[0] except IndexError: # the list is empty, all duration frames were saved break if frame_duration >= closest_duration: # if closest duration is less than or equals the frame duration, # then save the frame frame_duration_formatted = format_timedelta(timedelta(seconds=frame_duration)) cv2.imwrite(os.path.join(filename, f"frame{frame_duration_formatted}.jpg"), frame) # drop the duration spot from the list, since this duration spot is already saved try: saving_frames_durations.pop(0) except IndexError: pass # increment the frame count count += 1 import os for filename in os.listdir('/content/gdrive/MyDrive/x/train_videos/Red'): if __name__ == "__main__": a=main(filename)
错误原因
- 路径不完整:
os.listdir仅返回文件名,未拼接目录路径,导致cv2.VideoCapture无法正确打开视频文件,此时cap.get(cv2.CAP_PROP_FPS)返回0,触发除零错误。 - 未过滤非视频文件:目录下可能存在非视频格式文件(如缓存文件、文本文件),打开这类文件时FPS为0,引发除零。
- 代码缩进错误:批量处理的循环未正确嵌套在
if __name__ == "__main__":块内,且a=main(filename)无缩进,存在语法错误。 - 缺少异常校验:未检查视频是否成功打开,也未校验FPS是否为有效数值(非零),直接进行除法运算。
修复方案及修改后代码
针对上述问题,修改后的代码如下:
from google.colab import drive root = '/content/gdrive/' drive.mount(root) from datetime import timedelta import cv2 import numpy as np import os # 每秒保存的帧数 SAVING_FRAMES_PER_SECOND = 10 # 支持的视频后缀,可根据需要补充 SUPPORTED_VIDEO_EXTENSIONS = ('.mp4', '.avi', '.mov', '.mkv') def format_timedelta(td): """格式化timedelta,保留两位毫秒""" result = str(td) try: result, ms = result.split(".") except ValueError: return (result + ".00").replace(":", "-") ms = int(ms) ms = round(ms / 1e4) return f"{result}.{ms:02}".replace(":", "-") def get_saving_frames_durations(cap, saving_fps): """计算需要保存帧的时间点列表""" s = [] frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT) fps = cap.get(cv2.CAP_PROP_FPS) # 校验帧数量和FPS是否有效,避免除零 if frame_count <= 0 or fps <= 0: return s clip_duration = frame_count / fps # 生成时间点,步长为1/saving_fps for i in np.arange(0, clip_duration, 1 / saving_fps): s.append(i) return s def main(video_file): # 提取文件名(不含路径)用于创建保存目录 base_filename = os.path.basename(video_file) filename, _ = os.path.splitext(base_filename) save_dir = filename + "-opencv" # 创建保存目录 if not os.path.isdir(save_dir): os.mkdir(save_dir) # 打开视频文件并校验是否成功 cap = cv2.VideoCapture(video_file) if not cap.isOpened(): print(f"无法打开视频文件: {video_file}") return fps = cap.get(cv2.CAP_PROP_FPS) # 校验FPS是否有效 if fps <= 0: print(f"视频文件{video_file}的FPS无效,跳过处理") cap.release() return # 确定实际保存帧率(不超过视频本身FPS) saving_frames_per_second = min(fps, SAVING_FRAMES_PER_SECOND) saving_frames_durations = get_saving_frames_durations(cap, saving_frames_per_second) count = 0 while True: is_read, frame = cap.read() if not is_read: break frame_duration = count / fps try: closest_duration = saving_frames_durations[0] except IndexError: break if frame_duration >= closest_duration: frame_duration_formatted = format_timedelta(timedelta(seconds=frame_duration)) save_path = os.path.join(save_dir, f"frame{frame_duration_formatted}.jpg") cv2.imwrite(save_path, frame) try: saving_frames_durations.pop(0) except IndexError: pass count += 1 # 释放视频资源 cap.release() if __name__ == "__main__": video_dir = '/content/gdrive/MyDrive/x/train_videos/Red' # 遍历目录下所有文件 for filename in os.listdir(video_dir): # 过滤非视频文件 if filename.lower().endswith(SUPPORTED_VIDEO_EXTENSIONS): # 拼接完整文件路径 full_video_path = os.path.join(video_dir, filename) main(full_video_path)
关键修复点
- 完整路径拼接:将目录路径与文件名拼接,确保
cv2.VideoCapture能正确找到视频文件。 - 过滤非视频文件:通过后缀名筛选视频文件,避免处理无效文件。
- 增加资源校验:检查视频是否成功打开、FPS是否有效,提前跳过无效文件,避免除零错误。
- 修复代码结构:将批量处理逻辑正确嵌套在
if __name__ == "__main__":块内,修正缩进错误。 - 资源释放:处理完视频后调用
cap.release()释放资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者fatemeh khoshhal
相关产品推荐
相关产品推荐

