C++ OpenCV实现boxfilter方框滤波的方法详解

 更新时间:2022年10月13日 11:34:34   作者:拜阳  
box filter的作用很简单,即对局部区域求平均,并把值赋给某个点,一般我们赋给区域中心。本文将用C++实现boxfilter方框滤波,需要的可以了解一下

box filter简单解释

box filter的作用很简单,即对局部区域求平均,并把值赋给某个点,一般我们赋给区域中心。用公式表达如下:

其中patch是以(row,col)为中心的一块区域。

为了跟后面的公式及程序对应,我们做如下定义:

  • r:patch的半径。半径在宽高方向可以不相等,但是本文目的不在于对半径的处理,所以简单起见设为相等。
  • n:patch的长度,等于(2∗r+1)。
  • (rows,cols):图像的尺寸,行数和列数。
  • (row,col):对完整图像的索引。
  • (i,j):对图像patch的索引
  • k:对通道的索引。

1. 暴力实现——四循环

外层两个循环是关于完整图像(row,col)的循环,内层两个循环是关于图像patch(i,j)的循环。

注意:如果图像是多通道的话实际上还有一个通常维度的循环,但是通道数不是本文优化的重心,所以本文不再赘述这个因素,后文也不再提,并且在计算量的估计中也会把这个因素省略掉。

这个实现比较简单,需要做的计算有:

  • rows∗cols∗n∗n次加法,内层循环的计算量o(n2),非常大。
  • rows∗cols次除法:除法为了求平均

2. 行列分离

patch的平均可以进行行列分离,也就是先对行方向做平均,并缓存结果,再对缓存的结果做列方向的平均。以公式的形式表达如下:

举个例子展开写会容易理解,比如3*3的patch,共9个数:

这种方式的计算量:

  • 2∗rows∗cols∗n次加法,相对于暴力版本,内层循环降低了一个数量级的算力,变成o(n)了
  • 2∗rows∗cols次除法

3. 行列分离优化版

第二种实现可以对求和做进一步优化。在单个维度做求和时,可以对当前一维patch的和做一个缓存,当中心点移动后,减去弹出像素的值,加上新增像素的值,这样就避免了重复性求和操作。

这种方案需要对patch的和做一个初始化和缓存,该方案的计算量为:

  • 2∗rows∗cols次减法,2∗rows∗cols次加法,内层循环的计算变为o(1)了,进一步降低了一个数量级算力。
  • 2∗rows∗cols次除法

代码

上面做计算量估计的时候没有考虑边界条件,在具体代码实现的时候需要仔细处理边界,防止数组访问越界。

代码同时跟opencv做了个效果和性能的对比,第三种方式虽然仍然比opencv慢,但性能基本处于同一量级了,opencv可能还做了一些其他跟算法无关的优化,比如指令集、并行化之类的。

注意:下面为了方便比较,opencv boxFilter的边界处理参数选择BORDER_CONSTANT。即使是边界处patch不满覆盖的情况下,opencv仍然除以n2 ,也就是说除以的数字有点大了,所以边界会逐渐发黑,特别是kernel_size(对应于radius)比较大时候视觉效果更明显。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <ctime>


using namespace std;
using namespace cv;


Mat BoxFilter_1(const Mat& image, int radius);
Mat BoxFilter_2(const Mat& image, int radius);
Mat BoxFilter_3(const Mat& image, int radius);


int main()
{
    clock_t time_beg;
    clock_t time_end;

    Mat image = imread("lena_std.bmp", IMREAD_UNCHANGED);
    image.convertTo(image, CV_32FC3);
    image /= 255.0f;

    int radius = 9;
    int ksize = radius * 2 + 1;

    Mat image_box_filter_cv;
    time_beg = clock();
    boxFilter(image, image_box_filter_cv, -1, Size(ksize, ksize), Point(-1, -1), true, BORDER_CONSTANT);
    time_end = clock();
    cout << "box-filter-cv time cost: " << time_end - time_beg << endl;

    Mat image_box_filter_1 = BoxFilter_1(image, radius);
    Mat image_box_filter_2 = BoxFilter_2(image, radius);
    Mat image_box_filter_3 = BoxFilter_3(image, radius);

    

    namedWindow("original_image", 1);
    imshow("original_image", image);
    namedWindow("cv_box_filter", 1);
    imshow("cv_box_filter", image_box_filter_cv);
    namedWindow("box_filter-1", 1);
    imshow("box_filter-1", image_box_filter_1);
    namedWindow("box_filter-2", 1);
    imshow("box_filter-2", image_box_filter_2);
    namedWindow("box_filter-3", 1);
    imshow("box_filter-3", image_box_filter_3);

    Mat diff;
    cv::absdiff(image_box_filter_2, image_box_filter_3, diff);
    namedWindow("diff", 1);
    imshow("diff", 50 * diff);

    waitKey(0);
    destroyAllWindows();

    return 0;
}


