opencv的實用研究--分析輪廓並尋找邊界點


opencv的實用研究--分析輪廓並尋找邊界點
​      輪廓是圖像處理中非常常見的。對現實中的圖像進行采樣、色彩變化、灰度變化之后,能夠處理得到的是“輪廓”。它直接地反應你了需要分析對象的邊界特征。而對輪廓的分析,實際上也就是對原圖像特征的分析。
      在Opencv中,已經實現了基礎的輪廓算法,但是相比較於比如halcon這樣的專業軟件,在輪廓處理這塊的功能還是比較缺乏的。這里就通過一個具體問題,說明自己的學習研究。不對之處歡迎批評。
       P.S這里的輪廓處理相關函數,已經包涵在GOBase中,具體可以到公告中找Github.
一、問題提出
      那么如果對於一個簡單的圖像,比如
      已經獲得了最大物體的輪廓,比如
  
  //灰度域變化
    threshold(gray,gray,0,255,THRESH_BINARY_INV);
    GaussianBlur(gray,gray,Size(3,3),0,0);
    //尋找和繪制輪廓
    VP bigestContour = FindBigestContour(gray);
    contours.push_back(bigestContour);  

  

 
    
     由於在opencv里面,輪廓是以
  1.  vector<vector<point>>
    保存的,那 么如何獲得這個輪廓的四個頂點了?
     嘗試直接打印輪廓中第一個點,那么的確是左上角

但是不具有通用性,在一些比較復雜的圖片上面效果不行,比如
那么也就是說,必須通過特征分析的方法獲得已經獲得的輪廓中點的特性,而opencv本身沒有提供相關功能。
二、直觀的解決
      現在,對於“左上”和“右下”的兩個點,是比較好分析的。因為在所有的包含在輪廓中的點中,他們一個是x,y同時最小的,一個是x,y同時最大的。
      比較復雜的是“左下”和"右上"兩個點, 因為他們的數值不是非常有特征, 比較容易產生混淆。這個時候,如果僅僅是通過x,y值來分析,即使是對於簡單圖像,也很難得到穩定的結果。
  1. int itopleft =65535;
    int idownright =0;
    Point ptopleft;
    Point pdownright;
    Point pdownleft;
    for(int i=0;i<bigestContour.size();i++){
    //左上
    if(bigestContour[i].x + bigestContour[i].y <itopleft){
    itopleft = bigestContour[i].x + bigestContour[i].y ;
    ptopleft = bigestContour[i];
    }
    //右下
    if(bigestContour[i].x+bigestContour[i].y>idownright){
    idownright = bigestContour[i].x+bigestContour[i].y;
    pdownright = bigestContour[i];
    }
    }
    int idownleft =65534;
    //對於左下的點來說,應該是所有y大於左上的點中,x最小的
    for(int i=0;i<bigestContour.size();i++){
    if(bigestContour[i].y>ptopleft.y){
    if(bigestContour[i].x<idownleft){
    idownleft = bigestContour[i].x;
    pdownleft = bigestContour[i];
    }
    }
    }
    //繪制
    circle(board,ptopleft,10,Scalar(255),5);
    circle(board,pdownright,10,Scalar(255),5);
    circle(board,pdownleft,10,Scalar(255),5);
    

      

三、利用模型來解決
      那么,直觀的方法是不穩定的。這個時候,我想到在進行圖像處理的時候,有所謂“特征點”的說法。比較常見的比如harris /shift/surf。那么我是否能夠通過分析輪廓圖像,找到輪廓圖像特征點的方法找到我需要的邊角了?
      編碼實現:
///在board上尋找角點
///// Detector parameters  
int blockSize = 2;  
int apertureSize = 3;  
double k = 0.04;  
int thresh = 1;
/// Detecting corners  
board.convertTo(board,CV_32F);
cornerHarris( board,dst,2,3,0.04);
///// Normalizing  
normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );  
convertScaleAbs( dst_norm, dst_norm_scaled );   
///// Drawing a circle around corners  
for( int j = 0; j < dst_norm.rows ; j++ )  { 
        for( int i = 0; i < dst_norm.cols; i++ )  {  
    if( (int) dst_norm.at<float>(j,i) > thresh )  {   
circle( dst_norm_scaled, Point( i, j ), 5,  Scalar(0), 2, 8, 0 );   
circle(src,Point( i, j ), 5,  Scalar(255,0,0), -1, 8, 0 );  
}  
}
}

  

  1.    
得到結果
NICE,在圖像中已經明顯的顯示出來了4個邊界點,再加上已經有的兩個點,得到結果不成問題。
四、問題進一步研究
      但是這里其實是用了一個“投機取巧”的方法,那就是使用圖像處理的才使用的harris算法來分析輪廓。opencv默認實現的harris速度慢且會內存移除。用在這個簡單的例子里面看似可以,但是無法處理現實問題。所以就必須分析原理。
      做圖像處理有一段時間了,我經常反思回憶,在圖像處理中,能夠穩定解決問題的,往往依靠的是“先驗知識,本質特征”;越是分析逼近圖像的本質特征,越能夠發現穩定的解決方法。比如對於輪廓的角來說,很容易想到處於邊角的點和兩邊的點肯定具有一定的關系,而這種關系具有特征性。
      所以有目的地尋找論文,很快就有了成果:
 
 
      對於我的研究來說,這篇論文兩個貢獻:一個是告知 首先要對圖像進行高斯模糊,這個是我之前沒有想到的。特別是對於現實世界中的輪廓,這種方法效果很好。因為邊角經過模糊,那么還是邊角,但毛刺經過模糊,能夠有效去除。
       論文中的算法實現是比較簡單的,並且給出了簡化算法,直接編碼驗證:
  1.  
    //遍歷輪廓,求出所有支撐角度
        int icount = bigestContour.size();
        float fmax = -1;//用於保存局部最大值
        int   imax = -1;
        bool  bstart = false;
        for (int i=0;i<bigestContour.size();i++){
            Point2f pa = (Point2f)bigestContour[(i+icount-7)%icount];
            Point2f pb = (Point2f)bigestContour[(i+icount+7)%icount];
            Point2f pc = (Point2f)bigestContour[i];
            //兩支撐點距離
            float fa = getDistance(pa,pb);
            float fb = getDistance(pa,pc)+getDistance(pb,pc);
            float fang = fa/fb;
            float fsharp = 1-fang;
            if (fsharp>0.05){
                bstart = true;
                if (fsharp>fmax){
                    fmax = fsharp;
                    imax = i;
                }
            }else{
                if (bstart){
                    circle(board,bigestContour[imax],10,Scalar(255),1);
                    circle(src,bigestContour[imax],10,Scalar(255,255,255),1);
                    imax  = -1;
                    fmax  = -1;
                    bstart = false;
                }
            }
        }  
    

      

      編碼過程中,相比較於原文,有兩處優化(原文中應該也提到了,但是沒有明說):一是通過取模,使得所有的輪廓點都參與運算;二是通過比較,取出角點的局部最大值。
      實現效果,比較理想:
 
 
五、小結反思
1、掌握知識,如果不能歸納數學模型,並且編碼實現,不叫真正掌握;
2、分析研究,如果從簡單的情況開始,控制變量,往往能夠左右逢源。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM