opencv 八鄰域輪廓跟蹤演算法

opencv 八鄰域輪廓跟蹤演算法

影象處理中,往往需要用到輪廓跟蹤解決一些問題,諸如輪廓點的排序得到相鄰點。當使用findContours()查詢輪廓的時候,我們希望得到Freeman碼,但是實驗發現並不是一個引數CV_CHAIN_CODE那麼回事,網上一大堆的蜻蜓點水,說這個引數可以實現Freeman碼生成,我怎麼就沒搜到一個生成結果,哪怕告訴我怎麼才能得帶到Freeman碼,多方查閱相關書籍,比如朱偉的《opencv影象處理程式設計例項》,在findContours函式中沒有指出這個引數CV_CHAIN_CODE,在影象處理大神毛星雲的《opencv3影象處理》也沒有看見這個引數。然後發現在opencv中,老版本的cvFindcontours貌似可以實現,網上查詢很多,也是沒有完整的解釋和相應的程式碼可以得到Freeman碼,很傷心,不開心。沒辦法,自己想得到類似Freeman碼只能自己創造。所以就看見了八鄰域輪廓跟蹤可以得到它下一個輪廓點在哪裡。
關於八鄰域概念這裡不再多說,網上這種就很多了。八鄰域跟蹤和八鏈碼跟蹤不一樣,前者是不知道下一個點在哪裡,要得到下一個點在它的那個位置,而八鏈碼跟蹤是知道了方向,可以得到它下一個點的位置(這只是我個人理解,可能也是錯的,哈哈)。網上也有相應的八鄰域程式碼,寫的很好,我大部分程式碼就是複製過來的,再加工一下,就可以滿足自己的需要了。文章連結文章連結,這篇文章的程式碼比較通俗易懂,但是是老版本的影象型別,需要改進加工。那我也上我的程式碼,不多說。
	int direction[8][2] = {{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}};
vector<FM> FM(contours[0].size());
vector<int>d(contours[0].size());
vector<int>k(contours[0].size());
int CornersN=300;
vector<Point2f>vecCorners(CornersN);
int startDirect=0 ;
int FindStartPoint = 0;
int FindPoint = 0;
//開始跟蹤
int X = startX;
int Y = startY;
int NextPtValue;
int nextX,nextY;
int i=0;
while(!FindStartPoint)
{
FindPoint=0;
while(!FindPoint)
{
//沿預定方向掃描,尋找下一個點
nextX=X direction[startDirect][1];
nextY = Y   direction[startDirect][0];
NextPtValue=drawing.at<uchar>(nextX,nextY);
if (NextPtValue != 0 && nextX>=0 && nextY>=0)
{
FM[i].ai=startDirect;
FM[i].x=nextY;
FM[i].y=nextX;
i  ;
FindPoint = 1;
X = nextX;
Y = nextY;
if (X == startX && Y == startY)
{
FindStartPoint = 1;
}
startDirect = startDirect-2;
if(startDirect<0)
startDirect = startDirect 8;
}
else
{
startDirect = startDirect 1;
if (startDirect>=8) 
startDirect = startDirect-8;
}
}
}
程式碼是部分程式碼,插入你的程式碼就好了,可能有些變數沒有定義,可以自己找到加定義。程式碼中FM是我自己定義的一個結構體,看程式碼應該就看懂FM裡面有什麼,包括x,y,ai分別表示點的x,y和它在它前一個點的方向。directiong[8][2]二維陣列是八個方向,首先是水平向右為起始,以逆時針為前進方向,結合八鄰域那個圖就懂了。找到了下一個點重新賦值,同時順時針後退兩個方向,沒找到逆時針前進一個方向。道理就是我畫個圖先:
首先如果起始點是最左上角的點,它的方向startDirect開始指向0,沒找到,然後1,依次到7,找到了它的下一個點,然後方向還是7,要順時針退兩個方向,就是減2,變成起始點為5了,因為5上面的4已經在上一個點查詢過了,不需要再去查詢了。說完了,可能還有一些沒說到的,基本思路那篇文章都說了一些,應該都比較好懂的。
文章有些不妥之處,望大家批評指正,謝謝大家!