0.引言
视频质量画面冻结检测已在C++基于opencv4的视频质量检测中有所介绍,本文将详细介绍其优化版本。
1. 原始代码分析
图像抖动检测的原始代码:
bool ScreenFreezeDetection(const cv::Mat& srcImg) {
if (srcImg.empty()) {
return true; // 直接返回true表示检测故障
}
cv::Mat backgroundA, backgroundB;
// 1. 获取云台运动前的背景A
static int frameCount = 0;
if (frameCount < NUM_FRAMES) {
if (frameCount == 0) {
backgroundA = cv::Mat::zeros(srcImg.size(), srcImg.type());
}
cv::accumulate(srcImg, backgroundA);
++frameCount;
if (frameCount == NUM_FRAMES) {
backgroundA /= NUM_FRAMES;
}
return false;
}
// 2. 发送云台运动指令,改变场景
// 3. 获取云台运动后的背景B
if (frameCount < 2 * NUM_FRAMES) {
int currentFrameIndex = frameCount - NUM_FRAMES;
if (currentFrameIndex == 0) {
backgroundB = cv::Mat::zeros(srcImg.size(), srcImg.type());
}
cv::accumulate(srcImg, backgroundB);
++frameCount;
if (frameCount == 2 * NUM_FRAMES) {
backgroundB /= NUM_FRAMES;
}
return false;
}
// 4. 计算背景A和背景B的颜色直方图
cv::Mat histA, histB;
int histSize = 256;
float range[] = {0, 256};
const float* histRange = {range};
cv::calcHist(&backgroundA, 1, 0, cv::Mat(), histA, 1, &histSize, &histRange, true, false);
cv::calcHist(&backgroundB, 1, 0, cv::Mat(), histB, 1, &histSize, &histRange, true, false);
// 5. 计算直方图的相似度(使用相关性比较方法)
double histSimilarity = cv::compareHist(histA, histB, cv::HISTCMP_CORREL);
// 6. 判断相似度是否小于阈值,如果小于则认为画面冻结
if (histSimilarity > HIST_SIM_THRESHOLD) {
return true;
} else {
return false;
}
}
存在的问题:
-
依赖外部设备运动:该算法需要通过控制云台运动来改变摄像机的视角,以获取不同的背景。这在实际应用中可能不方便,增加了系统的复杂性和成本。
-
复杂的背景处理:使用了帧累积和直方图比较的方法,计算量较大,效率较低。
-
静态场景误判:对于本身就没有明显变化的静态场景,可能误判为画面冻结。
2. 优化方案
-
取消对云台运动的依赖:改为直接比较连续帧之间的相似度,避免对外部设备的依赖。
-
使用结构相似度(SSIM):SSIM是一种衡量两幅图像相似度的指标,考虑了亮度、对比度和结构信息,比简单的直方图比较更准确。
-
引入冻结帧计数:只有当连续多帧都满足冻结条件时,才认为画面冻结,减少了误报率。
-
简化代码结构:移除了累积帧和直方图计算的复杂操作。
3. 优化后的代码
#include <opencv2/opencv.hpp>
#include <opencv2/quality.hpp> // 需要OpenCV Contrib模块
/**
* @brief 检测画面冻结的函数
* @param[in] srcImg 输入的当前图像帧
* @return 如果检测到画面冻结返回true,否则返回false
*/
bool ScreenFreezeDetection(const cv::Mat& srcImg) {
static cv::Mat prevImg;
static int freezeFrameCount = 0;
const int FREEZE_THRESHOLD = 30; // 冻结帧计数阈值
const double SIMILARITY_THRESHOLD = 0.99; // 相似度阈值
if (srcImg.empty()) {
return true; // 输入图像为空,认为画面冻结
}
if (prevImg.empty()) {
prevImg = srcImg.clone();
return false; // 第一帧,没有参考,无法判断
}
// 计算当前帧与上一帧的结构相似度(SSIM)
double similarity = cv::quality::QualitySSIM::compute(srcImg, prevImg, cv::noArray())[0];
if (similarity >= SIMILARITY_THRESHOLD) {
// 如果相似度高于阈值,认为画面可能冻结
freezeFrameCount++;
} else {
// 相似度低于阈值,认为画面正常
freezeFrameCount = 0;
}
prevImg = srcImg.clone();
// 如果连续的冻结帧数量超过阈值,认为画面冻结
if (freezeFrameCount >= FREEZE_THRESHOLD) {
return true;
} else {
return false;
}
}
4. 代码详细解读
流程说明:
-
开始:函数
ScreenFreezeDetection
开始执行。 -
检查输入图像是否为空:如果为空,返回
true
,认为画面冻结。 -
检查
prevImg
是否为空:如果是第一帧,初始化prevImg
,返回false
。 -
计算SSIM相似度:使用当前帧和
prevImg
计算SSIM相似度。 -
判断相似度是否超过阈值:如果相似度高,认为画面可能冻结,冻结帧计数器加1;否则,重置计数器。
-
更新
prevImg
:将当前帧保存为prevImg
,供下一次计算使用。 -
检查冻结帧计数器是否超过阈值:如果超过,返回
true
,认为画面冻结;否则,返回false
。
代码说明:
-
使用静态变量保存上一帧图像和冻结帧计数器:
prevImg
保存上一帧,freezeFrameCount
统计连续冻结帧的数量。 -
计算SSIM相似度:使用
cv::quality::QualitySSIM::compute
函数计算当前帧与上一帧的SSIM相似度。 -
判断画面是否冻结:如果相似度超过阈值
SIMILARITY_THRESHOLD
,则增加冻结帧计数;否则,重置计数。 -
返回检测结果:当冻结帧计数超过阈值
FREEZE_THRESHOLD
,认为画面冻结。