文章目录

积分图原理

第一个提出 Haar 特征快速计算方法的是 CVPR2001上 的那篇经典论文 [《Rapid object detection using a boosted cascade of simple features》] (http://www.cs.utexas.edu/~grauman/courses/spring2007/395T/papers/viola_cvpr2001.pdf), Viola 提出了一种利用积分图(integral image)快速计算 Haar 特征的方法, 这个方法使得图像的局部矩形求和运算的复杂度从 ​​O(MN)​​​ 下降到了 ​​O(4)​​ 。

void GetGrayIntegralImage(unsigned char *Src, int *Integral, int Width, int Height, int Stride)
{

memset(Integral, 0, (Width + 1) * sizeof(int)); // 第一行都为0
for (int Y = 0; Y < Height; Y++)
{
unsigned char *LinePS = Src + Y * Stride;
int *LinePL = Integral + Y * (Width + 1) + 1; // 上一行位置
int *LinePD = Integral + (Y + 1) * (Width + 1) + 1; // 当前位置,注意每行的第一列的值都为0
LinePD[-1] = 0; // 第一列的值为0
for (int X = 0, Sum = 0; X < Width; X++)
{
Sum += LinePS[X]; // 行方向累加
LinePD[X] = LinePL[X] + Sum; // 更新积分图
}
}
}

void GetRGBIntegralImage(unsigned char *Src, int *Integral, int Width, int Height, int Stride)
{
const int Channel = 3;
memset(Integral, 0, (Width + 1) * Channel * sizeof(int));
for (int Y = 0; Y < Height; Y++)
{
unsigned char *LinePS = Src + Y * Stride;
int *LinePL = Integral + Y * (Width + 1) * Channel + Channel;
int *LinePD = Integral + (Y + 1) * (Width + 1) * Channel + Channel;
LinePD[-3] = 0; LinePD[-2] = 0; LinePD[-1] = 0;
for (int X = 0, SumB = 0, SumG = 0, SumR = 0; X < Width; X++)
{
SumB += LinePS[0];
SumG += LinePS[1];
SumR += LinePS[2];
LinePD[0] = LinePL[0] + SumB;
LinePD[1] = LinePL[1] + SumG;
LinePD[2] = LinePL[2] + SumR;
LinePS += Channel;
LinePL += Channel;
LinePD += Channel;
}
}
}



void BoxBlur(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride, int Radius)
{
int Channel = Stride / Width;
int *Integral = (int *)malloc((Width + 1) * (Height + 1) * Channel * sizeof(int));
if (Channel == 1)
{
GetGrayIntegralImage(Src, Integral, Width, Height, Stride);
#pragma
for (int Y = 0; Y < Height; Y++)
{
int Y1 = max(Y - Radius, 0);
int Y2 = min(Y + Radius + 1, Height);
// int Y1 = Y - Radius;
// int Y2 = Y + Radius + 1;
// if (Y1 < 0) Y1 = 0;
// if (Y2 > Height) Y2 = Height;
int *LineP1 = Integral + Y1 * (Width + 1);
int *LineP2 = Integral + Y2 * (Width + 1);
unsigned char *LinePD = Dest + Y * Stride;
for (int X = 0; X < Width; X++)
{
int X1 = max(X - Radius, 0);
int X2 = min(X + Radius + 1, Width);
// int X1 = X - Radius;
// if (X1 < 0) X1 = 0;
// int X2 = X + Radius + 1;
// if (X2 > Width) X2 = Width;
int Sum = LineP2[X2] - LineP1[X2] - LineP2[X1] + LineP1[X1];
int PixelCount = (X2 - X1) * (Y2 - Y1); // 有效的像素数
LinePD[X] = (Sum + (PixelCount >> 1)) / PixelCount; // 四舍五入
}
}
}
else if (Channel == 3)
{
GetRGBIntegralImage(Src, Integral, Width, Height, Stride);
#pragma
for (int Y = 0; Y < Height; Y++)
{
int Y1 = max(Y - Radius, 0);
int Y2 = min(Y + Radius + 1, Height);
int *LineP1 = Integral + Y1 * (Width + 1) * 3;
int *LineP2 = Integral + Y2 * (Width + 1) * 3;
unsigned char *LinePD = Dest + Y * Stride;
for (int X = 0; X < Width; X++)
{
int X1 = max(X - Radius, 0);
int X2 = min(X + Radius + 1, Width);
int Index1 = X1 * 3;
int Index2 = X2 * 3;
int SumB = LineP2[Index2 + 0] - LineP1[Index2 + 0] - LineP2[Index1 + 0] + LineP1[Index1 + 0];
int SumG = LineP2[Index2 + 1] - LineP1[Index2 + 1] - LineP2[Index1 + 1] + LineP1[Index1 + 1];
int SumR = LineP2[Index2 + 2] - LineP1[Index2 + 2] - LineP2[Index1 + 2] + LineP1[Index1 + 2];
int PixelCount = (X2 - X1) * (Y2 - Y1);
LinePD[0] = (SumB + (PixelCount >> 1)) / PixelCount;
LinePD[1] = (SumG + (PixelCount >> 1)) / PixelCount;
LinePD[2] = (SumR + (PixelCount >> 1)) / PixelCount;
LinePD += 3;
}
}
}
free(Integral);
}

效果演示

python演示:

import cv2


def integral_rgb(img_path):
image = cv2.imread(img_path)

image_integral = cv2.integral(image, cv2.CV_32F)
cv2.normalize(image_integral, image_integral, 0, 255, cv2.NORM_MINMAX, -1)
image_integral_norm = cv2.convertScaleAbs(image_integral)

cv2.imwrite("./image.png", image)
cv2.imwrite("./image_integral_norm.png", image_integral_norm)
cv2.imshow("image", image)
cv2.imshow("image_integral_norm", image_integral_norm)
cv2.waitKey(0)
cv2.destroyAllWindows()


def integral_gray(img_path):
image = cv2.imread(img_path, 0)

image_integral = cv2.integral(image, cv2.CV_32F)
cv2.normalize(image_integral, image_integral, 0, 255, cv2.NORM_MINMAX, -1)
image_integral_norm = cv2.convertScaleAbs(image_integral)

cv2.imwrite("./gray_image.png", image)
cv2.imwrite("./gray_image_integral_norm.png", image_integral_norm)
cv2.imshow("gray_image", image)
cv2.imshow("gray_image_integral_norm", image_integral_norm)
cv2.waitKey(0)
cv2.destroyAllWindows()


if __name__ == '__main__':
img_path = r'C:\Users\xxx\Desktop\test.png'
integral_rgb(img_path)
integral_gray(img_path)

RGB图:

OpenCV—python 图像积分图_opencv

灰度图:

OpenCV—python 图像积分图_python_02