基于彩色直方图自适应检测电影镜头切换

实验目标

利用颜色直方图检测电影片段007.mp4, Godfather.mp4的镜头。
输入为视频文件名;输出为不同镜头的首尾帧;镜头按序号命名:001-1.jpg, 001-2.jpg, 002-1.jpg, 002-2.jpg, … 每一个镜头输出两个图像,对应与该镜头的首帧和尾帧(分别用-1,-2后缀表示)。

问题分析

本实验采用颜色直方图来检测电影片段中的镜头的切换,颜色直方图法首先将颜色空间分成为离散的颜色区间,计算每帧彩色图像中落入每个小区间的像素数目,得到其颜色直方图,然后计算两帧图像直方图的差值得到帧间差,直方图的优点是它对镜头的运动和图像内物体的运动不敏感,进而可以降低由此而引起的虚检测。
通过分析电影样本,可以发现其中电影007中,存在较多的爆炸,枪击,冒烟等画面,其中比较视频图像帧前后的差值较大,对于镜头切换检测有一定程度的影响。
同时考虑到人眼存在一个0.2秒的反应时间,电影镜头切换的频率存在上限值,即至少要间隔0.2*25=5帧图像,才有可能出现镜头切换。因此考虑到此约束条件,可以解决一部分的镜头切换检测中的遇到画面剧烈变化造成的误检测问题。
同时,查阅相关资料,发现在对直方图比较函数中,存在5中不同的比较方法,因此实验采用其中两种计算量较小,效果较好的两种比较方法进行比较。
由于,通过视频帧的彩色直方图之间的差异,来进行镜头切换的检测。因此,确定检测阈值,成为重要的一步,其中考虑到,算法模型的自适应性,尝试通过某种算法,可以自动生成适应于本电影的阈值。为此,本实验对阈值的自动生成也做了一些尝试。

方法流程设计

通过分析电影样本,首先通过截取视频的帧,得到视频的帧序列,然后计算其彩色直方图,起初,没有考虑到视频中存在许多诸如,爆炸,冒烟,枪击,等因素,直接利用彩色直方图,通过比较前一帧与后一帧的相关系数,来判断镜头的切换,通过实验的结果发现效果并不好,有很多漏检的情况,还有一些由于镜头画面发生较大变化导致的误检,如图所示的
爆炸前
爆炸后
如图所示,由于镜头画面发生了诸如,爆炸,射击等事件,使得相应的镜头检测出现误检测的情况。因此考虑到在计算图像彩色直方图前,需要进行预处理,将其中有相关的活动的因素去除。考虑到通过判断特定的像素值(比如爆炸会出现很多强光,烟雾是白色的),来过滤掉有关的像素值,进而达到其对彩色直方图的分布不产生影响。发现cv2中的calcHist()函数可以设置每个通道计算的亮度范围,由于默认的亮度范围是[0,256]。因此将通过采集上述事件主要的亮度范围,通过设置新的范围,来抑制干扰因素对直方图分布的影响。因此需要采集干扰因素的像素值,通过编写简单的程序来输出鼠标选定像素值的BGR值。(具体源代码见源文件)通过分析相关的像素值的分布,可以确定出较理想的亮度范围。其中对下图采集的采集点和相应BGR值如下表所示:

BGR
255255251
251255250
254255246

由上图可以分析出,对于干扰的因素,通过设定亮度范围来抑制,因此本实验采用ranges = [0,160,0,250,0,250],作为calcHist函数的参数。

自适应阈值计算方法

通过对实验数据的分析,想尝试一种方法可以自动生成阈值,来达到模型算法的泛化能力,以及提高对样本的自适应性。考虑到本问题类似于二分类,因此需要确定阈值来达到二分类的目的。由数据样本的分布,可以发现,其绝大多数视频帧之间的相关性都较高。如图2.3所示:
在这里插入图片描述

