原创 二值图像轮廓跟踪

2009-2-23 22:28 5260 7 8 分类: 软件与OS

    二值图像轮廓跟踪用于提取二值图像中的目标区域,以便对目标区域做进一步处理,如:区域填充,计算轮廓长度、面积、重心,特征提取和图像识别等。我们可以采用链码和线段表两种不同的方法来描述轮廓。链码方式描述轮廓需要记录轮廓的起点和轮廓上每一点相对于前一点的链码值列表。以8-领域链码为例,首先按照从左至右,从上至下的顺序查找轮廓起点;再按照右、右下、下、左下的顺序查找第二个轮廓点;然后按照右、右下、下、左下、左、左上、上、上右的顺序查找其它轮廓点,直到找到轮廓起点为止。
    由于事先无法确定轮廓点数量,无法事先分配合适的内存空间,因此可以使用vector来动态管理内存,从而简化程序设计。


#include <vector>
using namespace std;
typedef std::vector<int> ivecChainCode;
// 轮廓跟踪
// 1. pImageData   图像数据
// 2. nWidth       图像宽度
// 3. nHeight      图像高度
// 4. nWidthStep   图像行大小
// 5. pStart       起始点
// 6. pChainCode   链码表
BOOL TracingContour(unsigned char *pImageData, int nWidth, int nHeight, int nWidthStep,
    POINT *pStart, ivecChainCode *pChainCode)
{
    int            i           = 0;
    int            j           = 0;
    int            k           = 0;
    int            x           = 0;
    int            y           = 0;
    unsigned char *pLine       = NULL;
    BOOL           bTracing    = FALSE;
    POINT          ptCurrent   = { 0, 0 };
    POINT          ptTemp      = { 0, 0 };
    const POINT    ptOffset[8] =
    {
        {  1,  0 }, {  1,  1 }, { 0,  1 }, { -1,  1 },
        { -1,  0 }, { -1, -1 }, { 0, -1 }, {  1, -1 }
    };
    // 清空起始点与链码表
    pStart->x = 0;
    pStart->y = 0;
    pChainCode->clear();
    // 轮廓起点
    for (y = 0; y < nHeight; y++)
    {
        pLine = pImageData + nWidthStep * y;
        for (x = 0; x < nWidth; x++)
        {
            if (pLine[x] == 0xFF)
            {
                bTracing = TRUE;
                pStart->x = x;
                pStart->y = y;
                ptCurrent.x = x;
                ptCurrent.y = y;
            }
        }
    }
    // 轮廓跟踪
    while (bTracing)
    {
        bTracing = FALSE;
        for (i = 0; i < 8; i++, k++)
        {
            k &= 0x07;
            x = ptCurrent.x + ptOffset[k].x;
            y = ptCurrent.y + ptOffset[k].y;
            if (x >= 0 && x < nWidth && y >= 0 && y < nHeight)
            {
                // 判断是否为轮廓点
                if (pImageData[nWidthStep * y + x] == 0xFF)
                {
                    for (j = 0; j < 8; j += 2)
                    {
                        ptTemp.x = x + ptOffset[j].x;
                        ptTemp.y = y + ptOffset[j].y;
                        if (ptTemp.x >= 0 && ptTemp.x < nWidth &&
                            ptTemp.y >= 0 && ptTemp.y < nHeight)
                        {
                            if (pImageData[nWidthStep * ptTemp.y + ptTemp.x] == 0)
                            {
                                bTracing = TRUE;
                                ptCurrent.x = x;
                                ptCurrent.y = y;
                                pChainCode->push_back(k);
                                break;
                            }
                        }
                    }
                }
            }
            if (bTracing)
            {
                // 如果当前点为轮廓起点
                if (pStart->x == ptCurrent.x && pStart->y == ptCurrent.y)
                {
                    // 则跟踪完毕
                    bTracing = FALSE;
                }
                break;
            }
        }
        k += 0x06;
    }
    return TRUE;
}


// 轮廓绘制
// 1. pImageData   图像数据
// 2. nWidth       图像宽度
// 3. nHeight      图像高度
// 4. nWidthStep   图像行大小
// 5. ptStart      起始点
// 6. ChainCode    链码表
BOOL DrawContour(unsigned char *pImageData, int nWidth, int nHeight, int nWidthStep,
    POINT ptStart, ivecChainCode ChainCode)
{
    POINT          ptCurrent   = { 0, 0 };
    const POINT    ptOffset[8] =
    {
        {  1,  0 }, {  1,  1 }, { 0,  1 }, { -1,  1 },
        { -1,  0 }, { -1, -1 }, { 0, -1 }, {  1, -1 }
    };
    // 清空图像
    memset(pImageData, 0, nWidthStep * nHeight);
    // 轮廓绘制
    ptCurrent.x = ptStart.x;
    ptCurrent.y = ptStart.y;
    pImageData[nWidthStep * ptCurrent.y + ptCurrent.x] = 0xFF;
    for (ivecChainCode::iterator I = ChainCode.begin(); I != ChainCode.end(); i++)
    {
        ptCurrent.x += ptOffset[*i].x;
        ptCurrent.y += ptOffset[*i].y;
        pImageData[nWidthStep * ptCurrent.y + ptCurrent.x] = 0xFF;
    }
    return TRUE;
}


轮廓跟踪效果:



0be9dc0f-15a5-49ad-9b55-70774432ee9c.gif71c8cba6-f10e-467a-b05f-58054342c996.gif

文章评论1条评论)

登录后参与讨论

用户377235 2012-12-4 21:13

轮廓跟踪的很巧妙,不会因为边缘的一些特别情况而提前终止。想问一下博主,while循环最后的那个 k += 0x06; 是什么道理啊?还望指教,多谢!

相关推荐阅读
用户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 下一条