You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

批量处理视频目录触发除零错误,替换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)

错误原因

  1. 路径不完整:os.listdir仅返回文件名,未拼接目录路径,导致cv2.VideoCapture无法正确打开视频文件,此时cap.get(cv2.CAP_PROP_FPS)返回0,触发除零错误。
  2. 未过滤非视频文件:目录下可能存在非视频格式文件(如缓存文件、文本文件),打开这类文件时FPS为0,引发除零。
  3. 代码缩进错误:批量处理的循环未正确嵌套在if __name__ == "__main__":块内,且a=main(filename)无缩进,存在语法错误。
  4. 缺少异常校验:未检查视频是否成功打开,也未校验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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:43:09