OpenCV積分圖函數:integral ()詳解

 

/**************************************************************************************************/
// 函數名稱:OnMenu020503()
// 函數功能:“對角積分圖”菜單。
// 函數參數:
//     輸入參數: 無
//     輸出參數: 無
// 返 回 值:void
// 創建作者:(QQ:370711753)
// 修改日期:2017/11/08 16:25:58
/**************************************************************************************************/
void COpenCVDlg::OnMenu020503()
{
    try    // 錯誤處理
    {
        Mat lv_MatImageIntegralSum = Mat();
        Mat lv_MatImageIntegralSqSum = Mat();
        Mat lv_MatImageIntegralTilted = Mat();
        Mat lv_MatImageIntegralNorm = Mat();  
        // 計算積分圖像
        cv::integral(m_MatImageRead, lv_MatImageIntegralSum,
            lv_MatImageIntegralSqSum, lv_MatImageIntegralTilted, CV_64F, CV_64F);
        // 圖像數據歸一化
        cv::normalize(lv_MatImageIntegralTilted, lv_MatImageIntegralTilted, 0, 255, CV_MINMAX);
        convertScaleAbs(lv_MatImageIntegralTilted,lv_MatImageIntegralNorm);        // 精度轉換爲8位INT整型 
        // 顯示結果
        HV_ImageShow(lv_MatImageIntegralNorm, m_strNameWindow);
    }
    catch (Exception& lv_Exception)        // 拋出異常
    {
        CString lv_strException = _T("");
        lv_strException.Format(_T(":%s!"), lv_Exception.what());
        MessageBox(lv_strException, _T("錯誤!"), MB_ICONERROR);   
        return;
    }
    return;
}

//************************************************

Opencv中的積分圖算法

 

積分圖算法由Crow在1984年首次提出,是爲了在多尺度透視投影中提高渲染速度。積分圖算法是一種快速計算圖像區域和以及圖像區域平方和的算法。它的核心思想就是對每一個圖像建立起自己的積分圖查找表,在圖像處理的階段就可以根據預先建立積分圖查找表直接查找從而實現對均值卷積的線性時間計算。做到了卷積執行的時間與窗口大小無關。這種算法被應用到基於NCC的快速匹配、對象檢測和SURF變換、邊緣檢測、基於統計學的快速濾波器等方面。

積分圖算法的缺點:1、需要較大的內存來存儲積分圖。2、如果圖片較大,會導致數據溢出。

  • 積分圖的建立

積分圖由原圖像計算而來,假設原圖大小爲W*H,則積分圖大小爲(W+1)*(H+1)。在積分圖(integral image)上任意座標(x,y)處的值,表示原圖中座標爲(x,y)的點的左上角所有像素點像素值的和(平方和表中的就是平方和)。和表和平方和表建立公式如下:

和表:

平方和表:

如下圖所示,假設輸入圖像大小爲2x2,則積分圖大小爲3x3

  • 積分圖的查找

如上圖所示,如果想求輸入圖像中藍色區域內的像素值之和(3+2+5+4=14),只要根據和表積分圖進行兩次減法和一次減法即可:46+10-22-20=14。也就是右下角+左上角-右上角-左下角。

  • Opencv實例

opencv中計算積分圖的API介紹:


 
  1. //API一

  2. void integral( InputArray src, //輸入圖像

  3. OutputArray sum, //和表

  4. int sdepth = -1,); //和表深度

  5.  
  6. //API二

  7. void integral( InputArray src, //輸入圖像

  8. OutputArray sum, //和表

  9. OutputArray sqsum, //平方和表

  10. int sdepth = -1, //和表深度,一般爲CV_32S

  11. int sqdepth = -1 ); //平方和表深度,一般爲CV_32F

