cvMorphology形态学原理解析及源码分析
⑴?圖像形態學處理的概念...1
⑵?二值圖像的邏輯運算...3
⑶?膨脹和腐蝕...4
(4)?高級形態學變換...8
(5)?細化...10
?
⑴?圖像形態學處理的概念
數字圖像處理中的形態學處理是指將數字形態學作為工具從圖像中提取對于表達和描繪區域形狀有用處的圖像分量,比如邊界、骨架以及凸殼,還包括用于預處理或后處理的形態學過濾、細化和修剪等。圖像形態學處理中我們感興趣的主要是二值圖像。
在二值圖像中,所有黑色像素的集合是圖像完整的形態學描述,二值圖像的各個分量是Z2的元素。假定二值圖像A和形態學處理的結構元素B是定義在笛卡兒網格上的集合,網格中值為1的點是集合的元素,當結構元素的原點移到點(x,y)時,記為Sxy,為簡單起見,結構元素為3x3,且全都為1,在這種限制下,決定輸出結果的是邏輯運算。
IplConvKernel結構元素
typedef struct _IplConvKernel
{
? ? int ?nCols;?//結構元素的行寬
? ? int ?nRows;?//列高
? ? int?anchorX; //結構原點位置水平坐標
? ? int?anchorY; //結構原點位置垂直坐標
int *values; //當nShiftR為自定義時,value是指向結構元素數據的指針
//如果結構元素的大小定義為8*6,那么values為48長的int數組,值為0或1。
? ? int?nShiftR;// 用于表示結構元素的形狀類型
}IplConvKernel;
cvCreateStructuringElementEx創建結構元素?
IplConvKernel* cvCreateStructuringElementEx( int cols, int rows, intanchor_x, int anchor_y,
? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ?? ??????????int shape, int*values=NULL );
cols 結構元素的列數目 rows 結構元素的行數目
anchor_x 錨點的相對水平偏移量 anchor_y 錨點的相對垂直偏移量?
shape 結構元素的形狀,可以是下列值:?
CV_SHAPE_RECT, 長方形元素;?
CV_SHAPE_CROSS, 十字交叉型,交錯元素 a cross-shaped element;?
CV_SHAPE_ELLIPSE, 橢圓元素;?
CV_SHAPE_CUSTOM, 用戶自定義元素。這種情況下參數 values 在封閉矩形內定義核的形狀,即象素的那個鄰域必須考慮。
values 指向結構元素的指針,它是一個平面數組,表示對元素矩陣逐行掃描。(非零點表示該點屬于結構元)。如果指針為空,則表示平面數組中的所有元素都是非零的,即結構元是一個長方形(該參數僅僅當shape參數是 CV_SHAPE_CUSTOM 時才予以考慮)。?
形態核與卷積核不同,不需要任何數值填充核。當核在圖像上移動時,核的元素只需要簡單表明應該在哪個范圍內計算最大值和最小值,參考點制定核與源圖像的位置關系,同時也鎖定了計算結果在目標圖像中的位置。行和列確定了所構造的矩形的大小(結構元素在矩形內),anchor_x和anchor_y是核的封閉矩形內的參考點坐標。
cvReleaseStructuringElement刪除結構元素?
void cvReleaseStructuringElement( IplConvKernel** element );
element 被刪除的結構元素的指針,函數 cvReleaseStructuringElement 釋放結構 IplConvKernel 。如果 *element 為 NULL, 則函數不作用。
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);
?????? // 檢測輸入數據,當用戶自定義的時候value不能為空,value默認為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);
?????? // 為什么創建的內存要比實際的大呢?大了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指向結構的首地址
??? element->values = (int*)(element + 1);
?????? // 如果為用戶自定義的類型,從values中取值
??? if( shape == CV_SHAPE_CUSTOM)
??? {
??????? for( i = 0; i < size; i++ )
??????????? element->values[i] =values[i];
??? }
??? else
??? {
????????????? // 根據不同的結構類型獲得不同的數值
??????? 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)內
??? anchor =normalizeAnchor(anchor,ksize);
?????? // 當只有一個結構元素的時候cols=1 rows=1,長方形結構元素
??? if( ksize == Size(1,1))
??????? shape= MORPH_RECT;
?????? // 如果為橢圓形的結構元素
??? if( shape == MORPH_ELLIPSE)
??? {
????????????? //r? c分別為橢圓的半徑
??????? r = ksize.height/2;
??????? c = ksize.width/2;
????????????? // 如果r!=0,inv_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;
????????????? // 根據不同的類型得到不同的起始坐標
??????? 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;
}
⑵?二值圖像的邏輯運算
邏輯運算盡管本質上很簡單,但對于實現以形態學為基礎的圖像處理算法是一種有力的補充手段。在圖像處理中用到的主要邏輯運算是:與、或和非(求補),它們可以互相組合形成其他邏輯運算。
⑶?膨脹和腐蝕
膨脹和腐蝕這兩種操作是形態學處理的基礎,許多形態學算法都是以這兩種運算為基礎的。
①?膨脹
結構元素B可以看作一個卷積模板,區別在于膨脹是以集合運算為基礎的,卷積是以算術運算為基礎的,但兩者的處理過程是相似的。
⑴?用結構元素B,掃描圖像A的每一個像素
⑵?用結構元素與其覆蓋的二值圖像做“與”操作
⑶?如果都為0,結果圖像的該像素為0。否則為1
cvDilate使用任意結構元素膨脹圖像?
voidcvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, intiterations=1 );
src輸入圖像. dst 輸出圖像. element 用于膨脹的結構元素。
若為 NULL,則使用(1+iterations*2)*(1+iterations*2)的長方形的結構元素?.iterations膨脹的次數?
函數 cvDilate 對輸入圖像使用指定的結構元進行膨脹,該結構決定每個具有最小值象素點的鄰域形狀:?dst=dilate(src,element):?dst(x,y)=max((x',y') in element))src(x+x',y+y')
函數支持(in-place)模式。膨脹可以重復進行 (iterations) 次. 對彩色圖像,每個彩色通道單獨處理。
在試圖找到連通分支(即具有相似的顏色或強度的像素點的大塊互相分離的區域)時通常使用膨脹操作。
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;
?????? // 若沒有結構元素輸入,kernel=NULL,anchor=(1,1)
?????? // 否則將結構元素中的值讀入kernel和anchor
??? convertConvKernel(element,kernel,anchor );
?????? // 邊界差值方法采用邊界復制
??? cv::dilate(src, dst, kernel, anchor, iterations,cv::BORDER_REPLICATE);
}
static voidconvertConvKernel( constIplConvKernel*src,cv::Mat&dst,cv::Point& anchor)
{
?????? // 若沒有輸入結構元素
??? if(!src)
??? {
??????? anchor= cv::Point(1,1);
??????? dst.release();
??????? return;
??? }
?????? // 獲取結構原點的坐標
??? anchor =cv::Point(src->anchorX,src->anchorY);
?????? // 讀取結構元素的值
??? 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();
?????? // 如果輸入的時候不輸入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)內
??? anchor =normalizeAnchor(anchor,ksize);
?????? // 這一句是多余的,因為在上面normalizeAnchor已經判斷了
??? CV_Assert(anchor.inside(Rect(0, 0,ksize.width,ksize.height)) );
?
??? _dst.create(src.size(),src.type() );
??? Mat dst = _dst.getMat();
?????? // 如果迭代步數為或者結構元素的尺寸為,不進行處理,直接輸出
??? if( iterations == 0 || kernel.rows*kernel.cols == 1 )
??? {
??????? src.copyTo(dst);
??????? return;
??? }
?????? // 如果沒有輸入結構元素,那么創建(1+iterations*2)*(1+iterations*2)的長方形結構元素
?????? // 結構元素的中心點為(iterations, iterations),并將迭代步數設置為
??? if( !kernel.data )
??? {
??????? kernel= getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
??????? anchor= Point(iterations,iterations);
??????? iterations= 1;
??? }
?????? // 如果結構步數大于的話并且kernel為長方形的結構元素,重新創建結構元素
??? 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;
// Tegra是NVIDIA公司于2008年推出的基于ARM構架通用處理器品牌(即CPU,NVIDIA稱為“Computer on a chip”片上計算機),能夠為便攜設備提供高性能、低功耗體驗。
#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;
??? }
?????? // ()操作符,最主要的運算符號
??? 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);
????????????? ?// 創建形態學濾波器
??????? 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;
};
②?腐蝕
對Z中的集合A和B,B對A進行腐蝕的整個過程如下:
⑴?用結構元素B,掃描圖像A的每一個像素
⑵?用結構元素與其覆蓋的二值圖像做“與”操作
⑶?如果都為1,結果圖像的該像素為1。否則為0
腐蝕處理的結果是使原來的二值圖像減小一圈。
cvErode使用任意結構元素腐蝕圖像?
void cvErode( const CvArr* src, CvArr* dst,IplConvKernel* element=NULL, int iterations=1 );
src 輸入圖像. dst 輸出圖像.element 用于腐蝕的結構元素。
若為 NULL, 則使用 (1+iterations*2)*(1+iterations*2)的長方形的結構元素iterations 腐蝕的次數?
函數 cvErode 對輸入圖像使用指定的結構元素進行腐蝕,該結構元素決定每個具有最小值象素點的鄰域形狀:?dst=erode(src,element): ?dst(x,y)=min((x',y') inelement))src(x+x',y+y')
函數可能是本地操作支持in-place,不需另外開辟存儲空間的意思。腐蝕可以重復進行 (iterations) 次. 對彩色圖像,每個彩色通道單獨處理。?
腐蝕操作通常消除圖像中的斑點噪聲,確保圖像中較大的區域仍然存在。
cvErode的源代碼與cvDialte的源代碼相似,在此不再對其進行解讀
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)?高級形態學變換
開操作是先腐蝕、后膨脹處理。
閉操作是先膨脹、后腐蝕處理。
cvMorphologyEx高級形態學變換?
void cvMorphologyEx( const CvArr* src, CvArr* dst, CvArr* temp,
? ? ? ? ? ? ? ? ? ??IplConvKernel* element, int operation, int iterations=1 );
src 輸入圖像. dst 輸出圖像. temp 臨時圖像,某些情況下需要,與源圖像同樣大小。臨時圖像 temp 在形態梯度以及對“頂帽”和“黑帽”操作時的 in-place 模式下需要。element 結構元素,如果沒有輸入,則使用3*3的長方形結構元素。 iterations 迭代次數.?
operation 形態操作的類型: CV_MOP_OPEN - 開運算 CV_MOP_CLOSE - 閉運算 CV_MOP_GRADIENT - 形態梯度 CV_MOP_TOPHAT - "頂帽" CV_MOP_BLACKHAT - "黑帽"?
函數 cvMorphologyEx 在膨脹和腐蝕基本操作的基礎上,完成一些高級的形態變換:?
開運算 dst=open(src,element)=dilate(erode(src,element),element)
開運算通常可以用來統計二值圖像中的區域數。
閉運算 dst=close(src,element)=erode(dilate(src,element),element)
在多數連通域分析方法中用閉運算去除噪聲區域
對于連通域分析,通常先采用腐蝕或者閉運算來消除純粹噪聲引起的部分,然后用開運算來連接鄰近的區域。閉運算消除低于其鄰近點的孤立點,開運算消除高于其鄰近點的孤立點。對于iterations=2,就開運算而言其實是腐蝕->腐蝕->膨脹->膨脹這樣的過程。
形態梯度dst=morph_grad(src,element)=dilate(src,element)-erode(src,element)
對圖像進行這一操作,可以將團塊blob的邊緣以高亮區域突出出來,保留完整的外圍邊緣。
"頂帽" dst=tophat(src,element)=src-open(src,element)?
"黑帽" dst=blackhat(src,element)=close(src,element)-src?
當試圖孤立的部分相對于其鄰近的部分有亮度變化時可以使用,分離比鄰近的點亮或暗的一些斑塊。開運算帶來的結果是放大裂縫或局部低亮度區域,因此頂帽操作可以突出與核大小相關的比源圖像周圍的區域更明亮的區域。黑帽操作突出比源圖像周圍的區域黑暗的區域。
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;
?????? // 如果沒有給定結構元素,則定義*3的長方形元素,元素原點為(1,1)
??? if (!element)
??? {
??????? temp_element= cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);
??? } else {
??????? temp_element= element;
??? }
?????? // 讀取結構元素中的值
??? convertConvKernel(temp_element,kernel,anchor );
?????? // 釋放定義的結構元素
??? if (!element)
??? {
??????? cvReleaseStructuringElement(&temp_element);
??? }
?????? // 執行形態學操作
??? 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)?細化
圖像細化一般作為一種圖像預處理技術出現,目的是提取源圖像的骨架,即是將原圖像中線條寬度大于1個像素的線條細化成只有一個像素寬,形成“骨架”,形成骨架后能比較容易的分析圖像,如提取圖像的特征。
細化基本思想是“層層剝奪”,即從線條邊緣開始一層一層向里剝奪,直到線條剩下一個像素的為止。圖像細化大大地壓縮了原始圖像地數據量,并保持其形狀的基本拓撲結構不變,從而為文字識別中的特征抽取等應用奠定了基礎。細化算法應滿足以下條件:
①?將條形區域變成一條薄線;
②?薄線應位與原條形區域的中心;
③?薄線應保持原圖像的拓撲特性。
細化分成串行細化和并行細化,串行細化即是一邊檢測滿足細化條件的點,一邊刪除細化點;并行細化即是檢測細化點的時候不進行點的刪除只進行標記,而在檢測完整幅圖像后一次性去除要細化的點。
常用的圖像細化算法有hilditch算法,pavlidis算法和rosenfeld算法等。注:進行細化算法前要先對圖像進行二值化,即圖像中只包含“黑”和“白”兩種顏色。
cvThin
void cvThin( IplImage* src,IplImage* dst, int iterations=1)
功能:將IPL_DEPTH_8U型二值圖像進行細化
參數:src原始IPL_DEPTH_8U型二值圖像。dst目標存儲空間,必須事先分配好,且和原圖像大小類型一致。iterations,迭代次數
在opencv之前的版本中有,后來去除了
總結
以上是生活随笔為你收集整理的cvMorphology形态学原理解析及源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Wireshark抓包分析TCP建立/释
- 下一篇: 京瓷1020手动双面打印提示_自动双面打