Opencv圖像識別從零到精通(17)----開運算、閉運算、頂帽、黑帽、形態學梯度、形態學角點、細化、填充

一、全體成員   

   經過了上一篇的膨脹、腐蝕以後,我們就可以用他們組合起來,形成了更多的形態效果,這樣就不會太多的改變原來圖像的大小,總結了一下,主要包含開運算、閉運算、頂帽、黑帽、形態學梯度、形態學角點、細化、填充這些方面。

1.開運算

對圖像進行先腐蝕後膨脹的操作就是圖像的開運算。

它的功能是有利於移走黑色前景下的白色小物體。

2.閉運算

對圖像進行先膨脹後腐蝕的操作就是圖像的閉運算。

它的功能是有利於移走黑色區域小洞。

3.形態學梯度

形態學梯度是一幅圖像腐蝕和膨脹的差值。

它有利於查找圖像的輪廓。

4.Top Hat

Top Hat是輸入圖像和它開運算的差值。

5.Black Hat

Black Hat是輸入圖像和它閉運算的差值。

6.形態學角點

dst=open-open

7細化

這個比較的複雜,講原理的話估計要還幾頁http://www.cnblogs.com/mikewolf2002/p/3321732.html,可以看看這篇文章說的很多,也很好

二、morphologyEx函數

其實,說了那麼多大多數都是和一個函數有關係那就是morphologyEx函數,這裏面給出了多種形態學類型的定義

<span style="font-size:18px;"><span style="font-size:18px;">void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,  
                       InputArray kernel, Pointanchor, int iterations,  
                       int borderType, constScalar& borderValue )  
{  
//拷貝Mat數據到臨時變量  
   Mat src = _src.getMat(), temp;  
   _dst.create(src.size(), src.type());  
   Mat dst = _dst.getMat();  
   
//一個大switch,根據不同的標識符取不同的操作  
   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, "unknown morphological operation" );  
    }  
}  </span></span>

三、開運算小例子

爲了好理解,簡單說明一個例子,其他的參考,改變參數就可以,下面是使用開運算作爲例子

<span style="font-size:18px;"><span style="font-size:18px;">//-----------------------------------------------------------------------------------------------  
int main( )  
{  
       Mat image = imread("1.jpg");  
       namedWindow("【原始圖】開運算");   
       namedWindow("【效果圖】開運算");    
       imshow("【原始圖】開運算", image);   
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));     
       morphologyEx(image,image, MORPH_OPEN, element);    
       imshow("【效果圖】開運算", image);   
       waitKey(0);   
       return 0;   
}  </span></span>


四、綜合成員

相信學習那麼長時間,上面的一看就明白了,那麼就可以進去綜合代碼

<span style="font-size:18px;"><span style="font-size:18px;">#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
Mat src, dst;
int morph_elem = 0;
int morph_size = 0;
int morph_operator = 0;
int const max_operator = 4;
int const max_elem = 2;
int const max_kernel_size = 21;
char* window_name = "Morphology Transformations Demo";
void Morphology_Operations( int, void* );
int main( int argc, char** argv )
{
  src = imread( "lena.jpg" );
  if( !src.data )
  { return -1; }
 namedWindow( window_name, CV_WINDOW_AUTOSIZE );
 createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name,
 &morph_operator, max_operator, Morphology_Operations );
 createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
                 &morph_elem, max_elem,
                 Morphology_Operations );
 createTrackbar( "Kernel size:\n 2n +1", window_name,
                 &morph_size, max_kernel_size,
                 Morphology_Operations );
 Morphology_Operations( 0, 0 );
 waitKey(0);
 return 0;
 }
void Morphology_Operations( int, void* )
{
  int operation = morph_operator + 2;
  Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
  morphologyEx( src, dst, operation, element );
  imshow( window_name, dst );
  }</span></span>

再看來看看角點檢測,其實也是膨脹腐蝕的變形,只是沒有一個好名字

<span style="font-size:18px;">Mat result2;
dilate(image,result2,x);
erode(result2,result2,square);
imshow("result2",result2);
absdiff(result2,result,result);
imshow("result3",result);
threshold(result,result,40,255,THRESH_BINARY);</span>


