使用 OpenCV 的 cv2.VideoCapture 进行帧计数与在 Python 中手动计数之间的差异

问题描述 投票:0回答:1

我正在Python中使用OpenCV进行视频处理项目,并且遇到了一个与计算视频中的帧数相关的奇怪问题。我正在使用

cv2.VideoCapture
从视频文件中读取帧,但在计算总帧数时得到的结果不一致。

这是我正在使用的代码片段:

import cv2

video_path = 'MSP-IMPROV-S01A-F01-P-MF01.avi'
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
print(f"FPS: {fps}")
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print("Total frames using cv2.VideoCapture:", total_frames)

cap = cv2.VideoCapture(video_path)
frame_count = 0
while cap.isOpened():
    ret, _ = cap.read()
    if not ret:
        break
    frame_count += 1
cap.release()
print("Manually counted frames:", frame_count)

我得到的输出如下:

FPS: 59.94005994005994
Total frames using cv2.VideoCapture: 1187
Manually counted frames: 594

最初,我对帧数的差异感到困惑。然而,在仔细检查输出帧后,我注意到帧被减半,并且 OpenCV 似乎以 1 的步长均匀地跳过帧(即每两帧一帧)。

这可能是 OpenCV 如何读取视频文件的问题吗?以下是一些额外的注意事项:

视频文件格式为AVI。这种格式会影响 OpenCV 读取帧的方式吗? 这是 OpenCV 帧读取机制中的已知行为或限制吗? 我需要调整 cv2.VideoCapture 中的特定设置或参数以避免跳帧吗? 对于如何确保 OpenCV 读取视频的每一帧而不跳过的任何见解或建议,我将不胜感激。

谢谢您的帮助!

python opencv video-processing avi interlacing
1个回答
0
投票

我认为这个答案在任何情况下都可能有用。不过,如果您的视频不是隔行扫描,我会感到惊讶,因为这种清晰的两倍帧数是一个非常大的指标。

我创建了一个函数来测试视频是否隔行扫描,它基于 ffmpeg,所以你必须使用

sudo apt install ffmpeg
来获取它。

之后,我用 python 编写了这个函数,以返回

True
False
,或者在出现问题时返回错误消息:

def testInterlacing(inputVideo):
    import subprocess
    import re
    # construct the ffmpeg command as a list of arguments
    ffmpegCommand = [
        "ffmpeg", # command for ffmpeg, you need to install this with sudo apt instlal ffmpeg
        "-filter:v", "idet", # filter this specific information
        "-frames:v", "100", # test on first 100 frames
        "-an", # don't test audio
        "-f", "rawvideo", "-y", "/dev/null", # assign results to nothing
        "-i", inputVideo # on this video
    ]
    try:
        outputBytes = subprocess.check_output(ffmpegCommand, stderr=subprocess.STDOUT) # get the output
        outputStr = outputBytes.decode('utf-8') # decode the output
        tffLines = re.findall(r'TFF:\s+(\d+)', outputStr) # search for a pattern that is the count of top field first 
        bffLines = re.findall(r'BFF:\s+(\d+)', outputStr) # search for a pattern that is the count of  bottom field first
        totalInterlacedFrames = sum(map(int, tffLines)) + sum(map(int, bffLines)) # get total interlaced frames in the 100 checked
        if totalInterlacedFrames != 0: # check if interlaced
            return True # YES
        else: # if not
            return False # NO
    except subprocess.CalledProcessError as e:
        return e

此后,您可以在主函数中使用此函数来指示计数应如何工作:

import cv2
inputVideoInterlaced = "letterman.mp4" # sample of interlaced video
inputVideoNotInterlaced = "countdown.mp4" # sample of nt interlaced video
inputVideo = inputVideoInterlaced # choose which file

if testInterlacing(inputVideo):
    cap = cv2.VideoCapture(inputVideo)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print(f"FPS: {fps}")
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print("Total frames using cv2.VideoCapture:", total_frames//2) # divide by 2!
    cap = cv2.VideoCapture(inputVideo)
    frame_count = 0
    while cap.isOpened():
        ret, _ = cap.read()
        if not ret:
            break
        frame_count += 1
    cap.release()
    print("Manually counted frames:", frame_count)
    
elif not(testInterlacing(inputVideo)):
    cap = cv2.VideoCapture(inputVideo)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print(f"FPS: {fps}")
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print("Total frames using cv2.VideoCapture:", total_frames)
    cap = cv2.VideoCapture(inputVideo)
    frame_count = 0
    while cap.isOpened():
        ret, _ = cap.read()
        if not ret:
            break
        frame_count += 1
    cap.release()
    print("Manually counted frames:", frame_count)
    
else:
    e = testInterlacing(inputVideo)
    print("Error while checking if video is interlaced:\n"+e)

我将您的代码复制粘贴到答案中,仅在检测到隔行扫描时将 cv2 帧数除以 2 进行小调整。

希望这对您有进一步帮助。

© www.soinside.com 2019 - 2024. All rights reserved.