日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

cvMorphology形态学原理解析及源码分析

發(fā)布時(shí)間:2023/12/10 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cvMorphology形态学原理解析及源码分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

⑴?圖像形態(tài)學(xué)處理的概念...1

⑵?二值圖像的邏輯運(yùn)算...3

⑶?膨脹和腐蝕...4

(4)?高級(jí)形態(tài)學(xué)變換...8

(5)?細(xì)化...10

?

⑴?圖像形態(tài)學(xué)處理的概念

數(shù)字圖像處理中的形態(tài)學(xué)處理是指將數(shù)字形態(tài)學(xué)作為工具從圖像中提取對(duì)于表達(dá)和描繪區(qū)域形狀有用處的圖像分量,比如邊界、骨架以及凸殼,還包括用于預(yù)處理或后處理的形態(tài)學(xué)過(guò)濾、細(xì)化和修剪等。圖像形態(tài)學(xué)處理中我們感興趣的主要是二值圖像。

在二值圖像中,所有黑色像素的集合是圖像完整的形態(tài)學(xué)描述,二值圖像的各個(gè)分量是Z2的元素。假定二值圖像A和形態(tài)學(xué)處理的結(jié)構(gòu)元素B是定義在笛卡兒網(wǎng)格上的集合,網(wǎng)格中值為1的點(diǎn)是集合的元素,當(dāng)結(jié)構(gòu)元素的原點(diǎn)移到點(diǎn)(x,y)時(shí),記為Sxy,為簡(jiǎn)單起見(jiàn),結(jié)構(gòu)元素為3x3,且全都為1,在這種限制下,決定輸出結(jié)果的是邏輯運(yùn)算。

IplConvKernel結(jié)構(gòu)元素

typedef struct _IplConvKernel

{

? ? int ?nCols;?//結(jié)構(gòu)元素的行寬

? ? int ?nRows;?//列高

? ? int?anchorX; //結(jié)構(gòu)原點(diǎn)位置水平坐標(biāo)

? ? int?anchorY; //結(jié)構(gòu)原點(diǎn)位置垂直坐標(biāo)

int *values; //當(dāng)nShiftR為自定義時(shí),value是指向結(jié)構(gòu)元素?cái)?shù)據(jù)的指針

//如果結(jié)構(gòu)元素的大小定義為8*6,那么values為48長(zhǎng)的int數(shù)組,值為0或1。

? ? int?nShiftR;// 用于表示結(jié)構(gòu)元素的形狀類型

}IplConvKernel;

cvCreateStructuringElementEx創(chuàng)建結(jié)構(gòu)元素?

IplConvKernel* cvCreateStructuringElementEx( int cols, int rows, intanchor_x, int anchor_y,

? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ?? ??????????int shape, int*values=NULL );

cols 結(jié)構(gòu)元素的列數(shù)目 rows 結(jié)構(gòu)元素的行數(shù)目

anchor_x 錨點(diǎn)的相對(duì)水平偏移量 anchor_y 錨點(diǎn)的相對(duì)垂直偏移量?

shape 結(jié)構(gòu)元素的形狀,可以是下列值:?

CV_SHAPE_RECT, 長(zhǎng)方形元素;?

CV_SHAPE_CROSS, 十字交叉型,交錯(cuò)元素 a cross-shaped element;?

CV_SHAPE_ELLIPSE, 橢圓元素;?

CV_SHAPE_CUSTOM, 用戶自定義元素。這種情況下參數(shù) values 在封閉矩形內(nèi)定義核的形狀,即象素的那個(gè)鄰域必須考慮。

values 指向結(jié)構(gòu)元素的指針,它是一個(gè)平面數(shù)組,表示對(duì)元素矩陣逐行掃描。(非零點(diǎn)表示該點(diǎn)屬于結(jié)構(gòu)元)。如果指針為空,則表示平面數(shù)組中的所有元素都是非零的,即結(jié)構(gòu)元是一個(gè)長(zhǎng)方形(該參數(shù)僅僅當(dāng)shape參數(shù)是 CV_SHAPE_CUSTOM 時(shí)才予以考慮)。?

