我正在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 读取视频的每一帧而不跳过的任何见解或建议,我将不胜感激。
谢谢您的帮助!
我认为这个答案在任何情况下都可能有用。不过,如果您的视频不是隔行扫描,我会感到惊讶,因为这种清晰的两倍帧数是一个非常大的指标。
我创建了一个函数来测试视频是否隔行扫描,它基于 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 进行小调整。
希望这对您有进一步帮助。