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 
forint j = 0; j < dst_norm.rows ; j ++ )  {
         forint 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 ); 
}  
}
得到結果
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、分析研究,如果從簡單的情況開始,控制變量,往往能夠左右逢源。

P.S 網友提問
  好,測試的圖片是這樣的,需要把圖片里的2段圓弧與2條直線分割出來, 《基於輪廓尖銳度的圖像角點檢測算法》所實現的算法,不清楚能不能實現
這個結果進行處理的話,根據“支撐點”的基本定義,應該只能夠得到這樣的結果:
如果后面關於圓弧和直線的區分,可能要做斜率研究,較為適宜。
附上全部代碼:
#include <opencv2\highgui.hpp>
#include <opencv2\opencv.hpp>
#include "GOCVHelper.h"
#include <iostream>
using namespace std;
using namespace cv;
int main() {
    Mat src = imread("e:/sandbox/測試圖片.jpg"IMREAD_COLOR);
    Mat gray;
    Mat board(src.size()src.type(), Scalar::all(0));
    vector<VPcontours;
    //灰度域變化
    cvtColor(srcgrayCOLOR_BGR2GRAY);
    threshold(graygray, 100, 255, THRESH_OTSU);
    bitwise_not(graygray);
    GaussianBlur(graygraySize(3, 3), 0, 0);
    //尋找和繪制輪廓
    VP bigestContour = FindBigestContour(gray);
    contours.push_back(bigestContour);
    //遍歷輪廓,求出所有支撐角度
    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(papb);
        float fb = getDistance(papc) + getDistance(pbpc);
        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(boardbigestContour[imax], 10, Scalar(255), 1);
                circle(srcbigestContour[imax], 10, Scalar(0, 0, 0), 1);
                imax = -1;
                fmax = -1;
                bstart = false;
            }
        }
    }
    imshow("src"src);
    waitKey(0);
}








免責聲明!

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



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