形態(tài)核與卷積核不同,不需要任何數(shù)值填充核。當(dāng)核在圖像上移動(dòng)時(shí),核的元素只需要簡(jiǎn)單表明應(yīng)該在哪個(gè)范圍內(nèi)計(jì)算最大值和最小值,參考點(diǎn)制定核與源圖像的位置關(guān)系,同時(shí)也鎖定了計(jì)算結(jié)果在目標(biāo)圖像中的位置。行和列確定了所構(gòu)造的矩形的大小(結(jié)構(gòu)元素在矩形內(nèi)),anchor_x和anchor_y是核的封閉矩形內(nèi)的參考點(diǎn)坐標(biāo)。

cvReleaseStructuringElement刪除結(jié)構(gòu)元素?

void cvReleaseStructuringElement( IplConvKernel** element );

element 被刪除的結(jié)構(gòu)元素的指針,函數(shù) cvReleaseStructuringElement 釋放結(jié)構(gòu) IplConvKernel 。如果 *element 為 NULL, 則函數(shù)不作用。

CV_IMPL IplConvKernel*

cvCreateStructuringElementEx( int cols, introws,

????????????????????????????? intanchorX, int anchorY,

????????????????????????????? intshape, int *values )

{

??? cv::Sizeksize = cv::Size(cols,rows);

??? cv::Pointanchor = cv::Point(anchorX,anchorY);

?????? // 檢測(cè)輸入數(shù)據(jù),當(dāng)用戶自定義的時(shí)候value不能為空,value默認(rèn)為NULL

??? CV_Assert(cols > 0 &&rows > 0 && anchor.inside(cv::Rect(0,0,cols,rows)) &&

?????????????? (shape!= CV_SHAPE_CUSTOM || values != 0));

?

??? int i, size = rows *cols;

??? int element_size = sizeof(IplConvKernel) +size*sizeof(int);

?????? // 為什么創(chuàng)建的內(nèi)存要比實(shí)際的大呢?大了size*sizeof(int)+32

??? IplConvKernel*element = (IplConvKernel*)cvAlloc(element_size+ 32);

?

??? element->nCols =cols;

??? element->nRows =rows;

??? element->anchorX =anchorX;

??? element->anchorY =anchorY;

//?? enum???? {??????????? CV_SHAPE_RECT????? =0,???????????? CV_SHAPE_CROSS???? =1,???????????

//????????? CV_SHAPE_ELLIPSE?? =2,???????????? CV_SHAPE_CUSTOM??? =100?????? };

??? element->nShiftR =shape< CV_SHAPE_ELLIPSE ?shape : CV_SHAPE_CUSTOM;

?????? // element指向結(jié)構(gòu)的首地址

??? element->values = (int*)(element + 1);

?????? // 如果為用戶自定義的類型,從values中取值

??? if( shape == CV_SHAPE_CUSTOM)

??? {

??????? for( i = 0; i < size; i++ )

??????????? element->values[i] =values[i];

??? }

??? else

??? {

????????????? // 根據(jù)不同的結(jié)構(gòu)類型獲得不同的數(shù)值

??????? cv::Matelem = cv::getStructuringElement(shape,ksize, anchor);

??????? for( i = 0; i < size; i++ )

??????????? element->values[i] =elem.data[i];

??? }

?

??? return element;

}

cv::Matcv::getStructuringElement(intshape, Sizeksize, Pointanchor)

{

??? int i, j;

??? int r = 0, c = 0;

??? double inv_r2 = 0;

??????

??? CV_Assert(shape ==MORPH_RECT|| shape ==MORPH_CROSS|| shape ==MORPH_ELLIPSE);

?????? //ifanchor.x=-1,anchor.x=ksize.width/2; if anchor.y=-1,anchor.y=ksize.height/2

?????? // 并判斷是否在rect(0, 0, ksize.width, ksize.height)內(nèi)

??? anchor =normalizeAnchor(anchor,ksize);

?????? // 當(dāng)只有一個(gè)結(jié)構(gòu)元素的時(shí)候cols=1 rows=1,長(zhǎng)方形結(jié)構(gòu)元素

??? if( ksize == Size(1,1))

??????? shape= MORPH_RECT;

?????? // 如果為橢圓形的結(jié)構(gòu)元素

??? if( shape == MORPH_ELLIPSE)

??? {

????????????? //r? c分別為橢圓的半徑

??????? r = ksize.height/2;

??????? c = ksize.width/2;

????????????? // 如果r!=0inv_r2=1/(r*r)

??????? inv_r2= r ? 1./((double)r*r) : 0;

??? }

??????

??? Mat elem(ksize, CV_8U);

?

??? for( i = 0; i < ksize.height; i++ )

??? {

??????? uchar*ptr =elem.data +i*elem.step;

??????? int j1 = 0, j2 = 0;

????????????? // 根據(jù)不同的類型得到不同的起始坐標(biāo)

??????? if( shape == MORPH_RECT|| (shape ==MORPH_CROSS&& i ==anchor.y) )

??????????? j2= ksize.width;

??????? else if( shape == MORPH_CROSS )

??????????? j1= anchor.x,j2 =j1 +1;

??????? else

??????? {

??????????? intdy =i - r;

??????????? if(std::abs(dy) <=r )

??????????? {

??????????????? intdx =saturate_cast<int>(c*std::sqrt((r*r - dy*dy)*inv_r2));

??????????????? j1= std::max(c -dx, 0);

??????????????? j2= std::min(c +dx +1, ksize.width);

??????????? }

??????? }

????????????? // 小于j1的賦,大于j1小于j2的賦,其余的賦0

??????? for( j = 0; j < j1; j++ )

??????????? ptr[j] = 0;

??????? for( ; j < j2; j++ )

??????????? ptr[j] = 1;

??????? for( ; j < ksize.width;j++ )

??????????? ptr[j] = 0;

??? }

?

??? return elem;

}

⑵?二值圖像的邏輯運(yùn)算

邏輯運(yùn)算盡管本質(zhì)上很簡(jiǎn)單,但對(duì)于實(shí)現(xiàn)以形態(tài)學(xué)為基礎(chǔ)的圖像處理算法是一種有力的補(bǔ)充手段。在圖像處理中用到的主要邏輯運(yùn)算是:與、或和非(求補(bǔ)),它們可以互相組合形成其他邏輯運(yùn)算。

⑶?膨脹和腐蝕

膨脹和腐蝕這兩種操作是形態(tài)學(xué)處理的基礎(chǔ),許多形態(tài)學(xué)算法都是以這兩種運(yùn)算為基礎(chǔ)的。

①?膨脹

結(jié)構(gòu)元素B可以看作一個(gè)卷積模板,區(qū)別在于膨脹是以集合運(yùn)算為基礎(chǔ)的,卷積是以算術(shù)運(yùn)算為基礎(chǔ)的,但兩者的處理過(guò)程是相似的。

⑴?用結(jié)構(gòu)元素B,掃描圖像A的每一個(gè)像素

⑵?用結(jié)構(gòu)元素與其覆蓋的二值圖像做“與”操作

⑶?如果都為0,結(jié)果圖像的該像素為0。否則為1

cvDilate使用任意結(jié)構(gòu)元素膨脹圖像?

voidcvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, intiterations=1 );

src輸入圖像. dst 輸出圖像. element 用于膨脹的結(jié)構(gòu)元素。

若為 NULL,則使用(1+iterations*2)*(1+iterations*2)的長(zhǎng)方形的結(jié)構(gòu)元素?.iterations膨脹的次數(shù)?

函數(shù) cvDilate 對(duì)輸入圖像使用指定的結(jié)構(gòu)元進(jìn)行膨脹,該結(jié)構(gòu)決定每個(gè)具有最小值象素點(diǎn)的鄰域形狀:?dst=dilate(src,element):?dst(x,y)=max((x',y') in element))src(x+x',y+y')

