二值映像輪廓跟蹤
二值映像輪廓跟蹤用於提取二值映像中的目的地區域,以便對目的地區域做進一步處理,如:地區填
充,計算輪廓長度、面積、重心,特徵提取和Image Recognition等。我們可以採用鏈碼和線段表兩種不同的方法來描述輪廓。鏈碼方式描述輪廓需要記錄輪廓的起點和輪廓上
每一點相對於前一點的鏈碼值列表。以8-領域鏈碼為例,首先按照從左至右,從上至下的順序尋找輪廓起點;再按照右、右下、下、左下的順序尋找第二個輪廓
點;然後按照右、右下、下、左下、左、左上、上、上右的順序尋找其它輪廓點,直到找到輪廓起點為止。
由於事先無法確定輪廓點數量,無法事先分配合適的記憶體空間,因此可以使用vector來動態管理記憶體,從而簡化程式設計。
#include <vector><br />typedef std::vector<int> ivChainCode;<br />// 輪廓跟蹤<br />// 1. pImageData 映像資料<br />// 2. nWidth 映像寬度<br />// 3. nHeight 映像高度<br />// 4. nWidthStep 映像行大小<br />// 5. pStart 起始點<br />// 6. pChainCode 鏈碼錶<br />bool TracingContour(unsigned char *pImageData, int nWidth, int nHeight, int nWidthStep,<br /> POINT *pStart, ivChainCode *pChainCode)<br />{<br /> int i = 0;<br /> int j = 0;<br /> int k = 0;<br /> int x = 0;<br /> int y = 0;<br /> bool bTracing = false;<br /> POINT ptCurrent = { 0, 0 };<br /> POINT ptTemp = { 0, 0 };<br /> unsigned char *pLine = NULL;<br /> const POINT ptOffset[8] =<br /> {<br /> { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 },<br /> { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }<br /> };<br /> // 清空起始點與鏈碼錶<br /> pStart->x = 0;<br /> pStart->y = 0;<br /> pChainCode->clear();<br /> // 輪廓起點<br /> for (y = 0; y < nHeight; y++)<br /> {<br /> pLine = pImageData + nWidthStep * y;<br /> for (x = 0; x < nWidth; x++)<br /> {<br /> if (pLine[x] == 0xFF)<br /> {<br /> bTracing = true;<br /> pStart->x = x;<br /> pStart->y = y;<br /> ptCurrent.x = x;<br /> ptCurrent.y = y;<br /> }<br /> }<br /> }<br /> // 輪廓跟蹤<br /> while (bTracing)<br /> {<br /> bTracing = false;<br /> for (i = 0; i < 8; i++, k++)<br /> {<br /> k &= 0x07;<br /> x = ptCurrent.x + ptOffset[k].x;<br /> y = ptCurrent.y + ptOffset[k].y;<br /> if (x >= 0 && x < nWidth && y >= 0 && y < nHeight)<br /> {<br /> // 判斷是否為輪廓點<br /> if (pImageData[nWidthStep * y + x] == 0xFF)<br /> {<br /> for (j = 0; j < 8; j += 2)<br /> {<br /> ptTemp.x = x + ptOffset[j].x;<br /> ptTemp.y = y + ptOffset[j].y;<br /> if (ptTemp.x >= 0 && ptTemp.x < nWidth &&<br /> ptTemp.y >= 0 && ptTemp.y < nHeight)<br /> {<br /> if (pImageData[nWidthStep * ptTemp.y + ptTemp.x] == 0)<br /> {<br /> bTracing = true;<br /> ptCurrent.x = x;<br /> ptCurrent.y = y;<br /> pChainCode->push_back(k);<br /> break;<br /> }<br /> }<br /> }<br /> }<br /> }<br /> if (bTracing)<br /> {<br /> // 如果當前點為輪廓起點<br /> if (pStart->x == ptCurrent.x && pStart->y == ptCurrent.y)<br /> {<br /> // 則跟蹤完畢<br /> bTracing = false;<br /> }<br /> break;<br /> }<br /> }<br /> k += 0x06;<br /> }<br /> return true;<br />}<br />// 輪廓繪製<br />// 1. pImageData 映像資料<br />// 2. nWidth 映像寬度<br />// 3. nHeight 映像高度<br />// 4. nWidthStep 映像行大小<br />// 5. ptStart 起始點<br />// 6. ChainCode 鏈碼錶<br />bool DrawContour(unsigned char *pImageData, int nWidth, int nHeight, int nWidthStep,<br /> POINT ptStart, ivChainCode ChainCode)<br />{<br /> POINT ptCurrent = { 0, 0 };<br /> const POINT ptOffset[8] =<br /> {<br /> { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 },<br /> { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }<br /> };<br /> // 清空映像<br /> memset(pImageData, 0, nWidthStep * nHeight);<br /> // 輪廓繪製<br /> ptCurrent.x = ptStart.x;<br /> ptCurrent.y = ptStart.y;<br /> pImageData[nWidthStep * ptCurrent.y + ptCurrent.x] = 0xFF;<br /> for (ivChainCode::iterator i = ChainCode.begin(); i != ChainCode.end(); i++)<br /> {<br /> ptCurrent.x += ptOffset[*i].x;<br /> ptCurrent.y += ptOffset[*i].y;<br /> pImageData[nWidthStep * ptCurrent.y + ptCurrent.x] = 0xFF;<br /> }<br /> return true;<br />}<br />