OpenCV C++(九)----幾何形狀的檢測和擬合

9.1、點集的最小外包

點集是坐標點的集。

9.1.1、最小外包旋轉(zhuǎn)矩形

//點集
Mat points = (Mat_<float>(5, 2) << 1, 1, 5, 1, 1, 10, 5, 10, 2, 5);
//計算點集的最小外包旋轉(zhuǎn)矩陣
RotatedRect rRect = minAreaRect(points);
//打印旋轉(zhuǎn)矩形的信息
cout << "旋轉(zhuǎn)矩形的角度" << rRect.angle << endl;
cout << "旋轉(zhuǎn)矩形的中心" << rRect.center << endl;
cout << "旋轉(zhuǎn)矩形的尺寸" << rRect.size << endl;

9.1.2、旋轉(zhuǎn)矩形的四個頂點

OpenCV3新特性

void boxPoints(RotatedRect box,OutputArray points)

便于計算旋轉(zhuǎn)矩形的四個頂點,這樣就可以使用函數(shù)line畫出四個頂點的連線,從而畫出旋轉(zhuǎn)矩形。

9.1.3、最小外包圓

void minEnclosingCircle( InputArray points,CV_OUT Point2f& center, CV_OUT float& radius );

9.1.4、最小外包直立矩形

Rect boundingRect( InputArray points );

9.1.5、最小凸包

凸包是將最外層的點連接起來構(gòu)成的凸多邊形,它能包含點集的所有點。

void convexHull( InputArray points, OutputArray hull,
                              bool clockwise = false, bool returnPoints = true );

利用該函數(shù)求出的凸包,坐標點的順序不是隨機排列的,而是按照某順序排列的,也就是把這些點依次相連就可以得到連線。

9.1.6、最小外包三角形

OpenCV3新特性

double minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle );

    //5行2列的單通道Mat
    Mat point = (Mat_<int>(5, 2) << 1, 1, 5, 1, 1, 10, 5, 10, 2, 5);
    //轉(zhuǎn)換為雙通道矩陣
    point = point.reshape(2, 5);
    //存儲三角形的三個頂點
    vector<Point> triangle;
    //點集的最小外包三角形
    double area;
    area = minEnclosingTriangle(point, triangle);
    //打印結(jié)果
    cout << "三角形的三個頂點";
    for (int i = 0; i < 3; i++)
    {
        cout << triangle[i] << ",";
    }
    cout << "面積" << area << endl;

9.2、霍夫直線檢測

如果知道原點到一條直線的代數(shù)距離ρ和與x軸的夾角θ,則直線方程可由以下方式表示:

image.png

當然, 反過來也可以, 如果知道平面內(nèi)的一條直線, 那么可以計算出唯一的ρ和θ, 即xoy平面內(nèi)的任意一條直線對應(yīng)參數(shù)空間(或稱霍夫空間) θoρ中的一點(ρ, θ) 。

image.png
image.png

Hough直線檢測的基本原理在于利用點與線的對偶性?;舴蜃儞Q運用兩個坐標空間之間的變換,將在一個空間中具有相同形狀的曲線或直線映射到另一個坐標空間的一個點上形成峰值,從而把檢測任意形狀的問題轉(zhuǎn)化為統(tǒng)計峰值問題。

結(jié)論:判斷xoy平面內(nèi)哪些點是共線的,首先求出每一個點對應(yīng)到霍夫空間的曲線,然后判斷哪幾條曲線相交于一點,最后將相交于一點的曲線反過來對應(yīng)到xoy平面內(nèi)的點,這些點是共線的。

在理論上,一個點對應(yīng)無數(shù)條直線或者說任意方向的直線(在參數(shù)空間中坐標軸表示的斜率k或者說θ有無數(shù)個),但在實際應(yīng)用中,我們必須限定直線的數(shù)量(即有限數(shù)量的方向)才能夠進行計算。

因此,我們將直線的方向θ離散化為有限個等間距的離散值,參數(shù)ρ也就對應(yīng)離散化為有限個值,于是參數(shù)空間不再是連續(xù)的,而是被離散量化為一個個等大小網(wǎng)格單元。將圖像空間(直角坐標系)中每個像素點坐標值變換到參數(shù)空間(極坐標系)后,所得值會落在某個網(wǎng)格內(nèi),使該網(wǎng)格單元的累加計數(shù)器加1。當圖像空間中所有的像素都經(jīng)過霍夫變換后,對網(wǎng)格單元進行檢查,累加計數(shù)值最大的網(wǎng)格,其坐標值(ρ0, θ0)就對應(yīng)圖像空間中所求的直線。

