編程環境:
VS + OpenCV + C++
完整代碼已經更新至GitHub,歡迎fork~GitHub鏈接
聲明:創作不易,未經授權不得復制轉載
statement:No reprinting without authorization
內容:
1. 實現圖像的高斯濾波:
-通過調整高斯函數的標準差(sigma)來控制平滑程度;
-void Gaussian(const MyImage &input, MyImage &output, double sigma);
-濾波窗口大小取為[6*sigma-1],[.]表示取整;
-利用二維高斯函數的行列可分離性進行加速;
-先對每行進行一維高斯濾波,再對結果的每列進行同樣的一維高斯濾波;
2. 實現快速均值濾波
-實現圖像的均值濾波;
-濾波窗口大小通過參數來指定;
-void MeanFilter(const MyImage &input, MyImage &output, int window_size);
-采用積分圖進行加速,實現與濾波窗口大小無關的效率;
問題一:卷積核的大小問題:
通過創建進度條來動態的交互更改核的大小,其中高斯采用[6sigma-1]進度條值為sigma0.01,為保證程序能正常健壯執行,對于計算出的核大小需要檢查是否太小或為偶數:
int ksize = sigma * 6 - 1;
if (ksize < 3) {
cout << "ksize is too small!error" << endl;
return;
}
int iRes = ksize % 2;
if (iRes == 0) {
ksize += 1;
}
問題二:邊緣處理
實用高斯和均值時都需考慮進行填充后才能進行濾波
常見的填充方式有三種:對稱復制填充、最邊緣復制填充、常量填充:
可以使用opencv內建的copyMakeBorder函數實現填充
不同填充效果如下;
結合效果發現對稱填充對高斯和均值濾波有更好的效果,也更符合客觀事實。
問題三:行列可分離的具體實現
描述:由于高斯函數可以寫成可分離的形式,因此可以采用可分離濾波器實現來加速。所謂的可分離濾波器,就是可以把多維的卷積化成多個一維卷積。具體到二維的高斯濾波,就是指先對行做一維卷積,再對列做一維卷積。這樣就可以將計算復雜度從O(MMNN)降到O(2MMN),M,N分別是圖像和濾波器的窗口大小。 這樣分解開來,算法的時間復雜度為O(ksize) ,運算量和濾波器的模板尺寸呈線性增長。
得到高斯濾波的一維數組,注意需要進行歸一化:
int half = ksize / 2;
//初始化濾波核
for (int i = 0; i < ksize; i++)
{
// 只需計算指數部分,高斯函數前的常數可以不用計算,會在歸一化的過程中給消去
//以(half,half)為中心建立坐標系進行計算
double g = exp(-(i - half) * (i - half) / (2 * sigma * sigma));
sum += g;
GS_filter[i] = g;
}
// 歸一化
for (int i = 0; i < ksize; i++)
GS_filter[i] /= sum;
并且需要對單通道和三通道圖像分別進行處理:
問題四:積分圖的算法實現問題
1、采用一維數組int * integral來存儲像素的積分,計算積分時注意可以使用迭代來降低復雜度,如下圖:
????Integral(i,j) = Integral(i,j-1) + prow(j)
????其中prow(j)為當前位置上的列的像素之和。
2、注意數組的大小由傳統的W * H改為 (W + 1) * (H + 1)會進一步使算法簡便,
使某個點的積分圖反映的是原圖中此位置左上角所有像素之和,這里是的累加和是不包括這個點像素本身的??梢院喕惴?,在計算時可以不用判斷是否為原圖的邊界像素,如下:
for (int yi = 1; yi < height+1; ++yi, Integral += 3 * (width + 1)) {
//對第一列像素值單獨處理
Integral[0] = 0;
Integral[1] = 0;
Integral[2] = 0;
Vec3b rgb = src.at<Vec3b>(yi-1, 0);
prow[0] += rgb[0];
prow[1] += rgb[1];
prow[2] += rgb[2];
Integral[3] = prow[0];
Integral[4] = prow[1];
Integral[5] = prow[2];
for (int xi = 2; xi < width+1; ++xi)
{
rgb = src.at<Vec3b>(yi-1, xi-1);
prow[3*(xi-1)+0] += rgb[0];
prow[3*(xi-1)+1] += rgb[1];
prow[3*(xi-1)+2] += rgb[2];
Integral[3 * xi + 0] = Integral[3 * (xi - 1) + 0] + prow[3 * (xi-1) + 0];
Integral[3 * xi + 1] = Integral[3 * (xi - 1) + 1] + prow[3 * (xi-1) + 1];
Integral[3 * xi + 2] = Integral[3 * (xi - 1) + 2] + prow[3 * (xi-1) + 2];
}
}
查閱資料,發現opencv中的內建函數也是這樣定義的:
最終實驗效果如下:
小結
圖像的濾波操作應該是圖像處理的基本操作,很多的圖像識別與分析算法都是以基本的卷積核為核心,所以掌握圖像基本卷積濾波操作還是很重要的。