merlinzjl

导航

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);
}

 

posted on 2021-02-08 18:10  merlinzjl  阅读(157)  评论(0编辑  收藏  举报