小白交流,大咖绕行!主要代码源于网络,个人添加了一些(画蛇添足)的东西。
需要解决的问题:
1、检测一个视频是否含有黑场(黑帧);
2、检测黑场的参数要根据要求,可以调整;
3、在追求的准确度的同时,能耗时越少越好;
解决方案:
通过搜索,基本得到两种方案(代码见后面):
1、python + opencv (只需要安装以及配置python,以及opencv库)
import time
def frames_to_timecode(framerate, frames):
"""
视频 通过视频帧转换成时间
:param framerate: 视频帧率
:param frames: 当前视频帧数
:return:时间(00:00:01:01)
"""
return '{0:02d}:{1:02d}:{2:02d}:{3:02d}'.format(int(frames / (3600 * framerate)),
int(frames / (60 * framerate) % 60),
int(frames / framerate % 60),
int(frames % framerate))
def video_black_test(videodir):
import cv2
# 把图片转换为单通道的灰度图
video_for_test = cv2.VideoCapture(videodir)
fps = 0
blackfps = 0
while (video_for_test.isOpened()):
retval, image = video_for_test.read()
if retval == True:
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 获取灰度图矩阵的行数和列数
r, c = gray_img.shape[:2]
piexs_sum = r * c # 整个灰度图的像素个数为r*c
# 获取偏暗的像素(表示0~19的灰度值为暗) 此处阈值可以修改
dark_points = (gray_img < 20)
target_array = gray_img[dark_points]
dark_sum = target_array.size
# 判断灰度值为暗的百分比
dark_prop = dark_sum / (piexs_sum)
if dark_prop >= 0.85:
print("有黑场,在:", frames_to_timecode(25, fps))
blackfps = blackfps + 1
else:
video_for_test.release()
cv2.destroyAllWindows()
break
startt = time.time() # --计算程序运行时间-开始
videodir = r"I:\Desktop\C00591.mp4"
video_black_test(videodir)
end = time.time() # --计算程序运行时间-结束
runt = end - startt
print(runt, '秒') # --打印程序运行耗时
2、python + ffmpeg (需要安装配置python 和 ffmpeg)
import os
import subprocess
import time
import datetime
def black_detect(video):
dt = datetime.datetime.now()
logname = str(dt)[:4] + str(dt)[5:7] + str(dt)[8:10] + str(dt)[11:13] + str(dt)[14:16] + str(dt)[17:19]
report_file_path = "I\:\\blackvideo-{}.log".format(logname)
report_file_path_convert = report_file_path[0] + report_file_path[2:] # ------------路径转换
report_file_name = {"FFREPORT": "file={}:level=32".format(report_file_path)} # ------------环境变量设置
cmd = r'ffmpeg -report -v quiet -i {} -vf blackdetect=d=0.04:pix_th=0.05:pic_th=0.80 -f null - '.format(video)
print(cmd)
try:
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=False, env=report_file_name)
popen.wait()
# stdout, stderr = popen.stderr
time.sleep(0.5)
except Exception as e:
print(e)
if os.path.exists(report_file_path_convert): # --判断是否生成日志文件
with open(report_file_path_convert, 'r') as rf:
content = rf.readlines()
black_duration = {}
part = 1
for con in content: # --循环查找并记录日志内的黑场内容
if 'black_start' in con:
print(con)
black_info = con.split(' ')[-1:-4:-1]
part_black_info = dict(map(lambda x: x.replace('n', '').split(':'), black_info))
black_duration[part] = part_black_info
part += 1
print(black_duration)
else:
print('未检测到黑场...')
startt = time.time()
videodir = "I:\Desktop\C00591.mp4"
black_detect(videodir)
end = time.time()
runt = end - startt
print(runt, '秒') # --打印程序运行耗时
方案分析:
1、两种方案都能实现需求中的(1)和(2)
2、对同一个视频(视频:长度11分20秒,1080P,25FPS)进行检测,方案(opencv)大约耗时81秒,方案(ffmpeg)大约耗时13秒。差距有点大,不知道是不是代码本身的问题,有知道的大神可以指点一下,谢谢!
3、opencv 主要是检测每一帧的灰度图, ffmpeg则是内部滤镜blackdetect实现
4、两个比较需要注意的地方:
(1)opencv的代码比较简单易懂,略过。ffmpeg的则略微麻烦,在网络查询到的代码,均是FFREPORT=file=report_name.log:level=32 ffmpeg -report -v quiet -i video.mp4 -vf blackdetect=d=0.5:pix_th=0.40:pic_th=0.85 -f null -
但我在pthon运行时没有任何反应,在CMD中运行,提示FFREPORT不是有效的命令。可能是我的ffmpeg的环境变量设置补恰当所致。庆幸的是Popen提供环境变量env的设置,解决了FFREPORT的问题。
(2)在设置FFREPORT时,我需要将日志文件转移到一个绝对地址上去(个人习惯),但是以下两种格式都无法正常执行:
r"I:\blackvideo-{}.log".format(logname)
"I:\\blackvideo-{}.log".format(logname)
在外网看见有人写成下面的样式,而只有这种格式,才能正常生成日志(有知道的大神,能不能科普一下为什么吗,谢谢谢谢!):
"I\:\\blackvideo-{}.log".format(logname)
参考:
FFMPEG 中的黑场检测_blackframe_普通网友的博客-CSDN博客