如下圖解

image.png

總結(jié):使用霍夫變換檢測直線具體步驟:
1.彩色圖像->灰度圖
2.去噪(高斯核)
3.邊緣提取(梯度算子、拉普拉斯算子、canny、sobel
4.二值化(判斷此處是否為邊緣點,就看灰度值==255)
5.映射到霍夫空間(準備兩個容器,一個用來展示hough-space概況,一個數(shù)組hough-space用來儲存voting的值,因為投票過程往往有某個極大值超過閾值,多達幾千,不能直接用灰度圖來記錄投票信息)
6.取局部極大值,設(shè)定閾值,過濾干擾直線
7.繪制直線、標定角點

優(yōu)點:Hough直線檢測的優(yōu)點是抗干擾能力強,對圖像中直線的殘缺部分、噪聲以及其它共存的非直線結(jié)構(gòu)不敏感,能容忍特征邊界描述中的間隙,并且相對不受圖像噪聲的影響

缺點:Hough變換算法的特點導(dǎo)致其時間復(fù)雜度和空間復(fù)雜度都很高,并且在檢測過程中只能確定直線方向,丟失了線段的長度信息。由于霍夫檢測過程中進行了離散化,因此檢測精度受參數(shù)離散間隔制約

OpenCV支持三種霍夫直線檢測算法:
1)Standard Hough Transform(SHT,標準霍夫變換)
2)Multiscale Hough Transform(MSHT,多尺度霍夫變換)
3)Progressive Probability Houth Transform(PPHT,漸進概率式霍夫變換)

在OpenCV3.0及以上版本中,霍夫直線檢測算法定義了兩個函數(shù):HoughLines(1、2)、HoughLinesP(3)

CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines, 
  double rho, double theta, int threshold, 
  double srn = 0, double stn = 0, 
  double min_theta = 0, double max_theta = CV_PI );
  
  //InputArray image:輸入圖像,必須是8位單通道圖像。 
  //OutputArray lines:檢測到的線條參數(shù)集合。 
  //double rho:以像素為單位的距離步長。 
  //double theta:以弧度為單位的角度步長。 
  //int threshold:累加計數(shù)值的閾值參數(shù),當參數(shù)空間某個交點的累加計數(shù)的值超過該閾值,則認為該交點對應(yīng)了圖像空間的一條直線。 
  //double srn:默認值為0,用于在多尺度霍夫變換中作為參數(shù)rho的除數(shù),rho=rho/srn。 
  //double stn:默認值為0,用于在多尺度霍夫變換中作為參數(shù)theta的除數(shù),theta=theta/stn。
  //如果srn和stn同時為0,就表示HoughLines函數(shù)執(zhí)行標準霍夫變換,否則就是執(zhí)行多尺度霍夫變換。

CV_EXPORTS_W void HoughLinesP( InputArray image, OutputArray lines, 
  double rho, double theta, int threshold, 
  double minLineLength = 0, double maxLineGap = 0 ); 
  
  //InputArray image:輸入圖像,必須是8位單通道圖像。 
  //OutputArray lines:檢測到的線條參數(shù)集合。 
  //double rho:直線搜索時的距離步長,以像素為單位。 
  //double theta:直線搜索時的角度步長,以弧度為單位。 
  //int threshold:累加計數(shù)值的閾值參數(shù),當參數(shù)空間某個交點的累加計數(shù)的值超過該閾值,則認為該交點對應(yīng)了圖像空間的一條直線。 
  //double minLineLength:默認值為0,表示最小線段長度閾值(像素)。 
  //double maxLineGap:線段上最近兩點之間的閾值.默認值為0,表示直線斷裂的最大間隔距離閾值。即如果有兩條線段是在一條直線上,但它們之間有間隙,那么如果這個間隔距離小于該值,則被認為是一條線段,否則認為是兩條線段。 

注意:霍夫直線變換是一種用來在圖像空間尋找直線的方法,輸入圖像要求是二值圖像,同時為了提高檢測直線的效率和準確率,在使用霍夫線變換之前,最好對圖像進行邊緣檢測生成邊緣二值圖像,這樣的檢測效果是最好的。

9.3、霍夫圓檢測

9.3.2、標準霍夫圓檢測

與霍夫直線檢測類似, 圖像的霍夫圓檢測就是檢測哪些前景或邊緣像素點在同一個 圓上, 并給出對應(yīng)圓的圓心坐標及圓的半徑; 而且仍然需要計數(shù)器來完成該過程, 只是 這里的計數(shù)器從二維變成了三維。

