1. 程式人生 > >二維凸包convex hull之C++及OpenCV實現

二維凸包convex hull之C++及OpenCV實現

打算接下來好好研究下演算法(很明顯,演算法才是王道啊),然後儘量用直觀的方式輸出,於是用OpenCV畫圖成了不二首選,各位看官接下來看到一堆“XXX之C++及OpenCV實現”之類的標題就別見怪了~

另外還有個打算,看到自己寫的東西被別人拿去佔為己有,不爽,開始貼版權了^_^。

本文出處:http://blog.csdn.net/xizhibei

============================================================

今天就是二維凸包,演算法導論中文版584頁說的就是凸包,現在,讓我們來實現它。

話說,凸包在很多地方有著重要的作用,如手勢識別,需要識別出手的輪廓的凸包,二維或者三維區域的邊界等等。

而對於凸包演算法,其中最有名的莫過於Graham掃描演算法,它的複雜度為nlog(n),過程很優美,相信你看過執行過程你就會同樣覺得了。

簡單來說,這個演算法的過程就是這樣:

1.計算求得輸入點x座標最小(如果x相等,則比較y是不是最小)的點,作為第一個點

2.其它的點按照極角按順時針排列,如果有共線的,取最近的那一個

3.依次將0,1,2三個座標壓入棧

4.對於剩餘的點,i依次從3到最後,看次棧頂,棧頂,以及當前的i對應的點,如果是非左轉動,那麼彈棧

5.將i對應的點壓棧,轉到3

好了,現在開始實現:

下面是一些標頭檔案/定義以及簡單函式實現

其中值得一提的便是叉積,二維向量A和B的叉積就是A.x * B.y - A.y *B.x ,結果為正就表明A經過順時針到達B(當然,小於180),反之則逆時針,憑藉這種演算法,就可以按極角排序了。

[cpp]view plaincopyprint?
  1. #include <cv.h>
  2. #include <cxcore.h>
  3. #include <highgui.h>
  4. #define POINT_NUM 100
  5. #define WIDTH 800
  6. #define HEIGHT 800
  7. #define zero 1e-12
  8. #define DIS(a,b) sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y))
  9. #define SGN(x) (fabs(x)<zero?0:(x>0?1:-1))
  10. #define CROSS(a,b,c) ((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))//叉積,用來判斷旋轉方向
  11. #define CMP(a,b) (a.x < b.x || SGN(a.x - b.x)==0 && a.y < b.y)//座標的比較
  12. #define RAND (rand() % 100000 / 100000.0)//產生0-1之間的浮點數
  13. CvPoint p[POINT_NUM];  
  14. CvPoint* hull_p = new CvPoint[POINT_NUM];//用來儲存凸包上的點
  15. int hull_size = 0;  
  16. IplImage* img;  
  17. //下面簡單實現了棧操作
  18. inlinevoid push(CvPoint* S,CvPoint pt)  
  19. {  
  20.     S[hull_size++] = pt;  
  21. }  
  22. inline CvPoint pop(CvPoint* S)  
  23. {  
  24.     return S[--hull_size];  
  25. }  
  26. inlinevoid swap(int x,int y)  
  27. {  
  28.     CvPoint pt = p[x];  
  29.     p[x] = p[y];  
  30.     p[y] = pt;  
  31. }  
  32. inlinebool compare(CvPoint a,CvPoint b,CvPoint c)  
  33. {  
  34.     int tmp = SGN(CROSS(a,b,c));  
  35.     if(tmp != 0)  
  36.         return tmp > 0;  
  37.     else//如果兩點共線的話,就需要比較遠近了
  38.         return DIS(a,b) < DIS(a,b);  
  39. }  
  40. //快排,極角的排序
  41. void sort(int l,int r)  
  42. {  
  43.     CvPoint tmp = p[(l + r) / 2];  
  44.     int i = l;  
  45.     int j = r;  
  46.     do
  47.     {  
  48.         while(compare(p[0],p[i],tmp))i++;  
  49.         while(compare(p[0],tmp,p[j]))j--;  
  50.         if(i <= j)  
  51.         {  
  52.             swap(i,j);  
  53.             i++;  
  54.             j--;  
  55.         }  
  56.     }while(i <=j);  
  57.     if(i < r)sort(i,r);  
  58.     if(j > l)sort(l,j);  
  59. }  
然後就是重點了:

