请教一个图像处理的算法:边界点到区域点的转换

cau228charm 2009-04-05 04:09:00
一个图像的区域可以有两种表示方法,一种是边界点表示,另一种是区域点表示;现在我已知了一个区域的边界点坐标点vector<POINT>vecBorder,现在我要将这个边界点集合转换为区域点,代码怎么写呢?想了很久还是没有想出来,只好求助大家了。谢谢!
函数:
CovBorderToObj(vector<POINT>* vecObjInner, vector<POINT>& vecObjBorder)
{
}

...全文
304 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
cau228charm 2009-06-01
  • 打赏
  • 举报
回复

//==================================================================================
//功能说明:设置两个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;
}

注意:上面的代码只能完成单连通区域边界点到区域点的转换,边界点也包含在最终得出的区域点中。
shayla 2009-04-09
  • 打赏
  • 举报
回复
种子填充算法和扫描线填充算法可以, 楼主搜一下
jeff994 2009-04-09
  • 打赏
  • 举报
回复
我说个办法,看你有没有办法实现。图像处理的几步就好了。
先呢, 把图片扩大化 (dilation). 用一个盘子做基本结构(structure elements). - 目的是把边界闭合。
然后呢,填充洞,就把整个图片填满。(fill - hole);
再然后,再缩小下。 用同样的结构(erode)。主要是为了把外面扩大的部分除掉。
ndy_w 2009-04-06
  • 打赏
  • 举报
回复
大概这样:每条扫描线经过一次边界,表示进入或离开区域,黑变白或白变黑。但扫描线经过在边界局部上沿和下沿需要处理一下。
1检查边界点是否封闭,交叉等。如果不封闭的增加边界点连起来。边界点排好队。
2边界点,如果左侧第一个非0的dy与右侧第一个非0的dy同号,表示是边界局部上沿或下沿。记0。否则记1。连续的1只记一个。
3从上向下扫描,每行从左到右,每经过一个记1的边界点,表示经过边界,结果值反向。
cau228charm 2009-04-06
  • 打赏
  • 举报
回复
2边界点,如果左侧第一个非0的dy与右侧第一个非0的dy同号,表示是边界局部上沿或下沿。记0。否则记1。连续的1只记一个。
上面这句话是什么意思啊?非0和dy分别表示什么呢? 谢谢
cau228charm 2009-04-06
  • 打赏
  • 举报
回复
谢谢楼上啊 我先研究下看看 期待其他详细一点的解决方法
cau228charm 2009-04-05
  • 打赏
  • 举报
回复
没有人解决啊

19,469

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