OpenCV之滤镜&人脸美化(三) 积分图算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huanghuangjin/article/details/81669468

这里写图片描述
有了积分图像,在做卷积的时候就可以大大减少运算次数,从而提升速度。
还可以计算积分平方和表,用于一些特殊的矩阵变换。

积分图应用:
    快速模糊
    快速匹配
    快速马赛克

代码

#include "../common/common.hpp"

static void mean_blur(Mat &image, Mat &sum, int size);

void main(int argc, char** argv) 
{
    Mat src = imread(getCVImagesPath("images/lena.png"));
    imshow("src9-5", src);

    Mat sum, sqrsum;
    integral(src, sum, sqrsum, CV_32S, CV_32F); // 计算积分和表于积分平方和表,sum sqrsum 设置为什么深度,看具体情况
    // [512 x 512],[513 x 513],[513 x 513]   计算出来的积分和表与积分平方和表的宽高比原图的宽高大1个尺寸,且第1行第1列的值都为0
    cout << src.size() << "," << sum.size() << "," << sqrsum.size() << endl;
    int size = 25; // 卷积的宽度
    mean_blur(src, sum, size);

    Mat dst;
    double time = getTickCount();
    blur(src, dst, Size(size, size), Point(-1, -1));
    cout << "blur time=" << (getTickCount() - time) / getTickFrequency() << endl; // blur time=0.0363686
    imshow("blur9-5", dst);

    waitKey(0);
}

void mean_blur(Mat &image, Mat &sum, int size) // 利用积分图实现均值模糊,比opencv的blur函数计算速度慢了10倍,将下面算法改成指针版速度会快?
{
    int w = image.cols; // 原图宽高
    int h = image.rows;

    Mat result = Mat::zeros(image.size(), image.type()); // 最终计算出来的均值图像
    int x2 = 0, y2 = 0; // 这四个值用于在积分和表中标定要计算的积分区域的位置
    int x1 = 0, y1 = 0;
    int ksize = size; // 要计算的积分区域的宽度,即相当于卷积的宽度
    int radius = ksize / 2; // 一半
    int ch = image.channels(); // 原图通道数
    int cx = 0, cy = 0; // 卷积的中心点坐标
    double time = getTickCount();
    for (int row = 0; row < h + radius; row++) // h + radius 这里只是为了后面计算卷积中心点cx,xy  并不是考虑了border
    {
        y2 = (row + 1) > h ? h : (row + 1); // row + 1 是因为和表比原图尺寸大1
        y1 = (row - ksize) < 0 ? 0 : (row - ksize); // 当row还不够ksize大小的时候,要计算的积分区域的上边两个点的值为0
        for (int col = 0; col < w + radius; col++) // w + radius 这里只是为了后面计算卷积中心点cx,xy  并不是考虑了border
        {
            x2 = (col + 1) > w ? w : (col + 1); // col + 1 是因为和表比原图尺寸大1
            x1 = (col - ksize) < 0 ? 0 : (col - ksize); // 当col还不够ksize大小的时候,要计算的积分区域的左边两个点的值为0
            for (int i = 0; i < ch; i++) // 计算各通道上的均值
            {
                int tl = sum.at<Vec3i>(y1, x1)[i]; // 上左的i通道
                int tr = sum.at<Vec3i>(y1, x2)[i]; // 上右的i通道
                int bl = sum.at<Vec3i>(y2, x1)[i]; // 下左的i通道
                int br = sum.at<Vec3i>(y2, x2)[i]; // 下右的i通道
                int s = br - bl - tr + tl; // 区域积分和计算公式
                cx = (col - radius) < 0 ? 0 : col - radius; // 获取'卷积'的中心点,当row,col小于radius的时候,这里都是取的目标图像的0,0位置
                cy = (row - radius) < 0 ? 0 : row - radius; // 这里的算法并没有考虑border
                int num = (x2 - x1)*(y2 - y1); // 积分区域的真实像素点的数目
                result.at<Vec3b>(cy, cx)[i] = saturate_cast<uchar>(s / num); // 积分区域的均值赋给目标图像cx,cy像素点的i通道,saturate_cast 值范围锁定在 0-255
            }
        }
    }
    cout << "mean_blur time=" << (getTickCount() - time) / getTickFrequency() << endl; // mean_blur time=0.363893
    imshow("blured-image", result);
}

效果图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huanghuangjin/article/details/81669468