這裡也跟上篇文章一樣,為了看清執行過程,加上了畫線函式,還有100微秒的延時,這樣就能看清了

[cpp]view plaincopyprint?
  1. void draw_hull()  
  2. {  
  3.     int min = -1;  
  4.     for(int j = 0;j < POINT_NUM;j++)//找出x座標最小的,作為起始點
  5.     {  
  6.         if(min == -1 || CMP(p[j],p[min]))  
  7.             min = j;  
  8.     }  
  9.     if(min != 0)  
  10.         swap(0,min);  
  11.     sort(1,POINT_NUM - 1);//其他點排序
  12.     push(hull_p,p[0]);  
  13.     push(hull_p,p[1]);  
  14.     push(hull_p,p[2]);  
  15.     for(int i = 3;i < POINT_NUM;i++)  
  16.     {  
  17.         while(CROSS(hull_p[hull_size - 2],hull_p[hull_size - 1],p[i]) < 0)//非左轉
  18.         {  
  19.             pop(hull_p);  
  20.             cvLine(img,hull_p[hull_size - 1],p[i],cvScalar(255,0,255));//為了看清執行過程而加的
  21.             cvShowImage("Image",img);  
  22.             cvWaitKey(100);  
  23.         }  
  24.         cvLine(img,hull_p[hull_size - 1],p[i],cvScalar(255,0,255));  
  25.         push(hull_p,p[i]);  
  26.     }  
  27.     cvPolyLine(img,&hull_p,&hull_size,1,1,cvScalar(0,0,255),2);//最終畫出凸包
  28. }  

顯示圖片: [cpp]view plaincopyprint?
  1. void show_outcome()  
  2. {  
  3.     cvSet(img,cvScalar(255,255,255));  
  4.     CvScalar color = cvScalar(0,0,0);  
  5.     for(int i = 0;i < POINT_NUM;i++)//畫出每個點,十字
  6.     {  
  7.         int x = p[i].x;  
  8.         int y = p[i].y;  
  9.         cvLine(img,cvPoint(x - 5,y),cvPoint(x + 5,y),color,2);  
  10.         cvLine(img,cvPoint(x,y - 5),cvPoint(x,y + 5),color,2);  
  11.     }  
  12.     draw_hull();  
  13.     cvShowImage("Image",img);  
  14.     cvWaitKey(0);  
  15. }  