細化,這個比較的複雜,需要好好的理解一下

 圖像細化(Image Thinning),一般指二值圖像的骨架化(Image Skeletonization) 的一種操作運算。細化就是經過一層層的剝離,從原來的圖中去掉一些點,但仍要保持原來的形狀,直到得到圖像的骨架。骨架,可以理解爲圖象的中軸。

<span style="font-size:18px;">#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <vector>
cv::Mat thinImage(const cv::Mat & src, const int maxIterations = -1)
{
    assert(src.type() == CV_8UC1);
    cv::Mat dst;
    int width  = src.cols;
    int height = src.rows;
    src.copyTo(dst);
    int count = 0;  
    while (true)
    {
        count++;
        if (maxIterations != -1 && count > maxIterations) 
            break;
        std::vector<uchar *> mFlag; //用於標記需要刪除的點
        //對點標記
        for (int i = 0; i < height ;++i)
        {
            uchar * p = dst.ptr<uchar>(i);
            for (int j = 0; j < width; ++j)
            {
                //如果滿足四個條件,進行標記
                //  p9 p2 p3
                //  p8 p1 p4
                //  p7 p6 p5
                uchar p1 = p[j];
                if (p1 != 1) continue;
                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
                uchar p8 = (j == 0) ? 0 : *(p + j - 1);
                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);
                if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
                {
                    int ap = 0;
                    if (p2 == 0 && p3 == 1) ++ap;
                    if (p3 == 0 && p4 == 1) ++ap;
                    if (p4 == 0 && p5 == 1) ++ap;
                    if (p5 == 0 && p6 == 1) ++ap;
                    if (p6 == 0 && p7 == 1) ++ap;
                    if (p7 == 0 && p8 == 1) ++ap;
                    if (p8 == 0 && p9 == 1) ++ap;
                    if (p9 == 0 && p2 == 1) ++ap;
                    if (ap == 1 && p2 * p4 * p6 == 0 && p4 * p6 * p8 == 0)
                    {
                        mFlag.push_back(p+j);
                    }
                }
            }
        }
        //將標記的點刪除
        for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
        {
            **i = 0;
        }
        if (mFlag.empty())
        {
            break;
        }
        else
        {
            mFlag.clear();//將mFlag清空
        }
        for (int i = 0; i < height; ++i)
        {
            uchar * p = dst.ptr<uchar>(i);
            for (int j = 0; j < width; ++j)
            {
                uchar p1 = p[j];
                if (p1 != 1) continue;
                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
                uchar p8 = (j == 0) ? 0 : *(p + j - 1);
                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);

                if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
                {
                    int ap = 0;
                    if (p2 == 0 && p3 == 1) ++ap;
                    if (p3 == 0 && p4 == 1) ++ap;
                    if (p4 == 0 && p5 == 1) ++ap;
                    if (p5 == 0 && p6 == 1) ++ap;
                    if (p6 == 0 && p7 == 1) ++ap;
                    if (p7 == 0 && p8 == 1) ++ap;
                    if (p8 == 0 && p9 == 1) ++ap;
                    if (p9 == 0 && p2 == 1) ++ap;

                    if (ap == 1 && p2 * p4 * p8 == 0 && p2 * p6 * p8 == 0)
                    {
                        //標記
                        mFlag.push_back(p+j);
                    }
                }
            }
        }
        for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
        {
            **i = 0;
        }
        if (mFlag.empty())
        {
            break;
        }
        else
        {
            mFlag.clear();//將mFlag清空
        }
    }
    return dst;
}
int main(int argc, char*argv[])
{
    if (argc != 2)
    {
        std::cout << "參數個數錯誤!" << std::endl;
        return -1;
    }
    cv::Mat src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
    if (src.empty())
    {
        std::cout << "讀取文件失敗!" << std::endl;
        return -1;
    }
    cv::threshold(src, src, 128, 1, cv::THRESH_BINARY);
    //圖像細化
    cv::Mat dst = thinImage(src);
    //顯示圖像
    dst = dst * 255;
    cv::namedWindow("src1", CV_WINDOW_AUTOSIZE);
    cv::namedWindow("dst1", CV_WINDOW_AUTOSIZE);
    cv::imshow("src1", src);
    cv::imshow("dst1", dst);
    cv::waitKey(0);
}</span>