9.3.2、基于梯度的霍夫圓檢測

步驟:首先定位圓心(兩個參數(shù)),然后計算半徑 (一個參數(shù))。在代碼實現(xiàn)中,首先構(gòu)造一個二維計數(shù)器,然后再構(gòu)造一個一維計數(shù)器。

OpenCV提供的函數(shù)HoughCircles實現(xiàn)了基于梯度的霍夫圓檢測, 在該函數(shù)的實現(xiàn)過 程中, 使用了Sobel算子且內(nèi)部實現(xiàn)了邊緣的二值圖, 所以輸入的圖像不用像函數(shù) HoughLinesPHoughLines一樣必須是二值圖。

CV_EXPORTS_W void HoughCircles( InputArray image, OutputArray circles,
                               int method, double dp, double minDist,
                               double param1 = 100, double param2 = 100,
                               int minRadius = 0, int maxRadius = 0 );

缺點是在不知道一些先驗知識的情況下, 需要多次調(diào)整參數(shù)才有可能得到我們想要的結(jié)果。

對于霍夫變換不限于對直線、圓進行檢測,也可以對橢圓等其他幾何形狀進行擬合。同時從原理中可以看出,霍夫變換的一個較大的優(yōu)點就是可以檢測出部分或者遮擋的直線和圓,當然其他幾何形狀也可以。

9.4、輪廓

9.4.1、查找、擬制輪廓

輪廓:有序點集

CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset = Point());

CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
                              int contourIdx, const Scalar& color,
                              int thickness = 1, int lineType = LINE_8,
                              InputArray hierarchy = noArray(),
                              int maxLevel = INT_MAX, Point offset = Point() );

9.4.2、外包、擬合輪廓

介紹了尋找圖像中輪廓的方法和點集的擬合, 那么這兩部分合起來, 就可以處理圖像目標的定位問題了 。

第一步: 對圖像邊緣檢測或者閾值分割得到二值圖,有時也需要對這些二值圖進行形態(tài)學(xué)處理。
第二步: 利用函數(shù)findContours尋找二值圖中的多個輪廓。
第三步: 對于通過第二步得到的多個輪廓,其中每一個輪廓都可以作為函數(shù) convexHull、minAreaRect等的輸入?yún)?shù),然后就可以擬合出包含這個輪廓的最小凸包、最小旋轉(zhuǎn)矩形等。

Mat img = imread("Koala.jpg", IMREAD_GRAYSCALE);
//step1:邊緣檢測,得到邊緣二值圖
GaussianBlur(img, img, Size(3, 3), 0.5);
Mat binaryImg;
Canny(img, binaryImg, 50, 200);
//step2:邊緣的輪廓
vector<vector<Point>> contours;
findContours(binaryImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//step3:對每一個輪廓進行擬合
for (int i = 0; i < contours.size(); i++)
{
    Rect rect = boundingRect(contours[i]);
    if (rect.area() > 500)
    {
        rectangle(img, rect, Scalar(255));
    }
}   

9.4.3、輪廓的周長和面積

用來計算點集所圍區(qū)域的周長:
參數(shù)closed是指點集是否首尾相接。

CV_EXPORTS_W double arcLength( InputArray curve, bool closed );

用來計算點集所圍區(qū)域的面積

CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false );

9.4.4、點和輪廓的位置關(guān)系

點集可以圍成一個封閉的輪廓, 那么空間中任意一點和這個輪廓無非有三種關(guān)系: 點在輪廓外、 點在輪廓上、 點在輪廓內(nèi)。

CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );

注:measureDist是bool類型,當其值為false時,函數(shù)pointPolygonTest的返回值有三種, 即+1、0、-1, +1代表pt在點集圍成的輪廓內(nèi),0代表pt在點集圍成的輪廓上,-1代表pt在點集圍成的輪廓外;當其值為true時,則返回值為pt到輪廓的實際距離。

9.4.5、輪廓的凸包缺陷

用來衡量凸包的缺陷:

void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects );

convexityDefects代表返回的凸包缺陷的信息,形式為vector< Vec4i>,每一個Vec4i代表一個缺陷,它的四個元素依次代表:缺陷的起點、終點、最遠點的索引及最遠點到凸包的距離。

對凸包的缺陷檢測在判斷物體形狀等方面發(fā)揮著很重要的作用,與凸包缺陷類似的還有如矩形度、橢圓度、 圓度等,它們均是衡量目標物體形態(tài)的度量。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容