1.快速均值滤波
1.1.实验过程中遇到的问题和解决方法
-
问题一:
- 问题:在使用积分图求快速均值滤波的时候,左上角的坐标应该是滤波核左上角位置x , y坐标都减去1。没减1导致图像有彩色边缘
- 解决:如问题所述
-
问题二:
2.结论分析与体会
2.1.流程介绍
代码写法有意避免if 的使用,这样运行速度快一点。
-
为图像加 padding。这里需要注意的是,滤波核的尺寸需要是奇数,因为一般将当前点作为滤波核的中心,如果是偶数的话就没有中心 if(windowSize % 2 == 0) windowSize += 1;
Mat padding_image;
int padding = windowSize / 2;
cv::copyMakeBorder(source_image , padding_image , padding , padding , padding , padding , cv::BORDER_DEFAULT);
-
计算积分图。需要注意的是,需要在加了 padding 之后的图像上计算积分图。因为计算的时候使用了前缀和,因此保留了一个守卫节点,积分图索引从 1 开始存储。
for(int i = 1 ; i <= padding_image.rows ; i++){ // 计算并保存积分图在 sum2d 中,因为使用前缀和,因此保留一个守卫节点,这里从1开始存储
init();
for(int j = 1 ; j <= padding_image.cols ; j++){
for(int c = 0 ; c < 3 ; c++){
sum2d[i][j][c] = 0;
sum_row[c] += padding_image.at<Vec3b>(i - 1 , j - 1)[c];//计算当前行的sum
sum2d[i][j][c] += sum2d[i - 1][j][c] + sum_row[c];//从1开始存,否则i-1越界
}
}
}
- 均值滤波。通过遍历原图,将原图像的每个像素点都作为滤波核的中心,计算滤波核内像素的平均值来代替当前像素的像素值。需要注意的是,积分图是在padding后的图像上计算的,所以原图像的
[x,y] 像素对应的是积分图中的 [x+padding,y+padding] 像素。因此,需要将所有坐标都加上 padding 。
db calculate(int x , int y , int padding , int c){
int Z = (2*padding + 1) * (2*padding + 1); // 窗口内像素个数
ll temp = sum2d[x + 2 * padding + 1][y + 2 * padding + 1][c] + sum2d[x][y][c] // sum是在padding后图像上计算的,因此原图的索引在sum上需要加上一个padding
- sum2d[x + 2 * padding + 1][y][c] - sum2d[x][y + 2 * padding + 1][c]; // 窗口内的和
return (temp * 1.0) / (Z * 1.0); // 计算均值
}
2.2.结果展示
本次实验算法计算积分图的过程中,又开了一个前缀和数组,用来计算当前行的当前位置及之前的数值和
for(int i = 1 ; i <= padding_image.rows ; i++){
init();
for(int j = 1 ; j <= padding_image.cols ; j++){
for(int c = 0 ; c < 3 ; c++){
sum2d[i][j][c] = 0;
sum_row[c] += padding_image.at<Vec3b>(i - 1 , j - 1)[c];//计算当前行的sum
sum2d[i][j][c] += sum2d[i - 1][j][c] + sum_row[c];
}
}
}
左图为原始图像,中间的图为算法实现的变换图像,右边的图为Opencv API 实现的变换图像
2.3.运行时间分析
- boxFilter() 算法利用了加法的行列可分离行,复杂度为 windowSize * Width * Height
紫色的框为卷积核大小,往右平推,算每一列的和(每次只需要多算一列)。下面的红色点为算出的结果,然后在对红色点求和,得到总的和。
算完之后直接找就行了,不用经过额外运算
?
S
(
u
,
v
)
=
S
(
u
,
v
?
1
)
+
sum
?
(
I
[
1
:
u
,
v
]
)
S(u, v)=S(u, v-1)+\operatorname{sum}(I[1: u, v])
S(u,v)=S(u,v?1)+sum(I[1:u,v])
这里可以看到,积分图的加法除了对数据本身求和之外,还要加上前一次的值,分析一下计算紫色框内的值要进行多少次加法
假如是一个 5 * 5 的框,需要计算 5 * 5 + 5 * 5 - 1 = 49 次。或者是 50次
boxFilter 需要计算 5 * 5 + 4 = 29次。基本上为积分图的一半。
而且在计算完和之后,积分图还要利用以下公式来求解框内的数据和,boxFilter 则是可以直接访问,因为boxFilter 上一步的求和结果就是框内数据和。所以积分图这里又比boxFilter 多进行运算。所以boxFilter 更快
源码地址:Computer-Vision/3.2.cpp at main · SDU-NSY/Computer-Vision (github.com)
|