一个图像的区域可以有两种表示方法,一种是边界点表示,另一种是区域点表示;现在我已知了一个区域的边界点坐标点vector<POINT>vecBorder,现在我要将这个边界点集合转换为区域点,代码怎么写呢?想了很久还是没有想出来,只好求助大家了。谢谢!
函数:
CovBorderToObj(vector<POINT>* vecObjInner, vector<POINT>& vecObjBorder)
{
}
函数:
CovBorderToObj(vector<POINT>* vecObjInner, vector<POINT>& vecObjBorder)
{
}
1检查边界点是否封闭,交叉等。如果不封闭的增加边界点连起来。边界点排好队。
2边界点,如果左侧第一个非0的dy与右侧第一个非0的dy同号,表示是边界局部上沿或下沿。记0。否则记1。连续的1只记一个。
3从上向下扫描,每行从左到右,每经过一个记1的边界点,表示经过边界,结果值反向。
上面这句话是什么意思啊?非0和dy分别表示什么呢? 谢谢
先呢, 把图片扩大化 (dilation). 用一个盘子做基本结构(structure elements). - 目的是把边界闭合。
然后呢,填充洞,就把整个图片填满。(fill - hole);
再然后,再缩小下。 用同样的结构(erode)。主要是为了把外面扩大的部分除掉。
//==================================================================================
//功能说明:设置两个POINT排序的判定准则,按照y从小到大,y值相等(x值从小到大排序)
//==================================================================================
bool SetSortRule(const POINT pt1, const POINT pt2)
{
if (pt1.y < pt2.y)
{
return true;
}
else if (pt1.y == pt2.y)
{
return (pt1.x < pt2.x);
}
else
{
return false;
}
}
//==================================================================================
//功能说明:设置两个POINT的相等准则
//==================================================================================
bool PointIsEqual(POINT ptFirst, POINT ptSecond)
{
return (ptFirst.x == ptSecond.x && ptFirst.y == ptSecond.y);
}//==================================================================================
//功能说明:对象边界点坐标转换为对象点坐标,对象边界点坐标为内边界
// 支持单连通,边界点>0,边界点被包括在了区域中
//入 口: vecObjBorder 对象边界点坐标
//
//出 口: vecObjInner 对象点(内部)坐标
//==================================================================================
bool CSegmentDataSet::CovBorderToObj(vector<POINT>* vecObjInner, vector<POINT>& vecObjBorder)
{
if (vecObjBorder.size() == 1)
{
vecObjInner->push_back(vecObjBorder.front());
return true;
} // 区域内的点
POINT pt; // 边界链码表
vector<int> vecCode; // 区域线段表
vector<POINT> vecLineTable;
vector<POINT>::iterator iter; // 排序
sort(vecObjBorder.begin(),vecObjBorder.end(),SetSortRule); // 闭合的单连通区域边界点生成链码表
if (!BorderToCode(&vecCode,vecObjBorder))
{
return false;
} // 链码表(4连通或者8连通)转换成线段表
if (!CodeToLineTable(&vecLineTable,vecCode))
{
return false;
}
// 根据线段表统计区域内点的坐标
for (iter = vecLineTable.begin();iter != vecLineTable.end();iter += 2)
{
int xStart =(*iter).x;
int xEnd = (*(iter+1)).x;
for (int x=xStart;x<=xEnd;x++)
{
pt.x = x;
pt.y = (*iter).y;
vecObjInner->push_back(pt);
}
} return true;
}
/*****************************************************************************************************
* 3 2 1
* 4__|__0 8连通示意图
* |
* 5 6 7
*
* 跟踪采用“向右看”的准则,即在跟踪过程中对象区域内部总是在边界前进方向的右侧
*
* 顺时针方向跟踪边界
*
************************************************************************************************ *///==================================================================================
//功能说明:闭合的单连通区域边界点生成链码表
//入 口: vecObjBorder 对象边界点坐标
//
//出 口: vecCode 对象边界点链码表
//==================================================================================
bool CSegmentDataSet::BorderToCode(vector<int>* vecCode, vector<POINT>& vecBorder)
{
if (vecBorder.empty())
{
return false;
}
vecCode->clear(); //逆时针定义中心像素点的8邻域坐标,注意:图像原点位于左上角
//第一列为x方向的偏移,第二列为y方向的偏移
int direction[8][2] = { {1, 0},{1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1},{1, 1}};
//边界起始点,待处理的当前点,当前点的邻域点
POINT startP;
POINT currentP;
POINT neighborP; startP = vecBorder[0]; //为链码表填充头两个单元,存储边界起始点的X,Y坐标
vecCode->push_back(startP.x);
vecCode->push_back(startP.y); //边界跟踪
//从初始边界点开始跟踪
currentP = startP; //邻域点是否是边界点的标识变量
bool isContourP; //定义中心象元点的左方为开始方向,共有8个方向(取值0--7)
int startDirection = 4; //表示还没有找到最初边界点的标识变量
bool findStartPointAgain = false; while (!findStartPointAgain)
{
isContourP = false;
while (!isContourP)
{
neighborP.x = currentP.x + direction[startDirection][0];
neighborP.y = currentP.y + direction[startDirection][1]; vector<POINT>::iterator ite;
ite = find_if(vecBorder.begin(),vecBorder.end(),bind1st(ptr_fun(PointIsEqual), neighborP));
//判断该邻域点是否是边界点
if (ite != vecBorder.end())
{
//从链码标的第三个单元开始填充链码序列
vecCode->push_back(startDirection); isContourP = true;
currentP.x = neighborP.x;
currentP.y = neighborP.y; //判断该邻域点是否已经回到边界起始点
if (currentP.x==startP.x&¤tP.y==startP.y)
{
findStartPointAgain = true;
} if (startDirection % 2 == 0)
{
startDirection++;
}
else
{
startDirection = (startDirection + 2) % 8;
}
}
else
{
startDirection--;
if (startDirection < 0)
{
startDirection += 8;
}
}
}
} //为链码表的第三个单元插入一个元素以记录边界的总点数
vecCode->insert(vecCode->begin()+2,vecCode->size()-2);
return true;
}
//==================================================================================
//功能说明:链码表(4连通或者8连通)转换成线段表
//入 口: vecCode 对象边界点链码表
//
//出 口: vecLineTable 对象的线段表
//==================================================================================
bool CSegmentDataSet::CodeToLineTable(vector<POINT>* vecLineTable, vector<int>& vecCode)
{
if (vecCode.empty())
{
return false;
} // 清空线段表
vecLineTable->clear(); // 在已经存储好的链码表末尾再插入一个点,用来存储第一个边界点的链码值,构成循环,作为离开第一个边界点的的链码
vecCode.push_back(-1000); // 逆时针定义中心像素点的8邻域坐标,注意:图像原点位于左上角
// 第一列为x方向的偏移,第二列为y方向的偏移
int direction[8][2] = { {1, 0},{1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1},{1, 1}}; // 链码表转换线段表的参数表
int tab[8][8] = {
//0 1 2 3 4 5 6 7
{0,0,0,0,2,2,2,2}, // 0 // 0为中间点,1为左端点
{1,1,1,1,0,3,3,3}, // 1 // 2为右端点,3为奇异点
{1,1,1,1,0,0,3,3}, // 2 // 行是进入的链码
{1,1,1,1,0,0,0,3}, // 3 // 列是离开时的链码
{1,1,1,1,0,0,0,0}, // 4
{0,3,3,3,2,2,2,2}, // 5
{0,0,3,3,2,2,2,2}, // 6
{0,0,0,3,2,2,2,2}, // 7
}; int codeIn, codeOut;
int pointType;
int endPointCount = 0; //线段表端点数初始化
int codeCount = vecCode[2];
vecCode[codeCount+3] = vecCode[3]; //取得边界起始点坐标
int startX = vecCode[0];
int startY = vecCode[1]; //先把边界起始点当作当前点
int currentX = startX;
int currentY = startY; for (int i = 3; i < codeCount + 3;i++) //沿整个链码序列处理一次
{
codeIn = vecCode[i]; //取进入当前点的链码,第一次循环时当前点为链码表中的第二个边界点
codeOut = vecCode[i + 1]; //取离开当前点的链码
pointType = tab[codeIn][codeOut]; //计算得到当前点坐标
currentX += direction[codeIn][0];
currentY += direction[codeIn][1]; POINT pt;
pt.x = currentX;
pt.y = currentY; if (pointType!=0) //中间点不填入临时表 不管是左端点还是右端点填入一次
{
vecLineTable->push_back(pt);
}
if (pointType==3) //奇异点再添一次,保证总端点数为偶数
{
vecLineTable->push_back(pt);
}
} // 对初始线段表按照y从小到大排序,y值相等的,按照x值从小到大排序
sort(vecLineTable->begin(),vecLineTable->end(),SetSortRule); vecCode.erase(vecCode.end()-1); //从链码表中删除开始添加的点 return true;
}注意:上面的代码只能完成单连通区域边界点到区域点的转换,边界点也包含在最终得出的区域点中。