C++基于opencv的视频质量检测--图像清晰度检测

发布于:2024-11-02 ⋅ 阅读:(67) ⋅ 点赞:(0)

0.引言

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

1. 原始算法实现

原始代码:

double sharpnessDetect(const cv::Mat& srcImg) {
  const int kGaussianSize = 3;  // 高斯模糊的核大小

  cv::Mat grayImg;
  if (srcImg.channels() != 1) {
    cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);
  } else {
    grayImg = srcImg;
  }

  cv::Mat blurredImg;
  cv::GaussianBlur(grayImg, blurredImg, cv::Size(kGaussianSize, kGaussianSize), 0);

  uint64_t sumFver = 0;
  uint64_t sumFhor = 0;
  uint64_t sumVver = 0;
  uint64_t sumVhor = 0;
  double blurFactor = 0.0;

  for (int r = 0; r < grayImg.rows; ++r) {
    for (int c = 0; c < grayImg.cols; ++c) {
      uint64_t diffFver = 0;
      uint64_t diffFhor = 0;
      uint64_t diffBver = 0;
      uint64_t diffBhor = 0;
      if (r != 0) {
        diffFver = static_cast<uint64_t>(std::abs(grayImg.at<uchar>(r, c) - grayImg.at<uchar>(r - 1, c)));
      }
      if (c != 0) {
        diffFhor = static_cast<uint64_t>(std::abs(grayImg.at<uchar>(r, c) - grayImg.at<uchar>(r, c - 1)));
      }
      if (r != 0) {
        diffBver = static_cast<uint64_t>(std::abs(blurredImg.at<uchar>(r, c) - blurredImg.at<uchar>(r - 1, c)));
      }
      if (c != 0) {
        diffBhor = static_cast<uint64_t>(std::abs(blurredImg.at<uchar>(r, c) - blurredImg.at<uchar>(r, c - 1)));
      }

      uint64_t verDiff = (diffFver > diffBver) ? (diffFver - diffBver) : 0;
      uint64_t horDiff = (diffFhor > diffBhor) ? (diffFhor - diffBhor) : 0;

      sumFver += diffFver;
      sumFhor += diffFhor;
      sumVver += verDiff;
      sumVhor += horDiff;
    }
  }

  double bFver = (static_cast<double>(sumFver - sumVver)) / (static_cast<double>(sumFver) + 1.0);
  double bFhor = (static_cast<double>(sumFhor - sumVhor)) / (static_cast<double>(sumFhor) + 1.0);
  blurFactor = (bFver > bFhor) ? bFver : bFhor;

  return 1.0 - blurFactor;
}

以下是原始代码的主要步骤:

  1. 图像预处理:将输入图像转换为灰度图。
  2. 高斯模糊:对灰度图像进行高斯模糊处理,得到模糊图像。
  3. 梯度计算:通过遍历每个像素,计算原始图像和模糊图像在垂直和水平方向上的梯度差异。
  4. 模糊因子计算:根据梯度差异,计算模糊因子,进而评估图像的清晰度。

存在的问题:

  • 效率低下:使用嵌套的for循环遍历每个像素,手动计算梯度,处理大尺寸图像时效率较低。

2. 优化思路

为了解决上述问题,我们对原始算法进行了以下优化:

  1. 向量化操作,避免显式循环:利用OpenCV的矩阵运算和函数,对整个图像进行批量处理,提高计算效率。
  2. 使用Sobel算子计算梯度:Sobel算子是常用的梯度计算方法,能够有效地提取图像的边缘信息。

3. 优化后的代码

以下是优化后的sharpnessDetect函数:

double sharpnessDetect(const cv::Mat& srcImg) {
    const int kGaussianSize = 3;  // 高斯模糊的核大小

    cv::Mat grayImg;
    if (srcImg.channels() != 1) {
        cv::cvtColor(srcImg, grayImg, cv::COLOR_BGR2GRAY);
    } else {
        grayImg = srcImg;
    }

    // 对灰度图像进行高斯模糊
    cv::Mat blurredImg;
    cv::GaussianBlur(grayImg, blurredImg, cv::Size(kGaussianSize, kGaussianSize), 0);

    // 计算原始图像和模糊图像的梯度
    cv::Mat gradXOrig, gradYOrig;
    cv::Mat gradXBlur, gradYBlur;

    cv::Sobel(grayImg, gradXOrig, CV_64F, 1, 0, 3);
    cv::Sobel(grayImg, gradYOrig, CV_64F, 0, 1, 3);

    cv::Sobel(blurredImg, gradXBlur, CV_64F, 1, 0, 3);
    cv::Sobel(blurredImg, gradYBlur, CV_64F, 0, 1, 3);

    // 计算梯度的绝对值
    cv::Mat absGradXOrig = cv::abs(gradXOrig);
    cv::Mat absGradYOrig = cv::abs(gradYOrig);

    cv::Mat absGradXBlur = cv::abs(gradXBlur);
    cv::Mat absGradYBlur = cv::abs(gradYBlur);

    // 计算梯度差的正值部分
    cv::Mat diffX = absGradXOrig - absGradXBlur;
    cv::Mat diffY = absGradYOrig - absGradYBlur;

    diffX = cv::max(diffX, 0);
    diffY = cv::max(diffY, 0);

    // 计算梯度的总和
    double sumFhor = cv::sum(absGradXOrig)[0];
    double sumFver = cv::sum(absGradYOrig)[0];

    // 计算梯度差的总和
    double sumVhor = cv::sum(diffX)[0];
    double sumVver = cv::sum(diffY)[0];

    // 计算模糊因子
    double bFver = (sumFver - sumVver) / (sumFver + 1e-6);
    double bFhor = (sumFhor - sumVhor) / (sumFhor + 1e-6);
    double blurFactor = std::max(bFver, bFhor);

    // 返回清晰度得分
    return 1.0 - blurFactor;
}

4. 代码详细解读

开始
图像是否为灰度图?
转换为灰度图
跳过转换
对灰度图像进行高斯模糊
计算原始图像和模糊图像的梯度
计算梯度的绝对值
计算梯度差的正值部分
计算梯度的总和
计算模糊因子
返回清晰度得分

流程说明:

  • 开始:函数sharpnessDetect开始执行。
  • 检查图像通道数:判断输入图像是否为灰度图。
    • 如果不是,转换为灰度图。
    • 如果是,跳过转换。
  • 高斯模糊:对灰度图像进行高斯模糊处理,得到模糊图像。
  • 计算梯度:使用Sobel算子计算原始图像和模糊图像在x和y方向的梯度。
  • 计算梯度的绝对值:对梯度矩阵取绝对值。
  • 计算梯度差的正值部分:计算原始梯度和模糊梯度之间的差异,保留正值部分。
  • 计算梯度的总和:分别计算原始梯度和差异梯度的总和。
  • 计算模糊因子:根据梯度总和计算模糊因子。
  • 返回结果:根据模糊因子计算清晰度得分,返回结果。

优化细节解读

  • 使用Sobel算子计算梯度cv::Sobel函数可以高效地计算图像在x和y方向的梯度,避免了手动计算相邻像素差异的繁琐过程。

  • 向量化操作:通过cv::abscv::sum等函数,对整个矩阵进行操作,充分利用了底层的优化和并行计算能力。

  • 梯度差的正值部分:使用cv::max函数,将梯度差中的负值置零,保留正值部分,与原始代码的逻辑一致。

  • 防止除零错误:在计算模糊因子时,分母加上了一个很小的值1e-6,防止除以零的情况。

  • 数据类型选择:使用CV_64F(双精度浮点型)确保计算的精度,避免数据溢出和精度损失。