两阶连通域标记算法实现--C++

两阶连通域标记算法实现–C++

算法思路来源: https://www.jianshu.com/p/6aee287228e8
github源码: https://github.com/qcdq1314/c-/tree/master/bwlabel

一些废话

连通域标记算法目前较快的应该是跑长码算法,但是本人还没有仔细看这部分内容,先看了两阶段标记的这样一个算法,通俗易懂,实现简单,便花了一点时间用c++写出来了,效率方面虽然不及跑长码算法,但是我在我的16年的i7-7200u上跑2046x2048的图像,2233个目标花了145ms可以全部标记出来,速度还是比较可观的。
下面是源代码,直接编译就可以使用了,另外在github也可以直接下载源代码,在github上下载的朋友记得给个小星星哦!
后续我会看看跑长码算法并用c++实现。

头文件

#ifndef BWLABEL_H
#define BWLABEL_H

#include <vector>
#include <tuple>
#include <map>

namespace qc{

    #ifndef QC_TYPEDEF
    #define QC_TYPEDEF
    typedef unsigned char uchar;
    typedef unsigned short int ushort;
    typedef unsigned int uint;
    typedef unsigned long int ulint;
    typedef const char cchar;
    typedef const int cint;
    typedef const unsigned int cuint;
    typedef const float cfloat;
    typedef const double cdouble;
    enum qcStatus{
        QC_SUCCESS  = 0,
        QC_FAIL     = -1,
    };
    #endif

    /*****************************
     * class   : 两阶段连通域标记算法--qc
     * author  : qc
     * date    : 2019.10.19
     * email   : qcdq1314@aliyun.com
     * remarks : 边界宽度1像素不做处理
    ******************************/
    class FindContours_TwoPass final
    {
    public:
        // area, max_x, max_y, min_x, min_y, x, y, w, h
        /************************
         * function : 目标类型说明
         * 0        : 面积
         * 1        : x的最大值
         * 2        : y的最大值
         * 3        : x的最小值
         * 4        : y的最小值
         * 5        : x的平均值
         * 6        : y的平均值
         * 7        : 宽度
         * 8        : 高度
        ************************/
        using blob = std::tuple<uint, uint, uint, uint, uint, uint, uint, uint, uint>;

        FindContours_TwoPass() = default;

        /************************
         * function : 析构函数
         * explain  : 自动调用release函数
        ************************/
        ~FindContours_TwoPass();

        /************************
         * function   : 初始化内存
         * rows       : 行
         * cols       : 列
         * return     : 0成功/-1失败
        ************************/
        int init(const uint &rows, const uint &cols);

        /************************
         * function : 释放内存
         * return   : 0成功/-1失败
        ************************/
        void release();

        /************************
         * function : 两阶段连通域标记算法--qc
         * src      : 二值化数据
         * return   : 目标容器
        ************************/
        std::vector<blob> findContours(uchar *src);

    private:
        uint rows = 0, cols = 0, cutRows = 0, cutCols = 0;
        uint *dst = nullptr;
        std::map<uint, uint> belongMap; // 归属表, 大的标记值属于小的标记值
    };
}
#endif // BWLABEL_H

源文件

#include "../include/bwlabel.h"
#include <memory.h>

qc::FindContours_TwoPass::~FindContours_TwoPass()
{
    release();
}

int qc::FindContours_TwoPass::init(const uint &rows, const uint &cols)
{
    this->rows = rows;
    this->cols = cols;
    cutRows = rows - 1; // 边界处不计算,提高效率
    cutCols = cols - 1; // 边界处不计算,提高效率
    dst = new (std::nothrow) uint[rows * cols];
    if(!dst)
        return QC_FAIL;

    return QC_SUCCESS;
}

void qc::FindContours_TwoPass::release()
{
    if(dst){
        delete [] dst;
        dst = nullptr;
    }
}

