該算法來源於論文《二值圖像中目標物體輪廓的邊界跟蹤算法》,在實際使用中速度很快,效果很好。
該算法基於常見的“蟲隨”法進行改進,具有以下優點:
- 既能有效跟蹤外邊界又能克服內邊界的存在對算法的影響。
- 對於不封閉的線段能實現很好的跟蹤
算法思路:
邊界跟蹤從二值圖像的左上角點還是逐點掃描,當遇到目標點是開始順序跟蹤。
封閉輪廓
直至跟蹤的後續點回到起始點。
對於非封閉輪廓
其後續點再沒有新的後續點爲止,跟蹤到一側的終結後需從起始點還是朝相反的方向跟蹤到另一終結點。
當存在多個分離的輪廓時,逐個跟蹤,爲避免陷入死循環,前面的區域處理好後應採用背景色對它進行填充。
本文程序僅實現對於封閉輪廓的跟蹤。
查找鄰域
設當前點P(x,y)在上一邊界點C的8鄰域內的位置編碼爲n,則從當前點(x,y)的8領域內的編碼爲n的位置,順時針方向移動2個像素的位置就是下一邊界點的起始位置。
使用數組來實現方向移動:
static const CvPoint icvCodeDeltas[8] =
{ {1, 0},{ 1, -1},{ 0, -1},{-1, -1},{ -1, 0 },{-1, 1},{0, 1},{1, 1}};
搜索:
int searchNeighbor(IplImage * src, int &x, int &y, int position_code)
{
int deltas[8];
CV_INIT_3X3_DELTAS(deltas, src->widthStep, 1);
uchar *p = (uchar *)(src->imageData + y * src->widthStep + x);
int start = (position_code + 6) % 8;
for (int i = 0; i < 5; i++)
{
int index = start % 8;
if (*(p + deltas[index]) == 255)
{
x += icvCodeDeltas[index].x;
y += icvCodeDeltas[index].y;
return index;
}
start++;
}
return -1;
}
遍歷
for (int i = 0; i < src->height; i++)
{
uchar *p_line = (uchar *)(src->imageData + src->widthStep *i);
for (int j = 0; j < src->width; j++){
if (p_line[j] == 255 && p_line[j - 1] == 0){
int x = j, y = i;
//position code
int pc = 0;
while (1){
pc = searchNeighbor(src, x, y, pc);
//mark
*(src->imageData + src->widthStep * y + x) = 1;
//pc == -1: there is no point next
//x == j && y == i back to start point
if (pc == -1 || (x == j && y == i)){
break;}
}
}
}
}