Mat BoxFilter_1(const Mat& image, int radius)
{
    int cols = image.cols;
    int rows = image.rows;
    int channels = image.channels();
    int row_bound = rows - 1;
    int col_bound = cols - 1;
    Mat result(rows, cols, CV_32FC3);

    clock_t time_beg;
    clock_t time_end;
    time_beg = clock();

    for (int row = 0; row < rows; ++row) {
        int row_beg = max(row - radius, 0);
        int row_end = min(row + radius, row_bound);
        for (int col = 0; col < cols; ++col) {
            int col_beg = max(col - radius, 0);
            int col_end = min(col + radius, col_bound);

            vector<float> sums(channels, 0.0f);
            int count = 0;
            for (int i = row_beg; i <= row_end; ++i) {
                for (int j = col_beg; j <= col_end; ++j) {
                    count++;
                    for (int k = 0; k < channels; ++k) {
                        sums[k] += image.at<Vec3f>(i, j)[k];
                    }
                }
            }

            for (int k = 0; k < channels; ++k) {
                result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);

                // opencv BORDER_CONSTANT:
                /*float COUNT = (float)(2 * radius + 1) * (2 * radius + 1);
                result.at<Vec3f>(row, col)[k] = sums[k] / COUNT;*/
            }
        }
    }
    result = cv::max(cv::min(result, 1.0), 0.0);
    time_end = clock();
    cout << "box-filter-1 time cost: " << time_end - time_beg << endl;

    return result;
}


Mat BoxFilter_2(const Mat& image, int radius)
{
    int cols = image.cols;
    int rows = image.rows;
    int channels = image.channels();
    int row_bound = rows - 1;
    int col_bound = cols - 1;
    Mat result(rows, cols, CV_32FC3);

    clock_t time_beg;
    clock_t time_end;
    time_beg = clock();

    // compute mean for row-wise
    Mat row_result(rows, cols, CV_32FC3);
    for (int row = 0; row < rows; ++row) {
        for (int col = 0; col < cols; ++col) {
            int col_beg = max(col - radius, 0);
            int col_end = min(col + radius, col_bound);

            vector<float> sums(channels, 0.0f);
            int count = 0;
            for (int j = col_beg; j <= col_end; ++j) {
                count++;
                for (int k = 0; k < channels; ++k) {
                    sums[k] += image.at<Vec3f>(row, j)[k];
                }
            }
            for (int k = 0; k < channels; ++k) {
                row_result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
            }
        }
    }

    // compute mean for column-wise
    for (int col = 0; col < cols; ++col) {
        for (int row = 0; row < rows; ++row) {
            int row_beg = max(row - radius, 0);
            int row_end = min(row + radius, row_bound);

            vector<float> sums(channels, 0.0f);
            int count = 0;
            for (int i = row_beg; i <= row_end; ++i) {
                count++;
                for (int k = 0; k < channels; ++k) {
                    sums[k] += row_result.at<Vec3f>(i, col)[k];
                }
            }
            for (int k = 0; k < channels; ++k) {
                result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
            }
        }
    }
    result = cv::max(cv::min(result, 1.0), 0.0);
    time_end = clock();
    cout << "box-filter-2 time cost: " << time_end - time_beg << endl;

    return result;
}