因此数据相关性集中分布在1.0。其中将非常接近于1.0的数据称为近似区间,分布区间就是相关系数在[min,1.0],其中min是相关系数中的最小值,则此样本的所有相关系数都在分布区间中,因此需要选取合适的阈值x,使得可以较好地进行分类。通过统计在近似区间帧的数量,计算出其占比rate,由于rate越大,阈值x也越大,同时默认分布区间的中心(min+1.0)/2,因此考虑到可以将分布区间的中心和1.0进行加权求和,来近似阈值x即
X = (1-rate)(1.0+min)/2+rate1.0**
通过样本数据,可以看出镜头不发生切换时,视频帧之间的近似值大约为0.001.
同时,实验采用两种不同的方法来比较彩色直方图的,一种是相关系数,另外一种是交集法,通过比较两种方法的结果,选出一个较好的比较方式。

效果展示

在这里插入图片描述

总结

通过本实验由007电影的镜头检测结果可以得出,相比较之下交集法的效果更好一点。但是对于画面变化剧烈的或者,仍存在误检测的情况。一方面通过设定镜头切换频率的最大值来约束对剧烈变化场景的检测,另一方面通过过滤掉特定区间亮度的像素值,对于视频中的剧烈变化的检测情况有所改善,同时对于阈值的设定方法提升了算法的泛化性,在电影Godfather中体现出较好的检测结果。

源代码

import os
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

#提取视频中的帧,保存在列表中
frame_list = []
cap = cv.VideoCapture('Data/Godfather.mp4')
fps = cap.get(5)
delay = int(1000/fps) - 5
while(cap.isOpened()):
    ret, frame = cap.read()
    #print(ret)
    if ret == False:
        break
    frame_list.append(frame)
    #print(frame)
    cv.imshow('frame',frame)
    if cv.waitKey(delay) & 0xFF == ord('q'):
        break
cap.release()
cv.destroyAllWindows()
print(len(frame_list))
#计算直方图函数
def Computer_Histograms(frame):
    hist = cv.calcHist(images = [frame],channels = [0,1,2],mask = None,histSize = [8,8,8],
                      ranges = [0,256,0,256,0,256])
    hist = hist.flatten()
    hist = hist / hist.sum()
    return hist

#计算每帧图片的直方图
hist_list = []
for each in frame_list:
    hist = Computer_Histograms(each)
    hist_list.append(hist)
#基于相关系数比较直方图
correl_list = []
for i in range(len(hist_list)-1):
    correl = cv.compareHist(hist_list[i], hist_list[i+1], cv.HISTCMP_CORREL)
    correl_list.append(correl)
print(len(correl_list))

#基于相关系数的比较直方图相似度图像
fig,ax = plt.subplots()
ax.plot(correl_list)
plt.show()

#基于相似度的阈值设定
print(min(correl_list))
# print(correl_list)
num = 0
for each in correl_list:
    if 1.0 - each < 0.001:
        num += 1
rate = num/len(correl_list)
print(rate)
value = (1+min(correl_list))*0.5*(1-rate)+rate
print(value)

#对视频图像帧进行镜头检测
lens_index = []

pre_index = 0
#需要小于阈值且间隔大于5
for index in range(len(correl_list)):
    if correl_list[index] < value and index >= pre_index+5:
        lens_index.append(index)
        pre_index = index
#检测出的镜头切换的数量
print(len(lens_index))

#将镜头切换的前一帧和后一帧保存
for i in range(len(lens_index)-1):
    cv.imwrite(str(i)+"-1.jpg",frame_list[lens_index[i]])
    cv.imwrite(str(i)+"-2.jpg",frame_list[lens_index[i+1]])


#对视频图像帧进行镜头检测
lens_index = []

pre_index = 0
#需要小于阈值且间隔大于5
for index in range(len(intersect)):
    if intersect[index] < value and index >= pre_index+5:
        lens_index.append(index)
        pre_index = index
#检测出的镜头切换的数量
print(len(lens_index))

#将镜头切换的前一帧和后一帧保存
for i in range(len(lens_index)-1):
    cv.imwrite(str(i)+"-1.jpg",frame_list[lens_index[i]])
    cv.imwrite(str(i)+"-2.jpg",frame_list[lens_index[i+1]])
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值