美文网首页
【DIP】基于积分图的滤波算法加速

【DIP】基于积分图的滤波算法加速

作者: ItchyHiker | 来源:发表于2018-11-21 15:25 被阅读0次

数字图像处理中经常会用到均值滤波,快速的均值方式通常会使用行求和,得到中间图像然后进行列求和。最后除以模版的面积。

基于积分图的均值滤波可以在O(1)时间内求出任意半径的均值滤波结果。前提是求出积分图。
先贴结果,如果下图积分图,中间图片是模版,则原图模版内的像素和为
I_4 + I_1 - I_2 - I_3
I1, I2, I3, I4为中间模板的四个顶点,对应的是积分图中的像素值,中心点到矩形模版四条边的距离为r, 则均值滤波后中间像素值为:
\frac{I_4 + I_1 - I_2 - I_3}{(2*r+1)^2}

plot by ItchyHacker.jpeg

下面看看公式是如何得出的:
I1 的像素值是原图A矩形内像素值和,I2 的像素值是原图A + C矩形内像素值和, I3 的像素值是原图A+B矩形内像素值和, I4 的像素值是原图A+B+C+D矩形内像素值和,
I_4 + I_1 - I_2 - I_3 = A+B+C+D + A - A - C - A - B = D
因此只要有积分图之后,任意半径的的均值滤波结果只需要取四个数加减然后除以均值滤波模版的面积便可。
OpenCV内置积分算法,下面贴一下我自己实现的积分算法和内置积分算法对比,还有基于积分图的均值滤波和两次求和遍历取平均的结果。程序边缘区域没有处理,只是简单的测试程序。

