积分图
原理
快速计算图像区域和以及图像区域平方和
上述原理用公式表示为:
I
(
x
,
y
)
=
I
(
x
−
1
,
y
)
+
I
(
x
,
y
−
1
)
−
I
(
x
−
1
,
y
−
1
)
+
p
i
x
e
l
(
x
,
y
)
I(x,y) = I(x-1,y) + I(x,y-1) - I(x-1,y-1) + pixel(x,y)
I(x,y)=I(x−1,y)+I(x,y−1)−I(x−1,y−1)+pixel(x,y)
此外还需要考虑边界问题,也就是第一行和第一列的计算。
对于第一行:
I
(
0
,
0
)
=
p
i
x
e
l
(
0
,
0
)
,
x
=
0
,
y
=
0
I
(
x
,
0
)
=
I
(
x
−
1
,
0
)
+
p
i
x
e
l
(
x
,
0
)
,
x
>
0
,
y
=
0
I(0,0) = pixel(0,0),x=0,y=0 I(x,0) = I(x-1,0) + pixel(x,0),x>0,y=0
I(0,0)=pixel(0,0),x=0,y=0I(x,0)=I(x−1,0)+pixel(x,0),x>0,y=0
对于第一列:
I
(
0
,
y
)
=
I
(
0
,
y
−
1
)
+
p
i
x
e
l
(
0
,
y
)
,
x
=
0
,
y
>
0
I(0,y) = I(0,y-1) + pixel(0,y),x=0,y>0
I(0,y)=I(0,y−1)+pixel(0,y),x=0,y>0
代码实现
积分图
//实现积分图
cv::Mat myIntegral( cv::Mat & src)
{
cv::Mat dst;
dst = cv::Mat::zeros(src.size(), src.type());//创建一个空矩阵
for (int y = 0; y < src.rows; ++y) {
for (int x = 0; x < src.cols; ++x) {
for (int c = 0; c < 3; ++c) {
int sum = src.at<cv::Vec3b>(y, x)[c];
if (y > 0) {
sum += dst.at<cv::Vec3b>(y - 1, x)[c];
}
if (x > 0) {
sum += dst.at<cv::Vec3b>(y, x - 1)[c];
}
if (y > 0 && x > 0) {
sum -= dst.at<cv::Vec3b>(y - 1, x - 1)[c];
}
dst.at<cv::Vec3b>(y, x)[c] = sum;
}
}
}
return dst;
}
积分图均值平滑
cv::Mat smoothBymyIntegral( cv::Mat& src, int windowSize) {
CV_Assert(src.type() == CV_8UC3 && windowSize % 2 == 1);
cv::Mat integralImg;
cv::integral(src, integralImg, CV_32S);
cv::Mat dst = src.clone();
int halfWindowSize = windowSize / 2;
for (int y = 0; y < src.rows; ++y) {
for (int x = 0; x < src.cols; ++x) {
int x1 = std::max(x - halfWindowSize, 0);
int y1 = std::max(y - halfWindowSize, 0);
int x2 = std::min(x + halfWindowSize + 1, src.cols);
int y2 = std::min(y + halfWindowSize + 1, src.rows);
for (int c = 0; c < 3; ++c) {
int sum = integralImg.at<cv::Vec3i>(y2, x2)[c] - integralImg.at<cv::Vec3i>(y1, x2)[c] - integralImg.at<cv::Vec3i>(y2, x1)[c] + integralImg.at<cv::Vec3i>(y1, x1)[c];
int area = (x2 - x1) * (y2 - y1);
dst.at<cv::Vec3b>(y, x)[c] = static_cast<uchar>(sum / area);
}
}
}
return dst;
}
均值平滑
//均值平滑函数
Mat smoothImage(const Mat& inputImage, int Sx, int Sy)
{
Mat smoothed;
inputImage.copyTo(smoothed);
int channels = smoothed.channels();
int width = smoothed.cols;
int height = smoothed.rows;
// 遍历图像像素
for (int i = Sy; i < height - Sy; i++)
{
for (int j = Sx; j < width - Sx; j++)
{
// 计算窗口内像素的平均灰度
double sum0 = 0;
double sum1 = 0;
double sum2 = 0;
for (int m = -Sy; m <= Sy; m++)
{
for (int n = -Sx; n <= Sx; n++)
{
if (channels == 1)
sum1 += smoothed.at<uchar>(i + m, j + n);
else if (channels == 3)
sum0 += smoothed.at<Vec3b>(i + m, j + n)[0];
sum1 += smoothed.at<Vec3b>(i + m, j + n)[1];
sum2 += smoothed.at<Vec3b>(i + m, j + n)[2];
}
}
// 将平均灰度赋给中心像素
if (channels == 1)
smoothed.at<uchar>(i, j) = static_cast<uchar>(sum1 / ((2 * Sx + 1) * (2 * Sy + 1)));
else if (channels == 3)
{
Vec3b& pixel = smoothed.at<Vec3b>(i, j);
pixel[0] = static_cast<uchar>(sum0/ ((2 * Sx + 1) * (2 * Sy + 1)));
pixel[1] = static_cast<uchar>(sum1 / ((2 * Sx + 1) * (2 * Sy + 1)));
pixel[2] = static_cast<uchar>(sum2 / ((2 * Sx + 1) * (2 * Sy + 1)));
}
}
}
return smoothed;
}
实验结果
原图
积分图均值平滑
5*5模板
积分图
积分图平滑函数
均值平滑
opencv自带平滑函数
自己实现平滑函数
运行时间比较
方法 | 运行时间 单位:s | |
---|---|---|
5*5 | 11*11 | |
opencv自带 | 0.0003209 | 0.0002521 |
自己实现均值滤波器 | 0.0024135 | 0.0098366 |
积分图加速均值滤波器 | 0.0015164 | 0.001455 |
结论:积分图实现加快滤波器执行速度