然後是主函式,這次的隨機點生成換了個方式,點隨機分佈在左邊的橢圓以及右邊的圓上,不再象上次那樣隨機產生在矩形區域內了: [cpp]view plaincopyprint?
  1. int main()  
  2. {  
  3.     img = cvCreateImage(cvSize(WIDTH,HEIGHT),IPL_DEPTH_8U,3);  
  4.     srand((unsigned)time(NULL));  
  5.     double phase = RAND * CV_PI * 2.0;  
  6. 相關推薦

    convex hullC++OpenCV實現

    打算接下來好好研究下演算法(很明顯,演算法才是王道啊),然後儘量用直觀的方式輸出,於是用OpenCV畫圖成了不二首選,各位看官接下來看到一堆“XXX之C++及OpenCV實現”之類的標題就別見怪了~ 另外還有個打算,看到自己寫的東西被別人拿去佔為己有,不爽,開始貼版權

    P2742 【模板】 / [USACO5.1]圈奶牛Fencing the Cows

    href sin const int can cross pri fin 二維 傳送門 二維凸包的板子 //minamoto #include<bits/stdc++.h> #define rint register int #define inf 0x3f3f

    快速和改進的演算法及其在O(n log h)中的實現實現部分)

    此篇接上一篇部落格http://blog.csdn.net/firstchange/article/details/78588669 實施選擇 陣列與列表 “List”類是一個C#集合,它使用一個數組作為其底層容器。使用“列表”而不是陣列應該有類似的效能。測試證實,

    快速和改進的演算法及其在O(n log h)中的實現(理論部分)

    在國外某知名網站上瀏覽資訊時發現了一篇非常好的論文,因為是英文的,自己翻譯、整理了一下,如果想看原始的可以去以下連結:https://www.codeproject.com/Articles/1210225/Fast-and-improved-D-Convex-Hull-algorithm-

    計算幾何 問題 Andrew演算法

    凸包:把給定點包圍在內部的、面積最小的凸多邊形。 Andrew演算法是Graham演算法的變種,速度更快穩定性也更好。 首先把所有點排序,按照第一關鍵字x第二關鍵字y從小到大排序,刪除重複點後得到點序列P1...Pn。 1)把P1,P2放入凸包中,凸包中的點使用棧儲存 2)從p3開始

    分治法求問題java

    https://blog.csdn.net/bone_ace/article/details/46239187 見解法2。 有一點需要說明的是,如果有多個到直線p1 pn的距離都最大的點,找pmax使的∠pmax p1 pn最大 下面為java程式碼 package fd; impo

    求解(Andrew演算法 )

    Andrew演算法是Graham演算法的變種。 其主要思想為把凸包上的點依次放入棧中, 如果發現形成了凹多邊形(叉積為負值) 就刪除一些點,使得又能夠維持凸的形態。 這時就會發現,處理各個點需要按照x從左往右的順序,排序即可 當然,這只是處理了下凸的一個凸殼,倒

    的板子

    二維凸包的板子 感覺沒什麼可說的,碼風抄的孔老師的。 #include <cstdio> #include <algorithm> #include <cmath> const int N=1e4+10; #define Point Vector const doubl

    USACO FC,

    #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> const int maxn=10000+10; using namespace std; struct

    P2742-/圈奶牛Fencing the Cows【

    正題 題目連結:https://www.luogu.org/recordnew/lists?uid=SSL_WYC_zombieeeeee&pid=P2742&status=&sort=0 題目大意 求凸包總長度 解題思路 求凸包

    【洛谷P2742】【模板】/[USACO5.1]圈奶牛【

    題目大意: 題目連結:https://www.luogu.org/problemnew/show/P2742 求二維平面上的凸包。 思路: 二維凸包模板題。在這裡就不講述凸包的概念和做法了。需要的話可以看本題題解。 採用的是

    【模板】 / [USACO5.1]圈奶牛Fencing the Cows

    坐標系 sca printf can || The cows 模板 get Problem surface 戳我 Meaning 坐標系內有若幹個點,問把這些點都圈起來的最小凸包周長。 這道題就是一道凸包的模板題啊,只要求出凸包後在計算就好了,給出幾個註意點 記得檢查是否

    尋找 convex hull(一)

         今天學習OpenCV2中的ConvecHull函式連線如下:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/shapedescriptors/hull/hull.html#hu

    【洛谷 P3187】 [HNOI2007]最小矩形覆蓋 (,旋轉卡殼)

    ref scanf const 維護 math int() 一個 數據 pre 題目鏈接 嗯,毒瘤題。 首先有一個結論,就是最小矩形一定有條邊和凸包重合。腦補一下就好了。 然後枚舉凸包的邊,用旋轉卡殼維護上頂點、左端點、右端點就好了。 上頂點用叉積,叉積越大三角形面積越大,

    問題的分治演算法python實現

    利用分治思想處理凸包問題。 劃分:將點集S中的資料按橫座標排序,選出橫座標最小的點A和縱座標最小的點B,AB的連線將S劃分為兩個子集,位於(AB) 上方的集合S1和位於(BA) 上方的集合S2。 遞迴求解:遞迴呼叫演算法求S1的凸包和S2的凸包,遞迴演算

    hdu3662 3D Convex Hull(三【三計算幾何基本操作)

    題目連線 分析: 三維凸包模板 瞧好了基本操作 三維和二維簡直不是一個級別的。。。orz #include<bits/stdc++.h> using namespace std; const double eps=1e-8; con

    計算幾何_三(3d convex hull)

    const double eps = 1e-8; typedef list<int>::iterator liit; inline int sign(double d){ if(d < -eps) return -1; return (d > e

    bzoj1964: hull

    typename opera ifdef ide esp urn print family www 傳送門 二維平面四個點求凸包面積->任選三個點面積之和/2 三維平面五個點求凸包體積->任選四個點體積之和/2 二維平面三個點面積->二個二維向量行列式

    D - FATE HDU-2159 FATE

    text panel ret sub lin printf print %d tdi D - FATE Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Sub

    HDU-2159

    裝備 urn 背包 stdio.h namespace logs set style bre 最近xhd正在玩一款叫做FATE的遊戲,為了得到極品裝備,xhd在不停的殺怪做任務。久而久之xhd開始對殺怪產生的厭惡感,但又不得不通過殺怪來升完這最後一級。現在的問題是,xhd