函數(shù)支持(in-place)模式。膨脹可以重復(fù)進(jìn)行 (iterations) 次. 對(duì)彩色圖像,每個(gè)彩色通道單獨(dú)處理。

在試圖找到連通分支(即具有相似的顏色或強(qiáng)度的像素點(diǎn)的大塊互相分離的區(qū)域)時(shí)通常使用膨脹操作。

CV_IMPL void

cvDilate( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element,int iterations)

{

??? cv::Matsrc = cv::cvarrToMat(srcarr),dst = cv::cvarrToMat(dstarr),kernel;

?????? // 輸入輸出必須是同等尺寸、同類型的

??? CV_Assert(src.size()==dst.size()&&src.type()==dst.type());

??? cv::Pointanchor;

?????? // 若沒(méi)有結(jié)構(gòu)元素輸入,kernel=NULLanchor=(1,1)

?????? // 否則將結(jié)構(gòu)元素中的值讀入kernelanchor

??? convertConvKernel(element,kernel,anchor );

?????? // 邊界差值方法采用邊界復(fù)制

??? cv::dilate(src, dst, kernel, anchor, iterations,cv::BORDER_REPLICATE);

}

static voidconvertConvKernel( constIplConvKernel*src,cv::Mat&dst,cv::Point& anchor)

{

?????? // 若沒(méi)有輸入結(jié)構(gòu)元素

??? if(!src)

??? {

??????? anchor= cv::Point(1,1);

??????? dst.release();

??????? return;

??? }

?????? // 獲取結(jié)構(gòu)原點(diǎn)的坐標(biāo)

??? anchor =cv::Point(src->anchorX,src->anchorY);

?????? // 讀取結(jié)構(gòu)元素的值

??? dst.create(src->nRows,src->nCols,CV_8U);

??? int i, size = src->nRows*src->nCols;

??? for( i = 0; i < size; i++ )

??????? dst.data[i] = (uchar)src->values[i];

}

void cv::dilate( InputArraysrc, OutputArraydst, InputArraykernel,

???????????????? Pointanchor,int iterations,

???????????????? intborderType,constScalar& borderValue)

{

??? morphOp(MORPH_DILATE,src,dst, kernel,anchor, iterations,borderType, borderValue);

}

static voidmorphOp( int op, InputArray _src, OutputArray_dst,

???????????????????? InputArray_kernel,

???????????????????? Pointanchor,int iterations,

???????????????????? intborderType,constScalar& borderValue)

{

??? Mat src = _src.getMat(),kernel= _kernel.getMat();

?????? // 如果輸入的時(shí)候不輸入kernel,則kernel.data=NULL,那么ksize=(3,3)

??? Size ksize = kernel.data ?kernel.size() :Size(3,3);

?????? // ifanchor.x=-1,anchor.x=ksize.width/2; if anchor.y=-1,anchor.y=ksize.height/2

?????? // 并判斷是否在rect(0, 0, ksize.width, ksize.height)內(nèi)

??? anchor =normalizeAnchor(anchor,ksize);

?????? // 這一句是多余的,因?yàn)樵谏厦?/span>normalizeAnchor已經(jīng)判斷了

??? CV_Assert(anchor.inside(Rect(0, 0,ksize.width,ksize.height)) );

?

??? _dst.create(src.size(),src.type() );

??? Mat dst = _dst.getMat();

?????? // 如果迭代步數(shù)為或者結(jié)構(gòu)元素的尺寸為,不進(jìn)行處理,直接輸出

??? if( iterations == 0 || kernel.rows*kernel.cols == 1 )

??? {

??????? src.copyTo(dst);

??????? return;

??? }

?????? // 如果沒(méi)有輸入結(jié)構(gòu)元素,那么創(chuàng)建(1+iterations*2)*(1+iterations*2)的長(zhǎng)方形結(jié)構(gòu)元素

?????? // 結(jié)構(gòu)元素的中心點(diǎn)為(iterations, iterations),并將迭代步數(shù)設(shè)置為

??? if( !kernel.data )

??? {

??????? kernel= getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));

