原创 链码表转换为线段表

2009-2-23 22:29 3411 7 8 分类: 软件与OS

    链码表和线段表都可以描述轮廓,通过轮廓跟踪得到链码表,链码表可以很容易计算出轮廓长度,但轮廓填充,计算面积、重心等使用线段表会更加容易。为了后续处理的方便,往往需要将链码表转换为线段表。线段表只需要记录每条线段的两个端点坐标,中间点全部舍弃。链码表上的点如果位于线段的中间则舍弃,如果位于线段的某一个端点则保留,如果位于线段的两个端点(即线段长度为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;
}


链码表转换为线段表并填充的效果:

bf6d328c-d066-47a9-94f8-59eb2152dd24.gif4a00fbb0-515e-44c3-9f11-60c1096b1270.gif

文章评论1条评论)

登录后参与讨论

zhangshaobing517_935512703 2009-2-21 16:53

请问这些文章都是你自己做过的,然后写的?那你绝对是高手/
相关推荐阅读
用户51033 2013-12-17 22:53
使用宏命令FormatDataLibsvm.xls将数据转成LIBSVM格式
http://blog.sina.com.cn/s/blog_5980835e0100dioo.html 毕业设计做的是svm,虽然不是很新的东西,但是能把它弄懂也是好的。要加一点技术含量无非...
用户51033 2013-12-17 22:50
LIBSVM做回归预测--终于弄通
http://blog.sina.com.cn/s/blog_5980835e0100dp52.html 看了网上很多帖子和博客,自己琢磨了很久到现在才弄明白怎么用libsvm来做预测。因为网上的帖子...
用户51033 2013-12-17 22:43
LIBSVM回归详细操作步骤
原文地址:LIBSVM回归详细操作步骤(附图)--更新至20090806作者:梦在继续 P.S. 多谢“三月未央”网友的提醒,本文中的一些错误得到改正,原先的第五幅图中路径有错(估计那晚太困了,稀里糊...
用户51033 2012-03-10 21:06
关于RS232/RS422/RS485总线的抗干扰的措施
2011-07-27 21:48 原文:http://hi.baidu.com/%B5%E7%D7%D3%C2%F8%CD%B7/blog/item/2b9d2c8e51bfd72466096e4...
用户51033 2012-02-18 22:06
高边和低边电流检测技术分析
当代电子系统中的电源管理可以通过高效的电源分配优化系统效率。电流检测是电源管理的关键技术之一,它不仅有助于保持理想的电压等级,而且能通过提供伺服调整保持电子系统处于正常状态,同时还能防止发生...
用户51033 2012-02-18 20:20
TVS二极管的选型和应用测试计算实例
    很多工程师在电路设计时都会考虑到EMC,但是在ESD方面却是很少考虑或甚至不考虑。个人认为有些是产品特性或是成本原因不考虑防雷防静电,但据了解, 相当多的工程师特别是比较年轻的工程师...
我要评论
1
7
关闭 站长推荐上一条 /2 下一条