//
//  integralCompare.cpp
//  FaceBeauty
//
//  Created by yuhua.cheng on 2018/11/6.
//  Copyright © 2018 yuhua.cheng. All rights reserved.
//
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
void myIntegral(const cv::Mat& img, cv::Mat& integral){
    // 我自己实现的积分图算法
    int height = img.rows;
    int width = img.cols;
    cv::Mat tempImg;
    img.convertTo(tempImg, CV_32FC3, 1/255.0);
    integral = tempImg.clone();
    // 第一行积分图值计算
    float* imgPtr = tempImg.ptr<float>(0);
    float* interPrevPtr = integral.ptr<float>(0);
    float* interPtr = integral.ptr<float>(0);
    interPtr += 3;
    for(int j = 1; j < width; j++){
        interPtr[0] = interPrevPtr[0] + imgPtr[0];
        interPtr[1] = interPrevPtr[1] + imgPtr[1];
        interPtr[2] = interPrevPtr[2] + imgPtr[2];
        imgPtr += 3;
        interPrevPtr += 3;
        interPtr += 3;
    }
    // 第一列积分图值计算
    for(int i = 1; i < height; i++){
        interPrevPtr = integral.ptr<float>(i-1);
        interPtr = integral.ptr<float>(i);
        imgPtr = tempImg.ptr<float>(i);
        interPtr[0] = interPrevPtr[0] + imgPtr[0];
        interPtr[1] = interPrevPtr[1] + imgPtr[1];
        interPtr[2] = interPrevPtr[2] + imgPtr[2];
    }
    // 利用第一行和第一列信息计算积分图
    for(int i = 1; i < height; i++){
        interPrevPtr = integral.ptr<float>(i-1)+3;
        interPtr = integral.ptr<float>(i)+3;
        imgPtr = tempImg.ptr<float>(i)+3; // 这里可以改进
        float rowSum[3] = {0};
        for(int j = 1; j < width; j++){
            rowSum[0] += (imgPtr-3)[0];
            rowSum[1] += (imgPtr-3)[1];
            rowSum[2] += (imgPtr-3)[2];
            interPtr[0] = interPrevPtr[0] + imgPtr[0] + rowSum[0];
            interPtr[1] = interPrevPtr[1] + imgPtr[1] + rowSum[1];
            interPtr[2] = interPrevPtr[2] + imgPtr[2] + rowSum[2];
            
            interPrevPtr += 3;
            interPtr += 3;
            imgPtr += 3;
        }
    }
}
void integralBoxBlur(const cv::Mat& img, cv::Mat& dst, int radius){
    // 使用积分图实现的boxBlur算法
    int r = radius;
    cv::Mat tempImg;
    img.convertTo(tempImg, CV_32FC3, 1/255.0);
    cv::Mat integral;
    myIntegral(img, integral);
    int weight = (2*r+1)*(2*r+1);
    for(int row = r; row < img.rows - r; row++){
        float* intePtr1 = integral.ptr<float>(row-r);
        float* intePtr2 = integral.ptr<float>(row+r);
        float* imgPtr = tempImg.ptr<float>(row);
        intePtr1 += 3*r;
        intePtr2 += 3*r;
        imgPtr += 3*r;
        for(int col = r; col < img.cols - r; col++){
            imgPtr[0] = ((intePtr2+3*r)[0] + (intePtr1-3*r)[0] - (intePtr1+3*r)[0] - (intePtr2-3*r)[0])/weight;
            imgPtr[1] = ((intePtr2+3*r)[1] + (intePtr1-3*r)[1] - (intePtr1+3*r)[1] - (intePtr2-3*r)[1])/weight;
            imgPtr[2] = ((intePtr2+3*r)[2] + (intePtr1-3*r)[2] - (intePtr1+3*r)[2] - (intePtr2-3*r)[2])/weight;
            intePtr1 += 3;
            intePtr2 += 3;
            imgPtr += 3;
        }
    }
    tempImg.convertTo(dst, CV_8UC3, 255);
}
void sumBoxBlur(const cv::Mat& img, cv::Mat& dst, int radius){
    // 普通求和平均的计算方式
    int r = radius;
    int weight = (2*r+1)*(2*r+1);
    int height = img.rows;
    int width = img.cols;
    cv::Mat tempImg;
    img.convertTo(tempImg, CV_32FC3, 1/255.0);
    cv::Mat rowSum;
    rowSum = tempImg.clone();
    cv::Mat colSum;
    colSum = tempImg.clone();
    dst = tempImg.clone();
    // 行求和
    for(int i = 0; i < height; i++){
        float *imgPtr = tempImg.ptr<float>(i);
        float *colPtr = colSum.ptr<float>(i);
        for(int j = 0; j < width; j++){
            if(j < r){
                imgPtr += 3;
                colPtr += 3;
            }else{
                for(int bias = -r; bias <= r; bias++){
                    colPtr[0] += (imgPtr+3*bias)[0];
                    colPtr[1] += (imgPtr+3*bias)[1];
                    colPtr[2] += (imgPtr+3*bias)[2];
                }
                imgPtr +=3;
                colPtr += 3;
            }   
        }
    }
    cv::imshow("colSum",colSum);
    // 列求和
    for(int j = 0; j < width; j++){
        for(int i = 0; i < height; i++){
            if(i < r){
                continue;
            }else{
                float *rowPtr = rowSum.ptr<float>(i)+3*j;
                for(int bias = -r; bias <= r; bias++){
                    rowPtr[0] += (colSum.ptr<float>(i+bias)+3*j)[0];
                    rowPtr[1] += (colSum.ptr<float>(i+bias)+3*j)[1];
                    rowPtr[2] += (colSum.ptr<float>(i+bias)+3*j)[2];
                }
            }
        }
    }
    dst = rowSum / weight;
    dst.convertTo(dst, CV_8UC3, 255);
}
int main(){
    cv::Mat img = cv::imread("/Users/yuhua.cheng/Documents/dataset/Face/10.jpg");
    int height = img.rows;
    int width = img.cols;
    cv::Mat result1, result2, result3;
    cv::Mat tempImg;
    img.convertTo(tempImg, CV_32FC3, 1/255.0);
    cv::Mat integral;
    double t0, t1;
    t0 =  cv::getTickCount();
    cv::integral(tempImg, integral, -1);
    t1 = cv::getTickCount();
    std::cout << "Opencv integral time: " << (t1 - t0) / cv::getTickFrequency() << std::endl;
    t0 =  cv::getTickCount();
    myIntegral(img, integral);
    t1 = cv::getTickCount();
    std::cout << "My integral time: " << (t1 - t0) / cv::getTickFrequency() << std::endl;
    t0 =  cv::getTickCount();
    integralBoxBlur(img, result1, 11);
    t1 = cv::getTickCount();
    std::cout << "Integral box Blur Time: " << (t1 - t0) / cv::getTickFrequency() << std::endl;
    cv::imshow("my BoxBlur", result1);
    t0 =  cv::getTickCount();
    cv::boxFilter(img, result2, -1, cv::Size(23,23));
    t1 = cv::getTickCount();
    std::cout << "Opencv box Filter Time: " << (t1 - t0) / cv::getTickFrequency() << std::endl;
    cv::imshow("opencv BoxFilter", result2);
    t0 =  cv::getTickCount();
    sumBoxBlur(img, result3, 11);
    t1 = cv::getTickCount();
    std::cout << "sumBoxFilter Time: " << (t1 - t0) / cv::getTickFrequency() << std::endl;
    cv::imshow("sum BoxFilter", result3);
    cv::waitKey(0);
}
结果: 
Opencv integral time: 0.0155424
My integral time: 0.0254387
Integral box Blur Time: 0.0337875
Opencv box Filter Time: 0.00485557
sumBoxFilter Time: 0.319712