??????? anchor= Point(iterations,iterations);

??????? iterations= 1;

??? }

?????? // 如果結(jié)構(gòu)步數(shù)大于的話并且kernel為長(zhǎng)方形的結(jié)構(gòu)元素,重新創(chuàng)建結(jié)構(gòu)元素

??? else if( iterations> 1 && countNonZero(kernel) == kernel.rows*kernel.cols )

??? {

??????? anchor= Point(anchor.x*iterations,anchor.y*iterations);

??????? kernel= getStructuringElement(MORPH_RECT,

?????????????????????????????????????? Size(ksize.width + (iterations-1)*(ksize.width-1),

??????????????????????????????????????????? ksize.height +(iterations-1)*(ksize.height-1)),

?????????????????????????????????????? anchor);

??????? iterations= 1;

??? }

?

int nStripes = 1;

// TegraNVIDIA公司于2008年推出的基于ARM構(gòu)架通用處理器品牌(即CPU,NVIDIA稱為“Computer on a chip”片上計(jì)算機(jī)),能夠?yàn)楸銛y設(shè)備提供高性能、低功耗體驗(yàn)。

#if definedHAVE_TEGRA_OPTIMIZATION

??? if (src.data != dst.data &&iterations == 1 &&? //NOTE:threads are not used for inplace processing

??????? (borderType & BORDER_ISOLATED) == 0&& //TODO: check border types

??????? src.rows >= 64 ) //NOTE: justheuristics

??????? nStripes = 4;

#endif

?

??? parallel_for_(Range(0,nStripes),

????????????????? MorphologyRunner(src,dst, nStripes,iterations,op,kernel,anchor,borderType,borderType,borderValue));

?

??? //Ptr<FilterEngine>f = createMorphologyFilter(op, src.type(),

??? //????????????????????????????????????????????kernel, anchor, borderType, borderType, borderValue );

?

??? //f->apply(src, dst );

??? //for( int i = 1;i < iterations; i++ )

??? //??? f->apply( dst, dst );

}

// 是否采用并行處理

void cv::parallel_for_(constcv::Range&range,constcv::ParallelLoopBody&body,doublenstripes=-1)

{

?????? // 大部分代碼省略,如果定義了并行框架,可以采用并行處理,一般不定義

??? (void)nstripes;

??? body(range);

}

class MorphologyRunner: public ParallelLoopBody

{

public:

??? MorphologyRunner(Mat_src, Mat _dst, int _nStripes,int _iterations,

???????????????????? int_op,Mat _kernel,Point _anchor,

???????????????????? int_rowBorderType,int_columnBorderType,constScalar& _borderValue):

??????? ????????????????????????? borderValue(_borderValue)

??? {

??????? src= _src;

??????? dst= _dst;

?

??????? nStripes= _nStripes;

??????? iterations= _iterations;

?

??????? op =_op;

??????? kernel= _kernel;

??????? anchor= _anchor;

??????? rowBorderType= _rowBorderType;

??????? columnBorderType= _columnBorderType;

??? }

?????? // ()操作符,最主要的運(yùn)算符號(hào)

??? void operator () ( const Range& range) const

??? {

??????? int row0 = min(cvRound(range.start *src.rows / nStripes),src.rows);

??????? int row1 = min(cvRound(range.end *src.rows / nStripes),src.rows);

?

??????? /*if(0)

??????????? printf("Size = (%d, %d),range[%d,%d), row0 = %d, row1 = %d\n",

?????????????????? src.rows, src.cols,range.start, range.end, row0, row1);*/

?

??????? Mat srcStripe = src.rowRange(row0,row1);

??????? Mat dstStripe = dst.rowRange(row0,row1);

????????????? ?// 創(chuàng)建形態(tài)學(xué)濾波器

??????? Ptr<FilterEngine>f= createMorphologyFilter(op,src.type(),kernel,anchor,

????????????????????????????????????????????????????rowBorderType,columnBorderType, borderValue);

????????????? // 主要的處理步驟在這里面,還未解讀

??????? f->apply(srcStripe,dstStripe );

??????? for( int i = 1; i < iterations;i++ )

??????????? f->apply(dstStripe,dstStripe );

??? }

?

private:

??? Mat src;

??? Mat dst;

??? int nStripes;

??? int iterations;

?

??? int op;

??? Mat kernel;

??? Point anchor;

??? int rowBorderType;

??? int columnBorderType;

??? Scalar borderValue;

};