Mat BoxFilter_3(const Mat& image, int radius)
{
    int cols = image.cols;
    int rows = image.rows;
    int channels = image.channels();
    Mat result(rows, cols, CV_32FC3);

    clock_t time_beg;
    clock_t time_end;
    time_beg = clock();

    // compute mean for row-wise
    Mat row_result(rows, cols, CV_32FC3);
    for (int row = 0; row < rows; ++row) {
        // initialize sums for row
        vector<float> sums(channels, 0.0f);
        int count = 0;
        for (int col = 0; col < radius; ++col) {
            if (col < cols) {
                count++;
                for (int k = 0; k < channels; ++k) {
                    sums[k] += image.at<Vec3f>(row, col)[k];
                }
            }
        }
        // process row
        for (int col = 0; col < cols; ++col) {
            int left = col - radius - 1;
            int right = col + radius;
            if (left >= 0) {
                count--;
                for (int k = 0; k < channels; ++k) {
                    sums[k] -= image.at<Vec3f>(row, left)[k];
                }
            }
            if (right < cols) {
                count++;
                for (int k = 0; k < channels; ++k) {
                    sums[k] += image.at<Vec3f>(row, right)[k];
                }
            }
            for (int k = 0; k < channels; ++k) {
                row_result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
            }
        }
    }

    // compute mean for column-wise
    for (int col = 0; col < cols; ++col) {
        // initialize sums for column
        vector<float> sums(channels, 0.0f);
        int count = 0;
        for (int row = 0; row < radius; ++row) {
            if (row < rows) {
                count++;
                for (int k = 0; k < channels; ++k) {
                    sums[k] += row_result.at<Vec3f>(row, col)[k];
                }
            }
        }
        // process column
        for (int row = 0; row < rows; ++row) {
            int up = row - radius - 1;
            int down = row + radius;
            if (up >= 0) {
                count--;
                for (int k = 0; k < channels; ++k) {
                    sums[k] -= row_result.at<Vec3f>(up, col)[k];
                }
            }
            if (down < rows) {
                count++;
                for (int k = 0; k < channels; ++k) {
                    sums[k] += row_result.at<Vec3f>(down, col)[k];
                }
            }
            for (int k = 0; k < channels; ++k) {
                result.at<Vec3f>(row, col)[k] = sums[k] / static_cast<float>(count);
            }
        }
    }
    result = cv::max(cv::min(result, 1.0), 0.0);
    time_end = clock();
    cout << "box-filter-3 time cost: " << time_end - time_beg << endl;

    return result;
}

以上就是C++ OpenCV实现boxfilter方框滤波的方法详解的详细内容,更多关于C++ OpenCV boxfilter方框滤波的资料请关注脚本之家其它相关文章!

相关文章

  • C++输入一个字符串,把其中的字符按照逆序输出的两种方法解析

    C++输入一个字符串,把其中的字符按照逆序输出的两种方法解析

    以下是对C++中输入一个字符串,把其中的字符按照逆序输出的两种方法进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-07-07
  • C++大数模板(推荐)

    C++大数模板(推荐)

    本篇文章是对C++大数模板的程序代码进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Qt中QDateTimeEdit的具体使用

    Qt中QDateTimeEdit的具体使用

    本文主要介绍了Qt中QDateTimeEdit的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Qt实现http服务的示例代码

    Qt实现http服务的示例代码

    这篇文章将为大家详细讲解有关Qt如何实现http服务,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获
    2023-04-04
  • VS2019项目打包生成.exe文件与Setup的步骤实现

    VS2019项目打包生成.exe文件与Setup的步骤实现

    这篇文章主要介绍了VS2019项目打包生成.exe文件与Setup的步骤实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • C/C++可变参数的使用

    C/C++可变参数的使用

    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    2013-09-09
  • C++11中列表初始化机制的概念与实例详解

    C++11中列表初始化机制的概念与实例详解

    在我们实际编程中,我们经常会碰到变量初始化的问题,对于不同的变量初始化的手段多种多样,下面这篇文章主要给大家介绍了关于C++11中列表初始化机制的相关资料,需要的朋友可以参考下
    2021-11-11
  • C语言实现简单井字棋游戏

    C语言实现简单井字棋游戏

    这篇文章主要为大家详细介绍了C语言实现简单井字棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • C语言运算符与表达式

    C语言运算符与表达式

    这篇文章主要介绍了C语言运算符与表达式,表达式是C语言的主体。在C语言中,表达式由操作符和操作数组成,更多相关介绍需要的小伙伴可以参考下面文章内容
    2022-07-07
  • C语言数据结构二叉树之堆的实现和堆排序详解

    C语言数据结构二叉树之堆的实现和堆排序详解

    堆是计算机科学中一类特殊的数据结构的统称,通常是一个可以被看做一棵完全二叉树的数组对象。而堆排序是利用堆这种数据结构所设计的一种排序算法。本文将详细介绍堆的实现和堆排序,需要的可以参考一下
    2022-04-04

最新评论