在OpenCV濾波算法中,有兩個非常重要的基本工具函數,copyMakeBorder和borderInterpolate
copyMakeBorder
函數原型
void copyMakeBorder( const Mat& src, Mat& dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value=Scalar() );
源碼在utils.cpp中
功能
擴充src的邊緣,將圖像變大,然後以各種外插方式自動填充圖像邊界,這個函數實際上調用了函數cv::borderInterpolate,這個函數最重要的功能就是爲了處理邊界,比如均值濾波或者中值濾波中,使用copyMakeBorder將原圖稍微放大,然後我們就可以處理邊界的情況了
其中:
src,dst:原圖與目標圖像
top,bottom,left,right分別表示在原圖四周擴充邊緣的大小
borderType:擴充邊緣的類型,就是外插的類型,OpenCV中給出以下幾種方式
* BORDER_REPLICATE
* BORDER_REFLECT
* BORDER_REFLECT_101
* BORDER_WRAP
* BORDER_CONSTANT
實際中,還有其他的宏定義
//! various border interpolation methods
enum { BORDER_REPLICATE=IPL_BORDER_REPLICATE, BORDER_CONSTANT=IPL_BORDER_CONSTANT,
BORDER_REFLECT=IPL_BORDER_REFLECT, BORDER_WRAP=IPL_BORDER_WRAP,
BORDER_REFLECT_101=IPL_BORDER_REFLECT_101, BORDER_REFLECT101=BORDER_REFLECT_101,
BORDER_TRANSPARENT=IPL_BORDER_TRANSPARENT,
BORDER_DEFAULT=BORDER_REFLECT_101, BORDER_ISOLATED=16 };
這幾種方式到底什麼意思呢?
OpenCV給出瞭解釋:
代碼來自源碼:filter.cpp
/*
Various border types, image boundaries are denoted with '|'
* BORDER_REPLICATE: aaaaaa|abcdefgh|hhhhhhh
* BORDER_REFLECT: fedcba|abcdefgh|hgfedcb
* BORDER_REFLECT_101: gfedcb|abcdefgh|gfedcba
* BORDER_WRAP: cdefgh|abcdefgh|abcdefg
* BORDER_CONSTANT: iiiiii|abcdefgh|iiiiiii with some specified 'i'
*/
個人覺得OpenCV解釋的還是挺形象的
這裏我們重點看下面這非常常見的幾種
BORDER_REPLICATE:複製法,也就是複製最邊緣像素。
如上圖,紅色區域爲src的最邊界像素,藍色區域是擴充的邊界,我們將邊緣擴大了5個像素(right=5),藍色區域的寬度就是5,複製了5次紅色區域的值
這種方式也就是OpenCV中的中值濾波medianBlur採用的邊界處理方式
BORDER_REFLECT_101:對稱法,也就是以最邊緣像素爲軸,對稱。
下面我們看圖
綠色區域是src最邊界的像素,藍色區域是我們擴充的5個像素的擴充邊界,而紅色區域就是藍色區域在src的對稱部分
這種方式也是OpenCV邊界處理的默認方式(BORDER_DEFAULT=BORDER_REFLECT_101)
也是filter2D,blur,GaussianBlur,bilateralFilter的默認處理方式,所以這種方式在邊界處理中應用還是非常廣泛的
BORDER_CONSTANT:常量法。
常量法就是以一個常量像素值(由參數 value給定)填充擴充的邊界值,這種方式在仿射變換,透視變換中非常常見
如下圖,
我們使用了默認的value,黑色填充了邊界,所以紅色區域的擴充的5個像素寬的邊界是黑色的
在copyMakeBorder的內部,調用了函數borderInterpolate
borderInterpolate
函數原型
int borderInterpolate( int p, int len, int borderType );
源碼在filter.cpp中
功能
根據不同的外插方法(borderType),如 BORDER_REPLICATE,計算外插像素對應於原圖中的1D座標,一般不單獨使用,而在其他函數內部使用,如在copyMakeBorder中使用
其中
p:擴充邊緣的像素的座標(橫座標或者縱座標)
len:src對於p所在的維的大小
borderType:與函數copyMakeBorder中的意思一樣,這裏就不重複了
示例:
比如我們在X方向使用BORDER_WRAP邊界方式,Y方向採用BORDER_REFLECT_101方式,那麼計算擴充邊界像素Point(-5,100)對應原圖中的位置就是
float val = img.at<float>(borderInterpolate(100, img.rows, BORDER_REFLECT_101),
borderInterpolate(-5, img.cols, BORDER_WRAP));
之前的博客中的濾波算法,沒有采用上面的函數,其實,如果採用上面的函數,處理濾波將會非常方便,下面的幾篇博客中,我將會修改之前的算法,採用OpenCV提供的函數處理濾波