You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV C++中基于邻域均值的快速孔洞填充滤波实现问询

四邻域均值孔洞填充的OpenCV高效实现问题

我实现了一个基础的孔洞填充滤波器,代码如下所示:

#include <iostream>
#include <opencv2/opencv.hpp>

int main(int argc, char** argv)
{
    // 注:实际使用的深度图尺寸为720 x 576,格式为8UC1
    // 此处构造小尺寸测试图验证逻辑
    uchar flatten[6 * 8] = { 140, 185,  48, 235, 201, 192, 131,  57,
                              55,  87,  82,   0,   6, 201,   0,  38,
                               6, 239,  82, 142,  46,  33, 172,  72,
                             133,   0, 232, 226,  66,  59,  10, 204,
                             214, 123, 202, 100,   0,  32,   6, 147,
                             105, 191,  50,  21,  87, 117, 118, 244};

    cv::Mat depthImg = cv::Mat(6, 8, CV_8UC1, flatten);

    // 暂不处理图像边界像素
    for (int i = 1; i < depthImg.cols - 1; i++) {
        for (int j = 1; j < depthImg.rows - 1; j++) {
            unsigned short sumNonZeroAdjs = 0;
            uchar countNonZeroAdjs = 0;
            if (depthImg.at<uchar>(j, i) == 0) {
                uchar iMinus1 = depthImg.at<uchar>(j, i - 1);
                uchar  iPlus1 = depthImg.at<uchar>(j, i + 1);
                uchar jMinus1 = depthImg.at<uchar>(j - 1, i);
                uchar  jPlus1 = depthImg.at<uchar>(j + 1, i);
                if (iMinus1 != 0) {
                    sumNonZeroAdjs += iMinus1;
                    countNonZeroAdjs++;
                }
                if (iPlus1 != 0) {
                    sumNonZeroAdjs += iPlus1;
                    countNonZeroAdjs++;
                }
                if (jMinus1 != 0) {
                    sumNonZeroAdjs += jMinus1;
                    countNonZeroAdjs++;
                }
                if (jPlus1 != 0) {
                    sumNonZeroAdjs += jPlus1;
                    countNonZeroAdjs++;
                }
                depthImg.at<uchar>(j, i) = sumNonZeroAdjs / countNonZeroAdjs;
            }
        }
    }

    std::cout << depthImg << std::endl;
    return 0;
}
// 运行输出结果:
[140, 185, 48, 235, 201, 192, 131, 57;
  55, 87, 82, 116, 6, 201, 135, 38;
  6, 239, 82, 142, 46, 33, 172, 72;
  133, 181, 232, 226, 66, 59, 10, 204;
  214, 123, 202, 100, 71, 32, 6, 147;
  105, 191, 50, 21, 87, 117, 118, 244]

上述滤波器通过计算上下左右四邻域非零像素的平均值,填充值为0的孔洞像素点,输出效果符合预期,但原型实现写法冗余,运行速度极慢。

核心需求:寻找逻辑一致(即使用邻域像素填充0值像素)、执行速度更快的OpenCV内置孔洞填充滤波器实现

运行环境:Ubuntu 20.04 LTS系统,OpenCV v4.2.0版本

更新1

根据收到的优化建议,我实现了指针风格的像素访问版本,完整代码如下:

#include <iostream>
#include <opencv2/opencv.hpp>

void inPlaceHoleFillingExceptBorderPtrStyle(cv::Mat& img) {
  typedef uchar T;
  T* ptr = img.data;
  size_t elemStep = img.step / sizeof(T);

  for (int i = 1; i < img.rows - 1; i++) {
    for (int j = 1; j < img.cols - 1; j++) {
      T& curr = ptr[i * elemStep + j];
      if (curr != 0) {
        continue;
      }

      ushort sumNonZeroAdjs = 0;
      uchar countNonZeroAdjs = 0;
      T iM1 = ptr[(i - 1) * elemStep + j];
      T iP1 = ptr[(i + 1) * elemStep + j];
      T jM1 = ptr[i * elemStep + (j - 1)];
      T jP1 = ptr[i * elemStep + (j + 1)];

      if (iM1 != 0) {
        sumNonZeroAdjs += iM1;
        countNonZeroAdjs++;
      }
      if (iP1 != 0) {
        sumNonZeroAdjs += iP1;
        countNonZeroAdjs++;
      }
      if (jM1 != 0) {
        sumNonZeroAdjs += jM1;
        countNonZeroAdjs++;
      }
      if (jP1 != 0) {
        sumNonZeroAdjs += jP1;
        countNonZeroAdjs++;
      }
      if (countNonZeroAdjs > 0) {
        curr = sumNonZeroAdjs / countNonZeroAdjs;
      }
    }
  }
}