②?腐蝕

對(duì)Z中的集合A和B,B對(duì)A進(jìn)行腐蝕的整個(gè)過(guò)程如下:

⑴?用結(jié)構(gòu)元素B,掃描圖像A的每一個(gè)像素

⑵?用結(jié)構(gòu)元素與其覆蓋的二值圖像做“與”操作

⑶?如果都為1,結(jié)果圖像的該像素為1。否則為0

腐蝕處理的結(jié)果是使原來(lái)的二值圖像減小一圈。

cvErode使用任意結(jié)構(gòu)元素腐蝕圖像?

void cvErode( const CvArr* src, CvArr* dst,IplConvKernel* element=NULL, int iterations=1 );

src 輸入圖像. dst 輸出圖像.element 用于腐蝕的結(jié)構(gòu)元素。

若為 NULL, 則使用 (1+iterations*2)*(1+iterations*2)的長(zhǎng)方形的結(jié)構(gòu)元素iterations 腐蝕的次數(shù)?

函數(shù) cvErode 對(duì)輸入圖像使用指定的結(jié)構(gòu)元素進(jìn)行腐蝕,該結(jié)構(gòu)元素決定每個(gè)具有最小值象素點(diǎn)的鄰域形狀:?dst=erode(src,element): ?dst(x,y)=min((x',y') inelement))src(x+x',y+y')

函數(shù)可能是本地操作支持in-place,不需另外開(kāi)辟存儲(chǔ)空間的意思。腐蝕可以重復(fù)進(jìn)行 (iterations) 次. 對(duì)彩色圖像,每個(gè)彩色通道單獨(dú)處理。?

腐蝕操作通常消除圖像中的斑點(diǎn)噪聲,確保圖像中較大的區(qū)域仍然存在。

cvErode的源代碼與cvDialte的源代碼相似,在此不再對(duì)其進(jìn)行解讀

CV_IMPL void

cvErode( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element,int iterations)

{

??? cv::Matsrc = cv::cvarrToMat(srcarr),dst = cv::cvarrToMat(dstarr),kernel;

??? CV_Assert(src.size()==dst.size()&&src.type()==dst.type());

??? cv::Pointanchor;

??? convertConvKernel(element,kernel,anchor );

??? cv::erode(src, dst, kernel, anchor, iterations,cv::BORDER_REPLICATE);

}

void cv::erode( InputArraysrc, OutputArraydst, InputArraykernel,

??????????????? Pointanchor,int iterations,

??????????????? intborderType,constScalar& borderValue)

{

??? morphOp(MORPH_ERODE,src,dst, kernel,anchor, iterations,borderType, borderValue);

}

(4)?高級(jí)形態(tài)學(xué)變換

開(kāi)操作是先腐蝕、后膨脹處理。

閉操作是先膨脹、后腐蝕處理。

cvMorphologyEx高級(jí)形態(tài)學(xué)變換?

void cvMorphologyEx( const CvArr* src, CvArr* dst, CvArr* temp,

? ? ? ? ? ? ? ? ? ??IplConvKernel* element, int operation, int iterations=1 );

src 輸入圖像. dst 輸出圖像. temp 臨時(shí)圖像,某些情況下需要,與源圖像同樣大小。臨時(shí)圖像 temp 在形態(tài)梯度以及對(duì)“頂帽”和“黑帽”操作時(shí)的 in-place 模式下需要。element 結(jié)構(gòu)元素,如果沒(méi)有輸入,則使用3*3的長(zhǎng)方形結(jié)構(gòu)元素。 iterations 迭代次數(shù).?

