1算法
C=conv2(A,B,shape); %卷積濾波
參數說明:
A:輸入圖像
B:卷積核
shape的可選值爲full、same、valid。
1)當shape=full時,返回所有二維卷積結果,即返回B的大小爲(ma+mb-1)x(na+nb-1)。
2)shape=same時,返回與A一樣大小的卷積中心部分。
3)shape=valid時,不考慮邊界補零,即只要有邊界補出的零參與運算的都捨去,返回B的大小爲(ma-mb+1)x(na-nb+1)。
上面敘述看起來略有些抽象,能夠結合下面的圖一塊兒理解。
3種參數的區別見圖。
函數
B = filter2(h,A,shape) ; %相關(correlation)濾波
這裏須要明晰一下卷積和相關的區別,卷積操做就是先將卷積核旋轉180度而後再和核大小相同的區域內的各元素對應相乘再相加。也就是核旋轉180後卷積。而相關操做就是直接用核卷積,不作旋轉。注意,我在敘述的時候,把2個區域對應元素相乘再相加這一個操做稱爲卷積。
參數說明:
A:輸入圖像,h:相關核
假設輸入圖像A大小爲ma x na,相關核h大小爲mb x nb。
1)當shape=full時,返回所有二維卷積結果,即返回B的大小爲(ma+mb-1)x(na+nb-1)
2)shape=same時,返回與A一樣大小的卷積中心部分。
3)shape=valid時,不考慮邊界補零,即只要有邊界補出的零參與運算的都捨去,返回B的大小爲(ma-mb+1)x(na-nb+1)。spa
imfilter函數可以實現3通道的RGB圖像和單通道的濾波器的卷積,而且返回的圖像也是3通道的。
B=imfilter(A,H,option1,option2,option3);
參數說明:
A:輸入圖像,H:濾波核
1)option1:邊界選項,可選的有:補充固定的值X(默認都補零),symmetric,replicate,circular
2)option2:輸出圖像大小選項,可選的有same(默認),full
3)option3:決定採用與filter2相同的相關濾波仍是與conv2相同的卷積濾波code
1)filter2輸入類型無所謂,輸出是double的,輸入在邊界老是補零(zero padded), 不支持其餘的邊界補充選項。
2)conv2的輸入只能爲double型的,不然會提示警告,輸出也是double型的。
2) imfilter:不必定要將輸入轉換爲double,可是輸出只與輸入同類型,有靈活的邊界補充選項。有symmetric、replicate,same等。
在適用圖像的維度上,imfilter可進行多維圖像(RGB等)進行空間濾波,filter2 只能對二維圖像(灰度圖)進行空間濾波,conv2能夠對圖像矩陣實現本身想實現的卷積操做,最簡單最經常使用的是二維。因此conv2和filter2相似,多維圖像(RGB等)要用imfilter。blog
【2】接口
二維卷積的算法原理比較簡單,參考隨意一本數字信號處理的書籍,而matlab的conv2函數的濾波有個形狀參數,用如下的一張圖很是能說明問題:圖片
這裏給出一種最原始的實現方案。這樣的實現對於數據矩陣大小爲1000x1000,卷積核矩陣大小爲20x20,在個人機器上需要大約1秒鐘的時間。而matlab採用的MKL庫最快僅僅需要將近0.1s的時間。ci
如下的代碼用到了本身眼下開發的FastIV中的一些函數接口。詳細代碼例如如下:開發
#include "fiv_core.h" typedef enum{ FIV_CONV2_SHAPE_FULL, FIV_CONV2_SHAPE_SAME, FIV_CONV2_SHAPE_VALID }FIV_CONV_SHAPE; void fIv_conv2(fIvMat** dst_mat, fIvMat* src_mat, fIvMat* kernel_mat, FIV_CONV_SHAPE shape) { int src_row = src_mat->rows; int src_cols = src_mat->cols; int kernel_row = kernel_mat->rows; int kernel_cols = kernel_mat->cols; int dst_row = 0, dst_cols = 0, edge_row = 0, edge_cols = 0; int i,j, kernel_i,kernel_j,src_i,src_j; fIvMat* ptr_dst_mat = NULL; switch(shape){ case FIV_CONV2_SHAPE_FULL: dst_row = src_row + kernel_row - 1; dst_cols = src_cols + kernel_cols - 1; edge_row = kernel_row - 1; edge_cols = kernel_cols - 1; break; case FIV_CONV2_SHAPE_SAME: dst_row = src_row; dst_cols = src_cols; edge_row = (kernel_row - 1) / 2; edge_cols = (kernel_cols - 1) / 2; break; case FIV_CONV2_SHAPE_VALID: dst_row = src_row - kernel_row + 1; dst_cols = src_cols - kernel_cols + 1; edge_row = edge_cols = 0; break; } ptr_dst_mat = fIv_create_mat(dst_row, dst_cols, FIV_64FC1); *dst_mat = ptr_dst_mat; for (i = 0; i < dst_row; i++) { ivf64* ptr_dst_line_i = (ivf64* )fIv_get_mat_data_at_row(ptr_dst_mat, i); for (j = 0; j < dst_cols; j++) { ivf64 sum = 0; kernel_i = kernel_row - 1 - FIV_MAX(0, edge_row - i); src_i = FIV_MAX(0, i - edge_row); for (; kernel_i >= 0 && src_i < src_row; kernel_i--, src_i++) { ivf64* ptr_src_line_i,*ptr_kernel_line_i; kernel_j = kernel_cols - 1 - FIV_MAX(0, edge_cols - j); src_j = FIV_MAX(0, j - edge_cols); ptr_src_line_i = (ivf64*)fIv_get_mat_data_at_row(src_mat, src_i); ptr_kernel_line_i = (ivf64*)fIv_get_mat_data_at_row(kernel_mat, kernel_i); ptr_src_line_i += src_j; ptr_kernel_line_i += kernel_j; for (; kernel_j >= 0 && src_j < src_cols; kernel_j--, src_j++){ sum += *ptr_src_line_i++ * *ptr_kernel_line_i--; } } ptr_dst_line_i[j] = sum; } } } FIV_ALIGNED(16) ivf64 ker_data[4*4] = {0.1,0.2,0.3,0.4, 0.5,0.6,0.7,0.8, 0.9,1.0,1.1,1.2, 1.3,1.4,1.5,1.6}; void test_conv2() { fIvMat* src_mat = fIv_create_mat_magic(8, FIV_64FC1); // 8x8 magic matrix fIvMat* kernel_mat = fIv_create_mat_header(4, 4, FIV_64FC1); fIvMat* dst_mat = NULL; fIv_set_mat_data(kernel_mat, ker_data, (sizeof(ivf64)) * 4 * 4); fIv_conv2(&dst_mat, src_mat, kernel_mat, FIV_CONV2_SHAPE_FULL); fIv_export_matrix_data_file(dst_mat,"dst_mat_4x4-full.txt", 1); fIv_release_mat(&src_mat); fIv_release_mat(&kernel_mat); fIv_release_mat(&dst_mat); } int main() { test_conv2(); return 0; }