std::vector<qc::FindContours_TwoPass::blob> qc::FindContours_TwoPass::findContours(uchar *src)
{
    memset(dst, 0, static_cast<size_t>(rows * cols) * sizeof(uint));
    belongMap.clear();

    uint index = 0, index_wait_add = 0;
    uint cur = 0, top = 0, left = 0;
    uint run = 0;
    std::vector<blob> blobs;

    for(uint i = 1; i < cutRows; ++i){
        for(uint j = 1; j < cutCols; ++j){
            // 中间行
            cur = i * cols + j;
            top = (i - 1) * cols + j;
            left = i * cols + j - 1;
            if(src[cur]){
                // 当前位置有值
                if(!src[left] && !src[top]){
                    // 左侧和上方没有值,标记此处,标记+1
                    dst[cur] = ++run;
                    blobs.push_back(std::make_tuple(1, j, i, j, i, 0, 0, 0, 0));
                    continue;
                }else if(src[left] && src[top]){
                    // 左侧和上方都有值,标记此处,标记为二者中的最小值, 且相邻的大值归属为小值, 相等则无归属关系
                    if(dst[left] > dst[top]){
                        dst[cur] = dst[top];
                        belongMap[dst[left]] = dst[top];
                    }else if(dst[left] < dst[top]){
                        dst[cur] = dst[left];
                        belongMap[dst[top]] = dst[left];
                    }else{
                        dst[cur] = dst[left];
                    }
                }else{
                    // 左侧和上方只有一处有值, 标记此处
                    dst[cur] = dst[top] | dst[left];
                }

                index = dst[cur] - 1;
                ++std::get<0>(blobs[index]);           // area
                if(!src[i * cols + j + 1]){
                    // 如果右侧没有值,则判断最大x值
                    if(std::get<1>(blobs[index]) < j)
                        std::get<1>(blobs[index]) = j; // max_x
                }
                if(!src[(i + 1) * cols + j]){
                    // 如果下方没有值,则判断最大y值
                    if(std::get<2>(blobs[index]) < i)
                        std::get<2>(blobs[index]) = i; // max_y
                }
                if(!src[left]){
                    // 如果左侧没有值,则判断最小x值
                    if(std::get<3>(blobs[index]) > j)
                        std::get<3>(blobs[index]) = j; // min_x
                }
                if(!src[top]){
                    // 如果上方没有值,则判断最小y值
                    if(std::get<4>(blobs[index]) > i)
                        std::get<4>(blobs[index]) = i; // min_y
                }
            }
        }
    }

    if(run == 0)
        return blobs;

    // 反向遍历归属表
    for(auto it = belongMap.rbegin(); it != belongMap.rend(); ++it){
        index = it->first - 1;
        index_wait_add = it->second - 1;
        std::get<0>(blobs[index_wait_add]) += std::get<0>(blobs[index]);    // area
        if(std::get<1>(blobs[index_wait_add]) < std::get<1>(blobs[index]))
            std::get<1>(blobs[index_wait_add]) = std::get<1>(blobs[index]); // max_x
        if(std::get<2>(blobs[index_wait_add]) < std::get<2>(blobs[index]))
            std::get<2>(blobs[index_wait_add]) = std::get<2>(blobs[index]); // max_y
        if(std::get<3>(blobs[index_wait_add]) > std::get<3>(blobs[index]))
            std::get<3>(blobs[index_wait_add]) = std::get<3>(blobs[index]); // min_x
        if(std::get<4>(blobs[index_wait_add]) > std::get<4>(blobs[index]))
            std::get<4>(blobs[index_wait_add]) = std::get<4>(blobs[index]); // min_y
        std::get<0>(blobs[index]) = 0; // in order to delete it.
    }

    std::vector<blob> blobs_new;
    for(auto it = blobs.begin(); it != blobs.end(); ++it){
        if(std::get<0>(*it) > 1){
            std::get<5>(*it) = (std::get<1>(*it) + std::get<3>(*it)) / 2; // x = (max_x + min_x) / 2
            std::get<6>(*it) = (std::get<2>(*it) + std::get<4>(*it)) / 2; // y = (max_y + min_y) / 2
            std::get<7>(*it) = std::get<1>(*it) - std::get<3>(*it) + 1;   // w = max_x - min_x + 1
            std::get<8>(*it) = std::get<2>(*it) - std::get<4>(*it) + 1 ;  // h = max_y - min_y + 1
            blobs_new.push_back(*it);
        }
    }

    return blobs_new;
}

测试结果

输入图像标记后的图像

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值