operation 形態(tài)操作的類型: CV_MOP_OPEN - 開(kāi)運(yùn)算 CV_MOP_CLOSE - 閉運(yùn)算 CV_MOP_GRADIENT - 形態(tài)梯度 CV_MOP_TOPHAT - "頂帽" CV_MOP_BLACKHAT - "黑帽"?

函數(shù) cvMorphologyEx 在膨脹和腐蝕基本操作的基礎(chǔ)上,完成一些高級(jí)的形態(tài)變換:?

開(kāi)運(yùn)算 dst=open(src,element)=dilate(erode(src,element),element)

開(kāi)運(yùn)算通常可以用來(lái)統(tǒng)計(jì)二值圖像中的區(qū)域數(shù)。

閉運(yùn)算 dst=close(src,element)=erode(dilate(src,element),element)

在多數(shù)連通域分析方法中用閉運(yùn)算去除噪聲區(qū)域

對(duì)于連通域分析,通常先采用腐蝕或者閉運(yùn)算來(lái)消除純粹噪聲引起的部分,然后用開(kāi)運(yùn)算來(lái)連接鄰近的區(qū)域。閉運(yùn)算消除低于其鄰近點(diǎn)的孤立點(diǎn),開(kāi)運(yùn)算消除高于其鄰近點(diǎn)的孤立點(diǎn)。對(duì)于iterations=2,就開(kāi)運(yùn)算而言其實(shí)是腐蝕->腐蝕->膨脹->膨脹這樣的過(guò)程。

形態(tài)梯度dst=morph_grad(src,element)=dilate(src,element)-erode(src,element)

對(duì)圖像進(jìn)行這一操作,可以將團(tuán)塊blob的邊緣以高亮區(qū)域突出出來(lái),保留完整的外圍邊緣。

"頂帽" dst=tophat(src,element)=src-open(src,element)?

"黑帽" dst=blackhat(src,element)=close(src,element)-src?

當(dāng)試圖孤立的部分相對(duì)于其鄰近的部分有亮度變化時(shí)可以使用,分離比鄰近的點(diǎn)亮或暗的一些斑塊。開(kāi)運(yùn)算帶來(lái)的結(jié)果是放大裂縫或局部低亮度區(qū)域,因此頂帽操作可以突出與核大小相關(guān)的比源圖像周?chē)膮^(qū)域更明亮的區(qū)域。黑帽操作突出比源圖像周?chē)膮^(qū)域黑暗的區(qū)域。

CV_IMPL void

cvMorphologyEx( const void* srcarr,void* dstarr, void*,

??????????????? IplConvKernel*element,intop, int iterations )

{

??? cv::Matsrc = cv::cvarrToMat(srcarr),dst = cv::cvarrToMat(dstarr),kernel;

??? CV_Assert(src.size()==dst.size()&&src.type()==dst.type());

??? cv::Pointanchor;

??? IplConvKernel*temp_element =NULL;

?????? // 如果沒(méi)有給定結(jié)構(gòu)元素,則定義*3的長(zhǎng)方形元素,元素原點(diǎn)為(1,1)

??? if (!element)

??? {

??????? temp_element= cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);

??? } else {

??????? temp_element= element;

??? }

?????? // 讀取結(jié)構(gòu)元素中的值

??? convertConvKernel(temp_element,kernel,anchor );

?????? // 釋放定義的結(jié)構(gòu)元素

??? if (!element)

??? {

??????? cvReleaseStructuringElement(&temp_element);

??? }

?????? // 執(zhí)行形態(tài)學(xué)操作

??? cv::morphologyEx(src,dst, op, kernel, anchor,iterations, cv::BORDER_REPLICATE );

}

void cv::morphologyEx( InputArray_src, OutputArray_dst, int op,

?????????????????????? InputArraykernel,Pointanchor,int iterations,

?????????????????????? intborderType,constScalar& borderValue)

