• Two pass 标识连通区域


    /* 对于m列n行的图像,我们从左向右,从上向下遍历每一个像素
    *  ①标签序号label从-1开始。
    *  ②如果当前像素为1
    *    i)左边和上边像素均为0,则直接label加1,设置当前像素对应的label值为当前label值
    *    ii)左边或上边有一个像素为1时, 当前像素对应的label值设置为左边或上边对应的label值
    *    iii)左边和上边都为有效像素时,取二者对应的label值中较小的那个值赋值给当前像素对应的label值
            同时合并label较大的值。
    *  ③最后再从左到右,从上到下,找到当前像素对应label的根节点
    */
    
    ushort get_root(ushort index, const ushort* map)
    {
        ushort i = index;
        while (map[i] != (ushort)(-1))
        {
            i = map[i];
        }
    
        return i;
    }
    
    void union_region(ushort min, ushort max, ushort* map)
    {
        if (min == max)
            return;
        ushort root1 = get_root(min, map);
        ushort root2 = get_root(max, map);
    
        if (root1 < root2)
        {
            map[max] = root1;
        }
        else
        {
            map[max] = root2;
        }
    }
    
    //只支持二值图,即src为二值图像
    cv::Mat MarkConnDomainWithTwoPass(cv::Mat& src)
    {
        int type = src.type();
        CV_Assert(src.type() == CV_8UC1);
        
        //每个像素存储对应的label索引,索引从0开始,-1表示没有对应的label
        cv::Mat dst = cv::Mat::zeros(src.rows, src.cols, CV_16UC1);
        //存储父子label的关系,
        //子label存储的值对应父label的index,根label值为0
        ushort *pMap = new ushort[src.rows * src.cols];
        memset(pMap, -1, src.rows * src.cols * sizeof(ushort));
    
        int label = -1;
    
        //第一遍标记
        for (int y = 0; y < src.rows; y++)
        {
            uchar* pCurCol = src.ptr<uchar>(y);
            uchar* pPreCol = nullptr;
            ushort * pCurDest = dst.ptr<ushort>(y);
            ushort * pPreDest = nullptr;
            if (y > 0)
            {
                pPreDest = dst.ptr<ushort>(y - 1);
                pPreCol = src.ptr<uchar>(y - 1);
            }
    
            for (int x = 0; x < src.cols; x++)
            {
                if (pCurCol[x] != 0)
                {
                    if (x == 0)
                    {
                        if (pPreCol == nullptr || pPreCol[x] == 0)
                        {
                            label++;
                            pCurDest[x] = label;
                        }
                        else
                        {
                            pCurDest[x] = pPreDest[x];
                        }
                    }
                    else if (y == 0)
                    {
                        if (x == 0 || pCurCol[x - 1] == 0)
                        {
                            label++;
                            pCurDest[x] = label;
                        }
                        else
                        {
                            pCurDest[x] = pCurDest[x - 1];
                        }
                    }
                    else if (pCurCol[x - 1] == 0 && pPreCol[x] == 0)
                    {
                        label++;
                        pCurDest[x] = label;
                    }
                    else if (pCurCol[x - 1] == 0 && pPreCol[x] == 1)
                    {
                        pCurDest[x] = pPreDest[x];
                    }
                    else if (pCurCol[x - 1] == 1 && pPreCol[x] == 0)
                    {
                        pCurDest[x] = pCurDest[x - 1];
                    }
                    else if (pCurCol[x - 1] == 1 && pPreCol[x] == 1)
                    {
                        if (pCurDest[x - 1] < pPreDest[x])
                        {
                            pCurDest[x] = pCurDest[x - 1];
                            union_region(pCurDest[x], pPreDest[x], pMap);
                        }
                        else
                        {
                            pCurDest[x] = pPreDest[x];
                            union_region(pCurDest[x], pCurDest[x - 1], pMap);
                        }
                    }
                }
                else
                {
                    pCurDest[x] = -1;
                }
            }
        }
        
        //合并相连接的区域
        for (int y = 0; y < dst.rows; y++)
        {
            ushort *pdst = dst.ptr<ushort>(y);
            for (int x = 0; x < dst.cols; x++)
            {
                if (pdst[x] != -1)
                {
                    pdst[x] = get_root(pdst[x], pMap);
                }
            }
        }
        
        if (pMap != nullptr)
        {
            delete[] pMap;
            pMap = nullptr;
        }
        
        return dst;
    }
    
    void test_MarkConnDomainWithTwoPass()
    {
        cv::Mat mat = cv::imread("E:\VisualWorkPlace\00OpenCVworkplace\images\testUse.bmp", cv::IMREAD_GRAYSCALE);
        
        //二值化处理
        cv::Mat halfVal(mat.rows, mat.cols, mat.type());
        for (int i = 0; i < mat.rows; i++)
        {
            uchar * p = halfVal.ptr<uchar>(i);
            uchar * pp = mat.ptr<uchar>(i);
            for (int j = 0; j < mat.cols; j++)
            {
                if (pp[j] == 0)
                {
                    p[j] = 1;
                }
                else
                {
                    p[j] = 0;
                }
            }
        }
        
        //two pass 标记连通区域
        cv::Mat dst = MarkConnDomainWithTwoPass(halfVal);
    
        std::ofstream ofout("test.log");
        ofout << dst;
        cv::waitKey(0);
    }
  • 相关阅读:
    浅谈MVP与ModelViewViewModel(MVVM)设计模式
    策略模式
    C#验证码
    如何招到烂程序员
    承载和使用WCF服务
    .NET Remoting 使用总结
    基于.Net Remoting的应用程序
    HTML5 是什么?
    关于HTTP及XMLHTTP状态代码一览
    Remoting多个信道(Chennel)的注册问题
  • 原文地址:https://www.cnblogs.com/merlinzjl/p/14389907.html
Copyright © 2020-2023  润新知