程序猿学习OpenCv图像处理

【图像处理】OpenCV系列二十七 --- 积分图(integr

2019-05-10  本文已影响16人  307656af5a04

上一节我们学习了图像分割算法,相信大家学习之后,对图像分割算法已经有了基本的认识,本节呢,我们对学习积分图,对integral函数详解

一、函数详解

1、函数原型

// 原型一
void integral(InputArray src, 
    OutputArray sum, 
    int sdepth = -1);

// 原型二
void integral(InputArray src, 
    OutputArray sum,
    OutputArray sqsum, 
    int sdepth = -1, 
    int sqdepth = -1);

// 原型三
void integral(InputArray src, 
    OutputArray sum,
    OutputArray sqsum, 
    OutputArray tilted,
    int sdepth = -1, 
    int sqdepth = -1);

2、函数功能
该函数计算源图像的一个或多个积分图像;

具体公式如下:

使用这些积分图像,你可以在一定时间内计算图像特定的上、右或旋转矩形区域的和、平均值和标准差;

例如:

例如,它可以与可变窗口大小进行快速模糊或快速块关联。对于多通道图像,每个信道的和是独立累积的;

作为一个实例,下图显示了直角矩形的积分(3,3,3,2)和倾斜矩形的积分(5,1,2,3)。显示原始图像中选定的像素,以及积分图像中的相对像素和倾斜。

积分图

3、积分图概念详解
积分图像,顾名思义,对每一个点的像素值,做相应的积分,其实简单表达就是每一个点的像素值就等于在这之前所有像素值的和;

给定一副图像(最好是灰度scale,彩色的话需要对每一个通道都要计算)。灰度值如下图所示:

图1:灰度图像

图1是一幅图像的像素(里面像素都是灰度scale,为了方便解释这里的灰度值都是1~9),要计算图中深度底色的矩形框中的灰度和,最直接的想法就是将这9个像素值直接相加,用数学公式表示就是:

公式1:i,j代表位置,image代表图像

很显然,如果只是针对小区域的话,这样的加减倒也不是很复杂,如果这个区域包含成千上万的像素,那计算起来就比较复杂了。所以随着计算区域的增大,时间复杂度增长也很快;

有没有一种比较简单的方法,可以无论需要计算的区域有多大,它的时间复杂度都不变呢,或者变化很小。这就是积分图像最初的考虑策略,尽量减少后续的计算以达到实时的效果;

将图1转化为积分图像如图2:

图1的积分图像

现在计算图中深度区域的值就可以很简单利用4次差别结果得到:120 - 42 - 21 + 6 = 63;
怎样利用积分图像来计算任意矩形框的值呢?一般来说简单作4次查表,再简单的“--+”运算即可得到矩形框里面的灰度和;

详解: 积分图像计算

区域1 : = sum(A);
区域2 : = sum(A + B);
区域3 : = sum(A + C);
区域4 : = sum(A + B + C + D);

所以,如果需要计算D区域中的灰度和,则
sum(D) = 区域4 - 区域2 - 区域3 + 区域1 (都是灰度值);

4、参数详解

二、综合实例

1、实验案例
计算图像的积分图

#include <opencv2/opencv.hpp>
#include <iostream>
 
using namespace cv;
 
int main( )
{
    // 载入图像
    Mat src = imread("lena.png");
    
    // 判断图像是否为空
    if (src.empty())
    {
        printf("image error!\n");
        return -1;
    }
    
    // 创建窗体
    namedWindow("原图", WINDOW_AUTOSIZE);
    
    // 显示原始图像
    imshow("原图", src);
    
    // 彩色图像转换为灰度图像
    Mat gray;
    cvtColor(src, gray,COLOR_BGR2GRAY);
    
    // 显示灰度图像
    namedWindow("灰度图", WINDOW_AUTOSIZE);
    imshow("灰度图", gray);
    
    // 求和图像
    Mat sumImage = Mat::zeros(gray.rows + 1, 
        gray.cols + 1, CV_32FC1);
    
    // 平方和图像
    Mat sqsumImage = Mat::zeros(gray.rows + 1, 
        gray.cols + 1, CV_64FC1);
    
    // 积分图像处理
    integral(gray, sumImage, sqsumImage);
    
    Mat resultSum;
    
    // 归一化处理
    normalize(sumImage, resultSum, 0, 255, 
        NORM_MINMAX, CV_8UC1, Mat());
    
    // 显示结果图像
    // 显然积分图像是右下角亮,左上角暗
    imshow("Integral-Image", resultSum);  
 
    waitKey(0);
    return 0;
}

2、实验结果

原图(左)与效果图(右)

3、实验2

计算感兴趣区域的所有像素的累加和,对比常规方法与积分图方法的异同!

(1)常规方法

#include <opencv2/opencv.hpp>
#include <iostream>
 
using namespace cv;
using namespace std;
 
int main( )
{
    // 载入图像
    Mat src = imread("lena.png");
    
    // 判断图像是否为空
    if (src.empty())
    {
        printf("image error !\n");
        return -1;
    }
    
    // 显示原始图像
    namedWindow("原图", WINDOW_AUTOSIZE);
    imshow("原图", src);
    
    // 彩色图像转换为灰度图像
    Mat gray;
    cvtColor(src, gray,COLOR_BGR2GRAY);
    
    // ROI区域矩形
    int xo = 240, yo = 240;
    int width = 100, height = 100;
    
    //定义图像的ROI(兴趣区域)
    Mat roiImage = gray(Rect(xo, yo, width, height)).clone();    
    
    // 显示感兴趣区域
    namedWindow("兴趣区域", WINDOW_AUTOSIZE);
    imshow("兴趣区域", roiImage);
    
    
    //计算兴趣区域灰度值的累加值
    int integralValue = sum(roiImage)[0]; 
    cout << integralValue << endl;
    
    waitKey();
    return 0;
}

实验结果

实验结果

(2)积分图方法

#include <opencv2/opencv.hpp>
#include <iostream>
 
using namespace cv;
using namespace std;
 
int main( )
{
    // 载入图像
    Mat src = imread("lena.png");
    
    // 判断图像是否为空
    if (src.empty())
    {
        printf("image error !\n");
        return -1;
    }
    
    // 显示原始图像
    namedWindow("原图", WINDOW_AUTOSIZE);
    imshow("原图", src);
    
    // 彩色图像转换为灰度图像
    Mat gray;
    cvtColor(src, gray,COLOR_BGR2GRAY);
    
    // ROI区域矩形
    int xo = 240, yo = 240;
    int width = 100, height = 100;
    
    //定义图像的ROI(感兴趣区域)
    Mat roi_img = gray(Rect(xo, yo, width, height)).clone();    
    
    // 显示ROI区域矩形
    namedWindow("感兴趣区域", WINDOW_AUTOSIZE);
    imshow("感兴趣区域", roi_img);
    
    
    //计算兴趣区域灰度值的累加值
    Mat sumImage = Mat::zeros(gray.rows + 1, 
        gray.cols + 1, CV_32SC1);
        
    Mat sqsumImage = Mat::zeros(gray.rows + 1, 
        gray.cols + 1, CV_64FC1);
    
    // 计算积分图
    integral(gray, sumImage, sqsumImage);   

    
    // 用三个加/减运算得到兴趣区域的累加值
    int integral_value = sumImage.at<int>(yo + height, xo + width) 
        - sumImage.at<int>(yo + height, xo)
        - sumImage.at<int>(yo, xo + width)
        + sumImage.at<int>(yo, xo);
        
    cout << integral_value << endl;
    
    waitKey();
    return 0;
}

实验结果

实验结果

经过对比,其实两种做法得到的结果是一样的。但计算积分图像需要遍历全部像素,因此速度比较慢。关键是一旦这个初始计算完成,只需要通过四个像素就能得到兴趣区域的累加和,与区域的尺寸无关。因此,如果需要在多个兴趣区域上计算像素的累加和,就最好采用积分图像。

我是奕双,现在已经毕业将近两年了,从大学开始学编程,期间学习了C语言编程,C++语言编程,Win32编程,MFC编程,毕业之后进入一家图像处理相关领域的公司,掌握了用OpenCV对图像进行处理,如果大家对相关领域感兴趣的话,可以关注我,我这边会为大家进行解答哦!如果大家需要相关学习资料的话,可以私聊我哦!

上一篇下一篇

猜你喜欢

热点阅读