实验中需要用到区域联通的算法,就是类似于matlab中bwlabel的函数。网上找了找c++源码未果,bwlabel-python版用python描述了matlab中的实现方法,但是最后对标签的处理部分并未看明白,故自己用c++实现了一个。先直接看bwlabel函数代码:

cv::Mat bwlabel(const cv::Mat in, int * num, const intmode)

{const int num_runs = number_of_runs(in);int * sc = new int[num_runs];int * ec = new int[num_runs];int * r = new int[num_runs];int * labels = new int[num_runs];

memset(labels,0, sizeof(int)*num_runs);

fill_run_vectors(in, sc, ec, r);

first_pass(sc, ec, r, labels, num_runs, mode);

cv::Mat result= cv::Mat::zeros(in.size(), CV_8UC1);int number = 0;for(int i = 0; i < num_runs; i++)

{

uchar* p_row = result.ptr(r[i]);for(int j = sc[i]; j <= ec[i]; j++)

p_row[j]=labels[i];if(number

number=labels[i];

}if(num !=NULL)*num =number;

delete [] sc;

delete [] ec;

delete [] r;

delete [] labels;returnresult;

}

bwlabel中要用到三个辅助函数:number_of_runs,fill_run_vectors,first_pass。函数number_of_runs计算每一行中非零像素团的个数并累加起来。

1 1 0 0 0 1 1 1 0 0

比如,上面这一行就有2个非零像素团,我们称这样的像素团为Run,函数number_of_runs实现如下:

int number_of_runs(const cv::Mat in)

{const int rows = in.rows;const int cols = in.cols;int result = 0;for(int row = 0; row < rows; row++)

{const uchar * p_row = in.ptr(row);if(p_row[0] != 0)

result++;for(int col = 1; col < cols; col++)

{if(p_row[col] != 0 && p_row[col-1] == 0)

result++;

}

}returnresult;

}

这个函数算法思想是,扫描每一行,对每一行,如果当前元素非零并且前一元素为零则Run的个数加一。

函数fill_run_vectors的作用是填充三个数据结构:sc[],ec[],r[],它们分别表示开始列标、结束列标和行标,数组长度为由number_of_runs函数得到的Run的个数。函数fill_run_vectors实现如下:

void fill_run_vectors(const cv::Mat in, int sc[], int ec[], intr[])

{const int rows = in.rows;const int cols = in.cols;int idx = 0;for(int row = 0; row < rows; row++)

{const uchar * p_row = in.ptr(row);int prev = 0;for(int col = 0; col < cols; col++)

{if(p_row[col] !=prev)

{if(prev == 0)

{

sc[idx]=col;

r[idx]=row;

prev= 1;

}else{

ec[idx++] = col - 1;

prev= 0;

}

}if(col == cols-1 && prev == 1)

{

ec[idx++] =col;

}

}

}

}

算法思想还是遍历每一行,用变量prev保存一行中上一个团是0还是1,如果出现01跳变那么就要记录下新的Run的开始列标和行标,如果出现10跳变(或者这行结束并且prev=1)那么就记录下这个Run的结束列标。

函数first_pass顾名思义,字面上说第一次扫描。因为函数扫描每一个Run块,给它打标签。当出现如下情况时:

1 1 0 0 1 1 1 0

0 1 1 1 1 0 0 0

函数给第一行第一个Run打上标签1,第二个Run打上标签2,当遍历到第二行时,发现这一行的一个Run与第一行第一个Run相邻,故打上标签1,但当继续遍历时发现这个Run也与第一行第二个Run相邻,但函数并没有改变第一行第二个Run的标签,而是记录下这两个标签其实该一样。遍历完第二行结果为:

1 1 0 0 2 2 2 0

0 1 1 1 1 0 0 0

遍历完每一个Run过后就是处理刚才未处理的标签了。函数first_pass实现如下:

void first_pass(const int sc[], const int ec[], const int r[],int labels[], const int num_runs, const intmode)

{int cur_row = 0;int next_label = 1;int first_run_on_prev_row = -1;int last_run_on_prev_row = -1;int first_run_on_this_row = 0;int offset = 0;int * equal_i = new int[num_runs];int * equal_j = new int[num_runs];int equal_idx = 0;if(mode == 8)

offset= 1;for(int k = 0; k < num_runs; k++)

{if(r[k] == cur_row + 1)

{

cur_row+= 1;

first_run_on_prev_row=first_run_on_this_row;

first_run_on_this_row=k;

last_run_on_prev_row= k - 1;

}else if(r[k] > cur_row + 1)

{

first_run_on_prev_row= -1;

last_run_on_prev_row= -1;

first_run_on_this_row=k;

cur_row=r[k];

}if(first_run_on_prev_row >= 0)

{int p =first_run_on_prev_row;while(p <= last_run_on_prev_row && sc[p] <= (ec[k] +offset))

{if(sc[k] <= ec[p] +offset)

{if(labels[k] == 0)

labels[k]=labels[p];else if(labels[k] !=labels[p])

{//labels[p] = labels[k];

equal_i[equal_idx] =labels[k];

equal_j[equal_idx]=labels[p];

equal_idx+= 1;

}

}

p+= 1;

}

}if(labels[k] == 0)

{

labels[k]= next_label++;

}

}/// process labels

for(int i = 0; i < equal_idx; i++)

{int max_label = equal_i[i] > equal_j[i] ?equal_i[i] : equal_j[i];int min_label = equal_i[i] < equal_j[i] ?equal_i[i] : equal_j[i];for(int j = 0; j < num_runs; j++)

{if(labels[j] ==max_label)

labels[j]=min_label;

}

}

delete [] equal_i;

delete [] equal_j;/process ignore labels

int * hist = new int[next_label];int * non_labels = new int[next_label];

memset(hist,0, sizeof(int)*next_label);int non_num = 0;for(int i = 0; i < num_runs; i++)

{

hist[labels[i]]++;

}for(int i = 1; i < next_label; i++)

{if(hist[i] == 0)

non_labels[non_num++] =i;

}for(int j = 0; j < num_runs; j++)

{int k =labels[j];for(int i = non_num-1; i >= 0; i--)

{if(k >non_labels[i])

{

labels[j]-= (i+1);break;

}

}

}

delete [] hist;

delete [] non_labels;

}

前面遍历每一个Run分两种情况,上一行有Run和上一行无Run:当上一行无Run时就分配一个新的标签,当上一行有Run时还要考虑是否与上一行Run相邻,若相邻则打上上一行的标签,当出现上面讲到的情况时就保存这两个标签到数组equal_i,equal_j中。

接下来就是处理equal_i和equal_j这两个数组了,要将它们当中相同族的不同标签合并到一起(注释process labels下面代码)。

这样过后还不能完事,有可能出现标签间断的现象(如1,2,4,6),就是还必须把标签(如1,2,4,6)映射到一个连续的空间(1,2,3,4)。参见注释process ignore labels以下代码。

这样过后就差不多了,最后一步是在bwlabel中给返回的Mat中元素打上对应的标签。

bwlabel算法_bwlabel函数的c++实现相关推荐

  1. 黄金分割算法求函数的极值C++实现

    黄金分割算法求极值迭代的最终判断是两次函数值的结果差是否很小,如果小,则说明该处函数的斜率很小,就是一个极值点. 过程如下: 以下是代码: //黄金分割法! /* ================== ...

  2. MATLAB算法(函数)编译为C++动态库遇到的问题

    MATLAB算法(函数)编译为C++动态库遇到的问题 今天在编译MATLAB函数为C++的动态链接库时遇到了几个问题,在网上搜索了很多资料都没有解决我遇到的问题,特此分享出来供大家参考. 环境: Wi ...

  3. STL算法中函数对象和谓词

    算法中函数对象和谓词 函数对象和谓词定义 函数对象 谓词 一元函数对象案例 一元谓词案例 二元函数对象案例 二元谓词案例 预定义函数对象和函数适配器 使用预定义函数对象 算术函数对象 关系函数对象 逻 ...

  4. 基于群智能算法的函数最值优化问题

    基于群智能算法的函数最值优化问题 摘要 针对求解函数的极值问题的群智能算法,大部分函数在定义域内都可以通过微分的方式求得极值点和找到最值.但是存在一些测试函数,他们的最值用求解的微分的方式只能使得计算 ...

  5. 智能计算作业——差分进化算法求解函数最值

    下面是智能计算作业,哎呀,你们快来抄我作业呀╭(╯^╰)╮ 问题描述: 算法理论我省略了,你们自己去百科上抄吧╭(╯^╰)╮ 求解步骤: (这个好像也是百科上的╭(╯^╰)╮我根据自己的需要改了一下) ...

  6. 粒子群优化算法的寻优算法——非线性函数极值寻优Matlab

    粒子群算法的起源 1995年,受到鸟群觅食行为的规律性启发,James Kennedy和Russell Eberhart建立了一个简化算法模型,经过多年改进最终形成了粒子群优化算法(Particle ...

  7. C语言实现一些算法或者函数以及一些经典问题的分析

    C语言实现一些算法或者函数 1.递归二分搜索 2.递归归并排序 3.Ackerman函数 4.Fibonacci数列 5.递归求排列 6.求最大公约数 7.偶位数的大整数乘法 8.快速排序 9.矩阵连 ...

  8. 粒子群算法的寻优算法-非线性函数极值寻优

    粒子群算法的寻优算法-非线性函数极值寻优 1.算法概述 粒子群优化算法(Particle Swarm optimization,PSO)又翻译为粒子群算法.微粒群算法.或微粒群优化算法.是通过模拟鸟群 ...

  9. matlab基于人工蜂群算法的函数优化分析【matlab优化算法十一】

    基于人工蜂群算法的函数优化分析 自然界中的群居昆虫,它们虽然个体结构简单,但是通过个体间的合作却能够表现出极其复杂的行为能力.受这些社会性昆虫群体行为的启发,研宄者通过模拟这些群体的行为提出了群集智能 ...

最新文章

  1. travis-ci如何配置android
  2. php 进程 线程,php进程还是线程
  3. 为什么CSS使用假元素?
  4. win10右键闪退到桌面_【雷粉百科】windows10鼠标点击右键出现卡顿或者转圈
  5. JAVA如何判断两个字符串是否相等(亲测第二种方式)
  6. linux 查看目录下文件个数
  7. JAMstack简介:现代Web的体系结构
  8. linux 设置更新源为cd,技术|如何修复 apt-get update 无法添加新的 CD-ROM 的错误
  9. web gooflow流程图实现带公式的流程配置
  10. linux tar压缩文件命令,tar打包压缩文件命令
  11. 经典详细的Struts2教程(附案例源码)
  12. 计算机名和ip不匹配,错误:主机名/ IP不匹配证书的altnames Node.js的
  13. fgetc getc函数
  14. android 水波动画效果,Android 实现水波纹动效
  15. SptingBoot+RabbitMQ整合出现的异常【Connection reset和Channel shutdown: connection error和ForgivingExceptionHa】
  16. php语言的应用领域,探讨主要的PHP应用领域
  17. JDBC与JAVA程序笔记
  18. 前端复习之Ajax,忘完了
  19. iOS开发底层之KVO探索下 -18
  20. 一家之言:de_nuke全面分析

热门文章

  1. Ruby. Vs . Python
  2. 静态路由与动态路由概念及实例
  3. 2018.12.30|区块链技术头条
  4. P1330 封锁阳光大学
  5. 抢占乡镇渠道 中国手机厂商比苹果有经验
  6. Kafka+Storm+HDFS整合实践
  7. 按单词逆序句子(含标点)
  8. 数据库 实体间的3种关系
  9. 《Inside XAML》翻译半成品
  10. flink的udtf中String[]转String数组