填充可以參考這篇文章http://lib.csdn.net/article/opencv/28355

<span style="font-size:18px;">#include "cxcore.h"   
#include "cv.h"   
#include "highgui.h"  
   
void FillInternalContours(IplImage *pBinary, double dAreaThre)   
{   
    double dConArea;   
    CvSeq *pContour = NULL;   
    CvSeq *pConInner = NULL;   
    CvMemStorage *pStorage = NULL;   
    // 執行條件   
    if (pBinary)   
    {   
        // 查找所有輪廓   
        pStorage = cvCreateMemStorage(0);   
        cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);   
        // 填充所有輪廓   
        cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));  
        // 外輪廓循環   
        int wai = 0;  
        int nei = 0;  
        for (; pContour != NULL; pContour = pContour->h_next)   
        {   
            wai++;  
            // 內輪廓循環   
            for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)   
            {   
                nei++;  
                // 內輪廓面積   
                dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));  
                printf("%f\n", dConArea);  
                if (dConArea <= dAreaThre)   
                {   
                    cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));  
                }   
            }   
        }   
        printf("wai = %d, nei = %d", wai, nei);  
        cvReleaseMemStorage(&pStorage);   
        pStorage = NULL;   
    }   
}   
int Otsu(IplImage* src)      
{      
    int height=src->height;      
    int width=src->width;          
  
    //histogram      
    float histogram[256] = {0};      
    for(int i=0; i < height; i++)    
    {      
        unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;      
        for(int j = 0; j < width; j++)     
        {      
            histogram[*p++]++;      
        }      
    }      
    //normalize histogram      
    int size = height * width;      
    for(int i = 0; i < 256; i++)    
    {      
        histogram[i] = histogram[i] / size;      
    }      
  
    //average pixel value      
    float avgValue=0;      
    for(int i=0; i < 256; i++)    
    {      
        avgValue += i * histogram[i];  //整幅圖像的平均灰度    
    }       
  
    int threshold;        
    float maxVariance=0;      
    float w = 0, u = 0;      
    for(int i = 0; i < 256; i++)     
    {      
        w += histogram[i];  //假設當前灰度i爲閾值, 0~i 灰度的像素(假設像素值在此範圍的像素叫做前景像素) 所佔整幅圖像的比例    
        u += i * histogram[i];  // 灰度i 之前的像素(0~i)的平均灰度值: 前景像素的平均灰度值    
  
        float t = avgValue * w - u;      
        float variance = t * t / (w * (1 - w) );      
        if(variance > maxVariance)     
        {      
            maxVariance = variance;      
            threshold = i;      
        }      
    }      
  
    return threshold;      
}     
  
int main()  
{  
    IplImage *img = cvLoadImage("lena.jpg", 0);  
    IplImage *bin = cvCreateImage(cvGetSize(img), 8, 1);  
  
    int thresh = Otsu(img);  
    cvThreshold(img, bin, thresh, 255, CV_THRESH_BINARY);  
  
    FillInternalContours(bin, 200);  
  
    cvNamedWindow("img");  
    cvShowImage("img", img);  
  
    cvNamedWindow("result");  
    cvShowImage("result", bin);  
  
    cvWaitKey(-1);  
  
    cvReleaseImage(&img);  
    cvReleaseImage(&bin);  
  
    return 0;  
}  </span>


五、matlab輔助

<span style="font-size:18px;">I=imread('d:\22.png');
SE=strel('rectangle',[3 3]);
I2=imopen(I,SE);
I3=imclose(I,SE);
I4=im2bw(I);
I5=bwmorph(I4,'thin',inf)
figure
subplot(131),imshow(I2),title('開運算')
subplot(132),imshow(I3),title('閉運算')
subplot(133),imshow(I5),title('細化')</span>


圖像識別算法交流 QQ羣:145076161,歡迎圖像識別與圖像算法,共同學習與交流

發佈了135 篇原創文章 · 獲贊 274 · 訪問量 130萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章