Reference

  1. https://blog.csdn.net/lijianlarry/article/details/78678297
  2. https://blog.csdn.net/u010839382/article/details/48241929
  3. Non-local Means Algorithm Introduction and Implementation: http://www.ipol.im/pub/art/2011/bcm_nlm/
  4. 积分图的并行算法:https://blog.csdn.net/10km/article/details/51610735
  5. 快速均值滤波算法 : https://www.opengl.org/discussion_boards/showthread.php/167435-fastest-wide-width-box-filter

相关文章

  • 【DIP】基于积分图的滤波算法加速

    数字图像处理中经常会用到均值滤波,快速的均值方式通常会使用行求和,得到中间图像然后进行列求和。最后除以模版的面积。...

  • SLAM系列(二):扩展卡尔曼滤波SLAM理论部分a

    关于2维的SLAM我们主要讲解两个算法,基本都源于 。第一是基于滤波的扩展卡尔曼滤波SLAM,第二是基于图优化SL...

  • 基于边缘保留滤波实现人脸磨皮的算法

    快速边缘保留滤波 快速边缘保留滤波是通过积分图像实现局部均方差的边缘保留模糊算法,计算简单而且可以做到计算量跟半径...

  • 39. 边缘检测

    本文解释Canny和sobel边缘检测算法。 1)Canny算法实现 步骤: 读取灰度图 高斯滤波 Canny算法...

  • 基于勒贝格积分的一种离散变换方法

    基于勒贝格积分原理的一种傅立叶变换算法 勒贝格原理 勒贝格积分勒贝格积分是指是值域积分原理,具有典型的统计特征。相...

  • 2018-08-08

    凌晨三点,分享一波滤波算法 1.限幅滤波算法 2.中值滤波算法 连续采样n次(n取奇数),按照从大到小排列,取中...

  • 基于GPS轨迹数据驻留点集散地识别

    聚类算法的GPS静态单点定位方法 为有效提高GPS静态单点定位的精度,提出了一种基于模糊聚类算法和卡尔曼滤波算法的...

  • OpenCV

    原图像保边滤波算法集锦--MeanShift滤波算法与实现https://blog.csdn.net/Trent1...

  • iOS-卡尔曼滤波算法

    一:前言 滤波算法 用于过滤掉连续的数据中出现偏差较大的数据 二:卡尔曼滤波算法 <0>卡尔曼滤波的原理请自行百度...

  • 基于图的推荐算法(3):Collaborative Simila

    前言 WWW2019,基于图嵌入思想的推荐算法研究 相关研究参见:基于图的推荐算法(1): Query-based...

网友评论

      本文标题:【DIP】基于积分图的滤波算法加速

      本文链接:https://www.haomeiwen.com/subject/mwvotqtx.html