1. 程式人生 > >opencv學習筆記(二十)cvFilter2D()卷積以及卷積邊界的處理

opencv學習筆記(二十)cvFilter2D()卷積以及卷積邊界的處理

20.1**cvFilter2D()卷積:**

void cvFilter2D(
const CvArr* src,
CvArr* dst,
const CvMat* kernel,
CvPoint anchor=cvPoint(-1,-1)
);
src
輸入影象

dst
輸出影象

kernel
卷積核, 單通道浮點矩陣。 如果想要應用不同的核於不同的通道,先用 cvSplit 函式分解影象到單個色彩通道上,然後單獨處理。

anchor
核的錨點表示一個被濾波的點在核內的位置。 錨點應該處於核內部。預設值 (-1,-1) 表示錨點在核中心。

這裡我們建立一個適當大小的矩陣,將係數連同源影象和目標影象一起傳遞給cvFilter2D()。我們還可以有選擇地輸人一個CvPoint指出核的中心位置,預設值(cvPoint (-1, -1))會設參考點為核的中心。如果定義了參考點,核的大小可以是偶數,否則只能是奇數。
源影象src和目標影象dst大小應該相同,有些人可能認為考慮到卷積核的額外的長和寬,源影象src應該大於目標影象dst。但是在OpenCV裡源影象src和目標影象dst的大小是可以一樣的,因為在預設情況下,在卷積之前,OpenCV通過複製源影象src的邊界建立了虛擬畫素,這樣以便於目標影象dst邊界的畫素可以被填充。
這裡我們所討論的卷積核的係數應該是浮點型別的,這就意味著我們必須用CV_32F來初始化矩陣。

20.2卷積模板及程式例項:

20.3OpenCV影象卷積(影象濾波)的程式程式碼:

#include "cv.h"
#include "highgui.h"
int main(int argc,char**argv) 
{ 
    IplImage* src, *dst;   
    float low[9] ={ 1.0/16, 2.0/16, 1.0/16, 2.0/16, 4.0/16, 2.0/16, 1.0/16, 2.0/16, 1.0/16 };//低通濾波核
    float high[9]={-1,-1,-1,-1,9,-1,-1,-1,-1};
    //高通濾波核——這個模板自己設,這裡用的是常用的核。
CvMat km = cvMat( 3, 3, CV_32FC1, low); //構造單通道浮點矩陣,將影象IplImage結構轉換為影象陣列 //上面這個矩陣就是在cvFilter2D裡面要用到的核 src = cvLoadImage( "b.jpg" ); dst = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 3 ); cvFilter2D( src, dst, &km, cvPoint( -1, -1 ) ); //設參考點為核的中心 cvNamedWindow( "src", CV_WINDOW_AUTOSIZE ); cvNamedWindow( "filtering", CV_WINDOW_AUTOSIZE ); cvShowImage( "src", src ); cvShowImage( "filtering", dst ); cvWaitKey(0); cvReleaseImage( &src ); cvReleaseImage( &dst ); return 0; }

20.4低通濾波和高通濾波:

低通濾波:邊緣平滑
高通濾波:邊緣提取與增強

濾波是訊號處理機影象處理中的一個基本操作。濾波去除影象中的噪聲,提取感興趣的特徵,允許影象重取樣。
影象中的頻域和空域:空間域指用影象的灰度值來描述一幅影象;而頻域指用影象灰度值的變化來描述一幅影象。而低通濾波器和高通濾波器的概念就是在頻域中產生的。
低通濾波器指去除影象中的高頻成分,而高通濾波器指去除影象中的低頻成分。

20.5卷積邊界

對於卷積,一個很自然的問題是如何處理卷積邊界。例如,在使用剛才所討論的卷積核時,當卷積點在影象邊界時會發生什麼?許多使用cvFilter2D()的OpenCV內建函式必須用各種方式來解決這個問題。同樣在做卷積時,有必要知道如何有效解決這個問題。
這個解決方法就是使用cvCopyMakeBorder()函式,它可以將特定的影象輕微變大,然後以各種方式自動填充影象邊界。
複製影象並且製作邊界。

20.5.1cvCopyMakeBorder()
定義:
void cvCopyMakeBorder(
const CvArr* src,
CvArr* dst,
CvPoint offset,
int bordertype,
CvScalar value=cvScalarAll(0)
);
引數:
src
輸入影象。

dst
輸出影象。(根據offset而相應改變大小)

offset
輸入影象(或者其ROI)欲拷貝到的輸出影象長方形的左上角座標(或者左下角座標,如果以左下角為原點)。長方形的尺寸要和原影象的尺寸的ROI分之一匹配。
——指的是輸出影象上指定哪個點為原點座標,拷貝影象。例子中選擇了cvPoint(5,5),cvPoint(25,25)這兩個點分別為影象的原點,則輸出影象要對應的擴大cvSize(img->width+10,img->height+10),cvSize(img->width+50,img->height+50)。
——這裡輸出影象至少要在邊界上加上原點座標值,當然最好是兩倍(不然就是隻有兩條邊會有邊框)。所以才是+10和50。

bordertype已拷貝的原影象長方形的邊界的型別:
IPL_BORDER_CONSTANT——填充邊界為固定值,值由函式最後一個引數指定。(預設黑色填充)
IPL_BORDER_REPLICATE——邊界用上下行或者左右列來複制填充。(其他兩種IPL邊界型別, IPL_BORDER_REFLECT 和IPL_BORDER_WRAP現已不支援)。

value如果邊界型別為IPL_BORDER_CONSTANT的話,那麼此為邊界畫素的值。

我們在前面已經提到,當呼叫OpenCV庫函式中的卷積功能時,cvCopyMakeBorder()函式就會被呼叫。在大多數情況下,邊界型別為
IPL_BORDER_REPLICATE,但有時並不希望用它。所以在另一種場合,可能用到cvCopyMakeBorder()。你可以建立一幅具有比想要得到的邊界稍微大一些的影象,無論呼叫任何常規操作,接下來就可以剪下到對源影象所感興趣的部分。這樣一來,OpenCV的自動加邊就不會影響所關心的畫素。

20.5.2程式例項

#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include <iostream>
int main(int argc,char** argv)
{
    IplImage* src=cvLoadImage("b.jpg");
    IplImage* dst1=cvCreateImage(cvSize(src->width+40,src->height+40),src->depth,src->nChannels);
    //因為cvPoint(20,20),所以長寬必須是+(20*2=)40
    IplImage* dst2=cvCreateImage(cvSize(src->width+40,src->height+40),src->depth,src->nChannels);
    cvZero(dst1);
    cvZero(dst2);
    cvCopyMakeBorder(src,dst1,cvPoint(20,20),IPL_BORDER_REPLICATE); //用邊界畫素的值填充
    cvCopyMakeBorder(src,dst2,cvPoint(20,20),IPL_BORDER_CONSTANT); //用黑色填充
    cvNamedWindow("dst1");
    cvNamedWindow("dst2");
    cvShowImage("src",src);
    cvShowImage("dst1",dst1);
    cvShowImage("dst2",dst2);
    cvWaitKey(0);
    cvDestroyWindow("src");
    cvDestroyWindow("dst1");
    cvDestroyWindow("dst2");
    cvReleaseImage(&src);
    cvReleaseImage(&dst1);
    cvReleaseImage(&dst2);
    return 0;
}