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

解决方案 »

  1.   

    大概这样:每条扫描线经过一次边界,表示进入或离开区域,黑变白或白变黑。但扫描线经过在边界局部上沿和下沿需要处理一下。
    1检查边界点是否封闭,交叉等。如果不封闭的增加边界点连起来。边界点排好队。
    2边界点,如果左侧第一个非0的dy与右侧第一个非0的dy同号,表示是边界局部上沿或下沿。记0。否则记1。连续的1只记一个。
    3从上向下扫描,每行从左到右,每经过一个记1的边界点,表示经过边界,结果值反向。
      

  2.   

    2边界点,如果左侧第一个非0的dy与右侧第一个非0的dy同号,表示是边界局部上沿或下沿。记0。否则记1。连续的1只记一个。 
    上面这句话是什么意思啊?非0和dy分别表示什么呢? 谢谢
      

  3.   

    我说个办法,看你有没有办法实现。图像处理的几步就好了。
    先呢, 把图片扩大化 (dilation). 用一个盘子做基本结构(structure elements). - 目的是把边界闭合。
    然后呢,填充洞,就把整个图片填满。(fill - hole);
    再然后,再缩小下。 用同样的结构(erode)。主要是为了把外面扩大的部分除掉。
      

  4.   


    //==================================================================================
    //功能说明:设置两个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&&currentP.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;
    }注意:上面的代码只能完成单连通区域边界点到区域点的转换,边界点也包含在最终得出的区域点中。