均值滤波是一种非常常见的滤波方法,本身算法难度并不大,但是想当然的算法复杂度都是width*height*size的复杂度。下面我试着写了下一个height*width复杂度算法
这个快速算法的精髓是采用一个数组代替滑动窗口。
以5*5的图像进行3*3的均值滤波为例。(height=width=5;size=3)
首先创建一个列和数组cur_rowSum,初始化为前size(3)行像素的列和。
0 | 5 | 7 | 9 | 0 |
(1) 列和数组为1*width
0 | 0 | 0 | 0 | 0 |
0 | 1 | 2 | 3 | 0 |
0 | 4 | 5 | 6 | 0 |
0 | 7 | 8 | 9 | 0 |
0 | 0 | 0 | 0 | 0 |
(2) 原始图像,周围pad为0
下面进行第一行的计算:
计算第一个像素的值,P=cur_rowSum[0..size-1],之后下一个像素的值刚好为p-最左一列的列和值加上最右一列的列和值。
0 | 5 | 7 | 9 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 12 | 2 | 3 | 0 |
0 | 4 | 5 | 6 | 0 |
0 | 7 | 8 | 9 | 0 |
0 | 0 | 0 | 0 | 0 |
由于在计算下一行的像素值时,也需要cur_rowSum。因此在第一行计算列和的时候同时计算下一个”cur_rowSum”,为了确保不影响本行的计算,因此采用另外一个数组存储。
计算时的方式为一个列和减去所在列的第一个像素,加上所在列的下一行像素。
0 | 12 | 7 | 9 | 0 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 2 | 3 | 0 |
0 | 4 | 5 | 6 | 0 |
0 | 7 | 8 | 9 | 0 |
0 | 0 | 0 | 0 | 0 |
如图,12=5-0+7;
如此进行循环进行,直到计算完毕,期间注意不要数组越界。
下面是代码。
#include<iostream>
#include<vector>
using namespace std;
void avg_filter(int *src_pad,int height,int width,int size=3){
int *src_pad1=src_pad;
int *temp_src=src_pad1;
vector<int> cur_sumRow(width,0);
//初始化cur_sumRow为前size行元素的和,也就列和。
for(int i=0;i<size;i++){
for(int j=0;j<width;j++){
cur_sumRow[j]+=*temp_src;
//假设src为按行存储。
temp_src++;
}
}
/*
*假设src为pad后的矩阵,一般pad时,奇数扩展size-1行/列,偶数扩展size行
*/
int src_height=height-size/2*2;
int src_width=width-size/2*2;
//*生成原始图像大小的矩阵。
int *result=new int[src_height*src_width];
int *temp_pad=src_pad+size*width;
int ind=0;
//由于移动中需要更新列和数组的临时变量
vector<int> temp_sumRow(width,0);
for(int i=size/2;i<height-size/2;i++){
int temp_sum=0;
for(int cur_i=0;cur_i<width;cur_i++){
temp_sumRow[cur_i]=cur_sumRow[cur_i];
}
for(int cur_i=0;cur_i<size;cur_i++){
temp_sum+=temp_sumRow[cur_i];
}
for(int j=0;j<width;j++){
if(j>=size/2&&j<width-size/2){
result[ind]=temp_sum;
ind++;
//减去最左列的值,加上最右列的值。
temp_sum-=temp_sumRow[j-size/2];
if(j+size/2+1<width){
if(size%2!=0){
temp_sum+=temp_sumRow[j+size/2+1];
}else{
temp_sum+=temp_sumRow[j+size/2];
}
}
}
//减去最上一行的值,加上下一行的值。由于是一维存储,因此用两个指针快速访问
cur_sumRow[j]-=*src_pad;
src_pad++;
//最后一行不计算。切会发生越界
if(i!=height-size/2-1){
cur_sumRow[j]+=*temp_pad;
temp_pad++;
}
}
//如果pad的为同一个数,不用考虑两端的列和变化、
}
for(int i=0;i<9;i++){
cout<<result[i]<<" ";
if((i+1)%3==0)
cout<<endl;
}
}
int main(){
int src[25]={
0, 0, 0, 0, 0,
0, 1, 2, 3, 0,
0, 4, 5, 6, 0,
0, 7, 8, 9, 0,
0, 0, 0, 0, 0
};
avg_filter(src,5,5,3);
}