C++基于opencv的视频质量检测--遮挡检测

文章目录

    • 0.引言
    • [1. 原始代码分析](#1. 原始代码分析)
      • [1.1 存在的问题](#1.1 存在的问题)
    • [2. 优化方案](#2. 优化方案)
    • [3. 优化后的代码](#3. 优化后的代码)
    • [4. 代码详细解读](#4. 代码详细解读)
      • [4.1. 输入检查](#4.1. 输入检查)
      • [4.2. 图像预处理](#4.2. 图像预处理)
      • [4.3. 高斯模糊](#4.3. 高斯模糊)
      • [4.4. 梯度计算](#4.4. 梯度计算)
      • [4.5. 计算梯度幅值和方向](#4.5. 计算梯度幅值和方向)
      • [4.6. 边缘检测](#4.6. 边缘检测)
      • [4.7. 计算边缘密度](#4.7. 计算边缘密度)
      • [4.8. 估计遮挡程度](#4.8. 估计遮挡程度)
      • [4.9. 限定结果范围](#4.9. 限定结果范围)
      • [4.10. 返回结果](#4.10. 返回结果)

0.引言

视频质量遮挡检测已在C++基于opencv4的视频质量检测中有所介绍,本文将详细介绍其优化版本。

1. 原始代码分析

首先,我们来看遮挡检测的原始代码:

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <vector>

/**
 * @brief 检测图像中的遮挡情况。
 * @param [in] srcImg 输入的图像
 * @return 返回一个double类型的数值,范围为0到1。数值越接近1,表示图像中的遮挡程度越高。
 */
double blockDetect(const cv::Mat& srcImg) {
    if (srcImg.empty()) {
        return -1.0;  // 如果输入图像为空,返回-1表示错误
    }

    cv::Mat grayImg, edges;
    cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(grayImg, grayImg, cv::Size(3, 3), 0);
    cv::Canny(grayImg, edges, 0, 0);

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;

    // 初始轮廓检测
    cv::findContours(edges, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
    int initialContourCount = static_cast<int>(hierarchy.size());

    // 细化后的轮廓检测
    cv::Canny(grayImg, edges, 0, 15);
    cv::findContours(edges, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
    int refinedContourCount = static_cast<int>(hierarchy.size());

    // 防止除以零的情况
    if (initialContourCount == 0) {
        initialContourCount = 1;
    }

    double occlusionLevel = 1.0 - static_cast<double>(refinedContourCount) / static_cast<double>(initialContourCount);
    return occlusionLevel;
}

1.1 存在的问题

  1. Canny边缘检测的阈值设置不合理 :在Canny函数中,阈值设为00,以及015,这可能导致边缘检测结果不可靠。

  2. 错误处理不够明确 :当输入图像为空时,返回-1.0,但这个值可能与正常的遮挡程度值混淆。

2. 优化方案

针对上述问题,我们对代码进行如下优化:

  • 自适应阈值设置:根据图像的特性动态设置Canny边缘检测的阈值,提高边缘检测的可靠性。

  • 提高代码可读性:增加详细的注释,使用更具描述性的变量名,提升代码的可读性和可维护性。

  • 算法改进:使用Sobel算子计算梯度,基于边缘密度来判断遮挡程度,获得更准确的结果。

3. 优化后的代码

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <vector>

/**
 * @brief 检测图像中的遮挡情况。
 * @param srcImg 输入的图像
 * @return 如果成功,返回一个介于0到1之间的double类型值,值越接近1表示遮挡程度越高;
 *         如果输入图像为空,返回-1.0。
 */
double occlusionDetect(const cv::Mat& srcImg) {
    if (srcImg.empty()) {
        // 输入图像为空
        return -1.0;
    }

    // 将图像转换为灰度图
    cv::Mat grayImg;
    if (srcImg.channels() == 3) {
        cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);
    } else {
        grayImg = srcImg.clone();
    }

    // 对灰度图进行高斯模糊,减少噪声
    cv::GaussianBlur(grayImg, grayImg, cv::Size(5, 5), 0);

    // 使用Sobel算子计算梯度
    cv::Mat gradX, gradY;
    cv::Sobel(grayImg, gradX, CV_64F, 1, 0, 3);
    cv::Sobel(grayImg, gradY, CV_64F, 0, 1, 3);

    // 计算梯度幅值和方向
    cv::Mat magnitude, angle;
    cv::cartToPolar(gradX, gradY, magnitude, angle, true);

    // 对梯度幅值进行阈值化,得到边缘图
    double maxVal;
    cv::minMaxLoc(magnitude, nullptr, &maxVal);
    cv::Mat edges;
    cv::threshold(magnitude, edges, 0.1 * maxVal, 255, cv::THRESH_BINARY);

    // 计算边缘密度
    double edgeDensity = cv::countNonZero(edges) / static_cast<double>(edges.total());

    // 根据边缘密度估计遮挡程度(假设遮挡区域边缘密度较低)
    double occlusionLevel = 1.0 - edgeDensity;

    // 将结果限定在0到1之间
    occlusionLevel = std::clamp(occlusionLevel, 0.0, 1.0);

    return occlusionLevel;
}

4. 代码详细解读

是 否 开始 输入图像是否为空 返回 std::nullopt 转换为灰度图 grayImg 高斯模糊处理 计算梯度 gradX 和 gradY 计算梯度幅值和方向 获取最大值 maxVal 阈值化得到 edges 计算边缘密度 edgeDensity 计算遮挡程度 occlusionLevel 限定 occlusionLevel 在0到1之间 返回 occlusionLevel

4.1. 输入检查

cpp 复制代码
if (srcImg.empty()) {
    return std::nullopt;
}
  • 目的:确保输入的图像有效。
  • 说明 :如果输入图像为空,函数返回std::nullopt,明确表示错误状态。

4.2. 图像预处理

cpp 复制代码
cv::Mat grayImg;
if (srcImg.channels() == 3) {
    cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);
} else {
    grayImg = srcImg.clone();
}
  • 目的:将彩色图像转换为灰度图像。
  • 说明:灰度图像降低了计算复杂度,适用于后续的梯度和边缘检测。

4.3. 高斯模糊

cpp 复制代码
cv::GaussianBlur(grayImg, grayImg, cv::Size(5, 5), 0);
  • 目的:平滑图像,减少噪声对梯度计算的影响。
  • 参数解释
    • cv::Size(5, 5):高斯核的大小,可根据需要调整。
    • 0:高斯核在x方向的标准差,设为0表示根据核大小自动计算。

4.4. 梯度计算

cpp 复制代码
cv::Mat gradX, gradY;
cv::Sobel(grayImg, gradX, CV_64F, 1, 0, 3);
cv::Sobel(grayImg, gradY, CV_64F, 0, 1, 3);
  • 目的:计算图像在x和y方向的梯度。
  • 参数解释
    • CV_64F:使用64位浮点型,确保梯度值的精度。
    • 1, 00, 1:指定导数的阶数,分别计算x和y方向的一阶导数。
    • 3:Sobel核的大小。

4.5. 计算梯度幅值和方向

cpp 复制代码
cv::Mat magnitude, angle;
cv::cartToPolar(gradX, gradY, magnitude, angle, true);
  • 目的:将梯度的x和y分量转换为极坐标形式,得到梯度的幅值和方向。
  • 参数解释
    • true:将角度值转换为度数(0-360),否则为弧度。

4.6. 边缘检测

cpp 复制代码
double maxVal;
cv::minMaxLoc(magnitude, nullptr, &maxVal);
cv::Mat edges;
cv::threshold(magnitude, edges, 0.1 * maxVal, 255, cv::THRESH_BINARY);
  • 目的:通过阈值化梯度幅值,提取边缘。
  • 步骤
    • 使用cv::minMaxLoc获取梯度幅值的最大值maxVal
    • 设定阈值为0.1 * maxVal,将高于此阈值的像素设为255(白色),其余设为0(黑色)。

4.7. 计算边缘密度

cpp 复制代码
double edgeDensity = cv::countNonZero(edges) / static_cast<double>(edges.total());
  • 目的:计算边缘像素占总像素的比例。
  • 说明:边缘密度反映了图像中边缘信息的丰富程度。

4.8. 估计遮挡程度

cpp 复制代码
double occlusionLevel = 1.0 - edgeDensity;
  • 目的:根据边缘密度估计遮挡程度。
  • 假设:遮挡区域的边缘密度较低,因此边缘密度越小,遮挡程度越高。

4.9. 限定结果范围

cpp 复制代码
occlusionLevel = std::clamp(occlusionLevel, 0.0, 1.0);
  • 目的:确保遮挡程度在有效范围内。

4.10. 返回结果

cpp 复制代码
return occlusionLevel;
  • 说明 :返回的occlusionLeveldouble类型,范围在0到1之间。
  • 无遮挡图像occlusionLevel值接近于0,表示遮挡程度低。
  • 遮挡图像occlusionLevel值接近于1,表示遮挡程度高。
相关推荐
神探小白牙2 小时前
海康视频在vue2.0中的使用
前端·音视频
AI天行健2 小时前
文生视频与图生视频的技术区别及适用场景分析
人工智能·音视频
汉克老师2 小时前
CSP-J 初赛(以满分为目标):第二十六课 《图的遍历——DFS深度优先搜索——从“树的先序遍历”走进真正的图世界》
c++·csp-j·小学生·学c++编程
阿童木写作2 小时前
跨境电商批量图片翻译与视频字幕翻译工具推荐
python·音视频
程序猿编码2 小时前
基于GGML的C++17轻量化语音推理引擎:说话人识别与语音分析技术全解析
开发语言·c++·pytorch·深度学习·神经网络·大模型
orient.lu2 小时前
第 21 章《图像生成与音频转录》· nanobot 多模态 Provider 源码解析:11 图像 + 6 转录注册表
音视频·nanobot
leoZ2313 小时前
第 4 篇:布局骨架——页面壳、栅格、卡片
前端·javascript·vue.js·opencv·计算机视觉·数据挖掘·语音识别
影视飓风TIM3 小时前
C++哈希表深度剖析:冲突处理、rehash、迭代器与unordered容器封装
c++·哈希算法·散列表
Mr数据杨3 小时前
【Codex】接入音频转录服务实现课堂语音结构化处理
django·音视频·codex·项目开发
C++ 老炮儿的技术栈3 小时前
Qt5 使用 QPainter 绘制阿基米德螺线
开发语言·c++·windows·qt·代码化