{

??? Mat src = _src.getMat(),temp;

??? _dst.create(src.size(),src.type());

??? Mat dst = _dst.getMat();

?

??? switch( op )

??? {

??? case MORPH_ERODE:

??????? erode(src,dst,kernel,anchor,iterations,borderType,borderValue );

??????? break;

??? case MORPH_DILATE:

??????? dilate(src,dst,kernel,anchor,iterations,borderType,borderValue );

??????? break;

??? case MORPH_OPEN:

??????? erode(src,dst,kernel,anchor,iterations,borderType,borderValue );

??????? dilate(dst,dst,kernel,anchor,iterations,borderType,borderValue );

??????? break;

??? case CV_MOP_CLOSE:

??????? dilate(src,dst,kernel,anchor,iterations,borderType,borderValue );

??????? erode(dst,dst,kernel,anchor,iterations,borderType,borderValue );

??????? break;

??? case CV_MOP_GRADIENT:

??????? erode(src,temp,kernel,anchor,iterations,borderType,borderValue );

??????? dilate(src,dst,kernel,anchor,iterations,borderType,borderValue );

??????? dst-= temp;

??????? break;

??? case CV_MOP_TOPHAT:

??????? if( src.data != dst.data )

??????????? temp= dst;

??????? erode(src,temp,kernel,anchor,iterations,borderType,borderValue );

??????? dilate(temp,temp,kernel,anchor,iterations,borderType,borderValue );

??????? dst= src - temp;

??????? break;

??? case CV_MOP_BLACKHAT:

??????? if( src.data != dst.data )

??????????? temp= dst;

??????? dilate(src,temp,kernel,anchor,iterations,borderType,borderValue );

??????? erode(temp,temp,kernel,anchor,iterations,borderType,borderValue );

??????? dst= temp - src;

??????? break;

??? default:

??????? CV_Error(CV_StsBadArg,"unknownmorphological operation" );

??? }

}

(5)?細(xì)化

圖像細(xì)化一般作為一種圖像預(yù)處理技術(shù)出現(xiàn),目的是提取源圖像的骨架,即是將原圖像中線條寬度大于1個(gè)像素的線條細(xì)化成只有一個(gè)像素寬,形成“骨架”,形成骨架后能比較容易的分析圖像,如提取圖像的特征。

細(xì)化基本思想是“層層剝奪”,即從線條邊緣開(kāi)始一層一層向里剝奪,直到線條剩下一個(gè)像素的為止。圖像細(xì)化大大地壓縮了原始圖像地?cái)?shù)據(jù)量,并保持其形狀的基本拓?fù)浣Y(jié)構(gòu)不變,從而為文字識(shí)別中的特征抽取等應(yīng)用奠定了基礎(chǔ)。細(xì)化算法應(yīng)滿足以下條件:

①?將條形區(qū)域變成一條薄線;

②?薄線應(yīng)位與原條形區(qū)域的中心;

③?薄線應(yīng)保持原圖像的拓?fù)涮匦浴?/p>

細(xì)化分成串行細(xì)化和并行細(xì)化,串行細(xì)化即是一邊檢測(cè)滿足細(xì)化條件的點(diǎn),一邊刪除細(xì)化點(diǎn);并行細(xì)化即是檢測(cè)細(xì)化點(diǎn)的時(shí)候不進(jìn)行點(diǎn)的刪除只進(jìn)行標(biāo)記,而在檢測(cè)完整幅圖像后一次性去除要細(xì)化的點(diǎn)。

常用的圖像細(xì)化算法有hilditch算法,pavlidis算法和rosenfeld算法等。注:進(jìn)行細(xì)化算法前要先對(duì)圖像進(jìn)行二值化,即圖像中只包含“黑”和“白”兩種顏色。

cvThin

void cvThin( IplImage* src,IplImage* dst, int iterations=1)

功能:將IPL_DEPTH_8U型二值圖像進(jìn)行細(xì)化

參數(shù):src原始IPL_DEPTH_8U型二值圖像。dst目標(biāo)存儲(chǔ)空間,必須事先分配好,且和原圖像大小類型一致。iterations,迭代次數(shù)

在opencv之前的版本中有,后來(lái)去除了

總結(jié)

以上是生活随笔為你收集整理的cvMorphology形态学原理解析及源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。