链码表和线段表都可以描述轮廓,通过轮廓跟踪得到链码表,链码表可以很容易计算出轮廓长度,但轮廓填充,计算面积、重心等使用线段表会更加容易。为了后续处理的方便,往往需要将链码表转换为线段表。线段表只需要记录每条线段的两个端点坐标,中间点全部舍弃。链码表上的点如果位于线段的中间则舍弃,如果位于线段的某一个端点则保留,如果位于线段的两个端点(即线段长度为1)则需要插入一个端点,最后按照垂直方向和水平方向排序。 在程序中判断链码表上的点在线段上的位置关系比较麻烦,且位置关系只有64种,因此采用查表法简单高效,不失为极佳方案。同样,事先无法确定线段数量,可以使用vector来动态管理内存。
#include <vector> #include <algorithm>
typedef std::vector<int> ivecChainCode; typedef std::vector<POINT> ptvecLineTable;
struct CPointCompare { bool operator()(const POINT& p1, const POINT& p2) { return (p1.y < p2.y || (p1.y == p2.y && p1.x < p2.x)); } };
// 转换为线段表 // 1. ptStart 起始点 // 2. ChainCode 链码表 // 3. pLineTable 线段表 BOOL ToLineTable(POINT ptStart, ivecChainCode ChainCode, ptvecLineTable *pLineTable) { POINT ptCurrent = { 0, 0 }; const POINT ptOffset[8] = { { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 } }; int nTable[8][8] = { { 0, 2, 2, 2, 2, 0, 0, 0 }, { 0, 2, 2, 2, 2, 3, 0, 0 }, { 0, 2, 2, 2, 2, 3, 3, 0 }, { 0, 2, 2, 2, 2, 3, 3, 3 }, { 1, 0, 0, 0, 0, 1, 1, 1 }, { 1, 3, 0, 0, 0, 1, 1, 1 }, { 1, 3, 3, 0, 0, 1, 1, 1 }, { 1, 3, 3, 3, 0, 1, 1, 1 } }; ivecChainCode::iterator i, j; // 清空线段表 pLineTable->clear(); ptCurrent.x = ptStart.x; ptCurrent.y = ptStart.y; if (ChainCode.empty()) { // 独立点 pLineTable->push_back(ptCurrent); pLineTable->push_back(ptCurrent); } else { // 查表法转换 for (i = ChainCode.begin(), j = i + 1; i != ChainCode.end(); i++, j++) { if (j == ChainCode.end()) { j = ChainCode.begin(); } ptCurrent.x += ptOffset[*i].x; ptCurrent.y += ptOffset[*i].y; switch (nTable[*i][*j]) { case 0: break; case 1: case 2: pLineTable->push_back(ptCurrent); break; case 3: pLineTable->push_back(ptCurrent); pLineTable->push_back(ptCurrent); break; } } // 排序 sort(pLineTable->begin(), pLineTable->end(), CPointCompare()); } return TRUE; }
// 轮廓填充 // 1. pImageData 图像数据 // 2. nWidth 图像宽度 // 3. nHeight 图像高度 // 4. nWidthStep 图像行大小 // 5. LineTable 线段表 BOOL FillContour(unsigned char *pImageData, int nWidth, int nHeight, int nWidthStep, ptvecLineTable LineTable) { for (ptvecLineTable::iterator i = LineTable.begin(); i != LineTable.end(); i += 2) { for (long x = i->x; x <= (i + 1)->x; x++) { pImageData[nWidthStep * i->y + x] = 0xFF; } } return TRUE; }
链码表转换为线段表并填充的效果:
|
zhangshaobing517_935512703 2009-2-21 16:53