計算積分圖並顯示:

                               


 
  1. //////////////////////////////////

  2. //opencv4.1.0

  3. //////////////////////////////////

  4.  
  5. #include <opencv2/opencv.hpp>

  6.  
  7. using namespace std;

  8. using namespace cv;

  9.  
  10. int main() {

  11. Mat src, sum, sqrsum;

  12. src = imread("1.png", 0);

  13.  
  14. integral(src, sum, sqrsum, CV_32S, CV_32F);

  15.  
  16. normalize(sum, sum, 0, 255, NORM_MINMAX, CV_8UC1, Mat());

  17. normalize(sqrsum, sqrsum, 0, 255, NORM_MINMAX, CV_8UC1, Mat());

  18.  
  19. imshow("原圖", src);

  20. imshow("和表積分圖", sum);

  21. imshow("平方和表積分圖", sqrsum);

  22.  
  23. waitKey(0);

  24. return 0;

  25. }

  • 獲取指定區域內像素值之和


 
  1.  
  2. //////////////////////////////////

  3. //opencv4.1.0

  4. //////////////////////////////////

  5.  
  6. #include <opencv2/opencv.hpp>

  7.  
  8. using namespace std;

  9. using namespace cv;

  10.  
  11. int get_block_sum(Mat &sum, int x1, int y1, int x2, int y2);

  12.  
  13. int main() {

  14. Mat src, sum, sqrsum;

  15. src = imread("1.png", 0);

  16.  
  17. //獲取ROI,便於觀察(使用ImageWatch插件)

  18. int LR = 4;

  19. int LC = 4;

  20. int width = 6;

  21. int height = 6;

  22. Mat roi = src(Rect(LR, LC, width, height)).clone();

  23.  
  24. //計算積分圖

  25. integral(src, sum, sqrsum, CV_32S, CV_32F);

  26.  
  27. //利用積分圖計算ROI內的像素值總和

  28. int value = get_block_sum(sum, LR, LC, LR + height, LC + width);

  29. cout << value << endl;

  30.  
  31. imshow("原圖", src);

  32. waitKey(0);

  33. return 0;

  34. }

  35.  
  36. int get_block_sum(Mat &sum, int x1, int y1, int x2, int y2) {

  37. int BottomRight = sum.at<int>(x2, y2);

  38. int TopLeft = sum.at<int>(x1, y1);

  39. int TopRight = sum.at<int>(x1, x2);

  40. int BottomLeft = sum.at<int>(x2, y1);

  41. int sum_value = (BottomRight + TopLeft - TopRight - BottomLeft);

  42. return sum_value;

  43. }

 

//********************************************

Paul Viola和Michael Jones在2001年首次將積分圖應用在圖像特徵提取上,在他們的論文“Rapid Object Detection using a Boosted Cascade of Simple Features”中,積分圖被當作一種新的圖像特徵表徵方式,可以把檢測的Haar特徵非常高效的計算出來,用於實時人臉檢測系統。

積分圖是一種能夠描述全局信息的矩陣表示方法,其構造方式是積分圖像上位置(i,j)處的值ii(i,j)是原圖像(i,j)左上角方向所有像素的和。

利用積分圖可以可以快速的計算圖像上某一區域內的像素和,如下圖:

要計算區域D內的像素和,只需要獲取到積分圖上1、2、3/4點各自的像素值,分別表述爲ii(1)、ii(2)、ii(3)、ii(4),則區域D的像素和=ii(4)+ii(1)-ii(2)-ii(3)。

 

傳統的計算像素和的方式需要遍歷區域D內所有的像素,再執行累加,計算量隨着區域D面積的增大而增大,而對積分圖方式來說,只需要在計算積分圖之後,通過簡單幾次加減運算就可以得到某一區域內“像素和”這一特徵,計算速度非常快,並且這種速度的提升效果隨着區域面積的增大和計算次數的增多表現的更爲明顯。

 

Opencv中使用integral函數計算積分圖。

 

 

void integral( InputArray src, OutputArray sum, int sdepth=-1 );


第一個參數src,可以使灰度圖或RGB彩色圖,單通道和三通道均可作爲輸入,但每個通道的精度必須是8位int或32位、64位浮點型;

 

第二個參數sum,積分圖,若輸入src是灰度圖,則積分圖也是灰度圖,若輸入src是RGB三通道圖,則積分圖sum也是RGB三通道彩色。sum的圖像深度是32位整型或32位、64位浮點型,這取決於第三個參數sdepth的定義;

第三個參數sdepth,定義積分圖的深度(depth),32位整型或者32位、64位浮點型。注意圖像的深度跟圖像的通道數是無關的,相關概念可以參看這裏:Opencv Mat矩陣中data、size、depth、elemSize、step等屬性的理解 ;

所以在使用sum之前,聲明即可,可以不事先定義Mat矩陣的大小和數據類型。

 

 

#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"

using namespace cv;

int main(int argc,char *argv[])
{
	Mat image=imread(argv[1]);	
	//cvtColor(image,image,CV_RGB2GRAY); //原圖像是三通道,積分圖也是三通道
	Mat imageIntegral;
	integral(image,imageIntegral,CV_32F); //計算積分圖
	normalize(imageIntegral,imageIntegral,0,255,CV_MINMAX);  //歸一化,方便顯示
	Mat imageIntegralNorm;
	convertScaleAbs(imageIntegral,imageIntegralNorm); //精度轉換爲8位int整型
	imshow("Source Image",image);
	imshow("Integral Image",imageIntegralNorm);
	waitKey();
}


 

 

原圖:

 

積分圖:

 

顯示的積分圖是經過歸一化後的。可以看到從左上角到右下角,圖像是越來越亮的,也就是說積分圖上像素值是越來越大的。

 

積分圖的圖像通道數跟原始圖像保存一致,當計算積分圖的原始圖像是彩色圖像時,積分圖也是彩色圖像,這時候積分圖計算的不是灰度,而是顏色:

//************************************

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章