void inPlaceHoleFillingExceptBorder(cv::Mat& img) {
  typedef uchar T;

  for (int i = 1; i < img.cols - 1; i++) {
    for (int j = 1; j < img.rows - 1; j++) {
      ushort sumNonZeroAdjs = 0;
      uchar countNonZeroAdjs = 0;
      if (img.at<T>(j, i) != 0) {
        continue;
      }

      T iM1 = img.at<T>(j, i - 1);
      T iP1 = img.at<T>(j, i + 1);
      T jM1 = img.at<T>(j - 1, i);
      T jP1 = img.at<T>(j + 1, i);

      if (iM1 != 0) {
        sumNonZeroAdjs += iM1;
        countNonZeroAdjs++;
      }
      if (iP1 != 0) {
        sumNonZeroAdjs += iP1;
        countNonZeroAdjs++;
      }
      if (jM1 != 0) {
        sumNonZeroAdjs += jM1;
        countNonZeroAdjs++;
      }
      if (jP1 != 0) {
        sumNonZeroAdjs += jP1;
        countNonZeroAdjs++;
      }
      if (countNonZeroAdjs > 0) {
        img.at<T>(j, i) = sumNonZeroAdjs / countNonZeroAdjs;
      }
    }
  }
}

int main(int argc, char** argv) {
  // 注:实际图像尺寸为720 x 576,格式8UC1
  // 构造小尺寸测试图
  // clang-format off
  uchar flatten[6 * 8] = { 140, 185,  48, 235, 201, 192, 131,  57,
                            55,  87,  82,   0,   6, 201,   0,  38,
                             6, 239,  82, 142,  46,  33, 172,  72,
                           133,   0, 232, 226,  66,  59,  10, 204,
                           214, 123, 202, 100,   0,  32,   6, 147,
                           105, 191,  50,  21,  87, 117, 118, 244};
  // clang-format on

  cv::Mat img = cv::Mat(6, 8, CV_8UC1, flatten);
  cv::Mat img1 = img.clone();
  cv::Mat img2 = img.clone();

  inPlaceHoleFillingExceptBorderPtrStyle(img1);
  inPlaceHoleFillingExceptBorder(img2);

  return 0;
}

/*** 预期输出
[140, 185,  48, 235, 201, 192, 131, 57;
  55,  87,  82, 116,  6,  201, 135, 38;
   6, 239,  82, 142, 46,   33, 172, 72;
 133, 181, 232, 226, 66,   59,  10, 204;
 214, 123, 202, 100, 71,   32,   6, 147;
 105, 191,  50,  21, 87,  117, 118, 244]
***/

更新2

在指针版本基础上做了进一步优化,优化后的代码如下:

void inPlaceHoleFillingExceptBorderImpv(cv::Mat& img) {
  typedef uchar T;
  size_t elemStep = img.step1();
  const size_t margin = 1;

  for (size_t i = margin; i < img.rows - margin; ++i) {
    T* ptr = img.data + i * elemStep;
    for (size_t j = margin; j < img.cols - margin; ++j, ++ptr) {
      T& curr = ptr[margin];
      if (curr != 0) {
        continue;
      }

      T& north = ptr[margin - elemStep];
      T& south = ptr[margin + elemStep];
      T&  east = ptr[margin + 1];
      T&  west = ptr[margin - 1];

      ushort  sumNonZeroAdjs = 0;
      uchar countNonZeroAdjs = 0;
      if (north != 0) {
        sumNonZeroAdjs += north;
        countNonZeroAdjs++;
      }
      if (south != 0) {
        sumNonZeroAdjs += south;
        countNonZeroAdjs++;
      }
      if (east != 0) {
        sumNonZeroAdjs += east;
        countNonZeroAdjs++;
      }
      if (west != 0) {
        sumNonZeroAdjs += west;
        countNonZeroAdjs++;
      }
      if (countNonZeroAdjs > 0) {
        curr = sumNonZeroAdjs / countNonZeroAdjs;
      }
    }
  }
}

内容的提问来源于stack exchange,提问作者ravi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 23:33:29