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
相关产品推荐
相关产品推荐

