一、 One-Pass对应的标记算法(Label.h)
使用:
unsigned char label = (unsigned char )fspace_2d(imgMask2.row,imgMask2.col,sizeof(unsigned char));
std::vector shapecenterpoint;
int ll = Label::CutAndLable(pTemp,label,imgMask2.row,imgMask2.col,shapecenterpoint);

512X512图像,耗时60ms
256X256图像,耗时20ms。
代码分析:
1. 输入待标记图像bitmap,初始化一个与输入图像同样尺寸的标记矩阵labelmap,一个队列queue以及标记计数labelIndex;
2. 按从左至右、从上至下的顺序扫描bitmap,当扫描到一个未被标记的前景像素p时,labelIndex加1,并在labelmap中标记p(相应点的值赋为labelIndex),同时,扫描p的八邻域点,若存在未被标记的前景像素,则在labelmap中进行标记,并放入queue中,作为区域生长的种子;
3. 当queue不为空时,从queue中取出一个生长种子点p1,扫描p1的八邻域点,若存在未被标记过的前景像素,则在labelmap中进行标记,并放入queue中;
4. 重复3直至queue为空,一个连通区标记完成;
5. 转到2,直至整幅图像被扫描完毕,得到标记矩阵labelmap和连通区的个数labelIndex。

// Label.h: interface for the Label class.
//
//////////////////////////////////////////////////////////////////////#if !defined(AFX_LABEL_H__BD38D587_97F3_4B51_9BB6_C0F044167FBA__INCLUDED_)
#define AFX_LABEL_H__BD38D587_97F3_4B51_9BB6_C0F044167FBA__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <vector>#ifndef _POINT4D_
#define _POINT4D_
struct Point4D
{int r;      //目标点区域的质心位置int c;double dr;  //目标点区域的质心位置的精确值double dc;int s;      //目标点区域的大小double   g; //目标点区域中最大灰度值Point4D& operator=(const Point4D &other){if (&other==this)return *this;   r = other.r;c = other.c;dr = other.dr;dc = other.dc;s = other.s;g = other.g;return *this;};
};
#endifclass Label
{
public:Label();virtual ~Label();  static int CutAndLable(unsigned char **inImg, unsigned char **outImg,int nRow, int nCol, std::vector<Point4D>& points);static void labelNeighborPoint(unsigned char **pImg,int nRow,int nCol,int i,int j,Point4D &pPStart);};#endif // !defined(AFX_LABEL_H__BD38D587_97F3_4B51_9BB6_C0F044167FBA__INCLUDED_)
// Label.cpp: implementation of the Label class.
//
//////////////////////////////////////////////////////////////////////#include "stdafx.h"
#include "Label.h"
#include "Comlib.h"
#include <vector>
using namespace std;Label::Label()
{}Label::~Label()
{}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
///////////////////////////////////////////////////////////////////////************************ label ************************************/
//种子填充标记
//
// 参数:
//1. inImg       - 输入图像
//2. outImg      - 输入图像
//2. nRow, nCol  - 图像高和宽
//3. points      - 标记点容器
//
// 返回值:      - 标记点的个数
/*********************************************************************/
int Label::CutAndLable(unsigned char **inImg, unsigned char **outImg,int nRow, int nCol, std::vector<Point4D>& points)
{for (int iii=0;iii<nRow;iii++)for (int jjj=0;jjj<nCol;jjj++){outImg[iii][jjj] = 0;}unsigned char **pImg = (unsigned char**)fspace_2d(nRow,nCol,sizeof(unsigned char));for (int ii=0;ii<nRow;ii++)for (int jj=0;jj<nRow;jj++){pImg[ii][jj] = inImg[ii][jj];}points.clear();Point4D temp;int nBlock =1;for (int i=0;i<nRow;i++)for (int j=0;j<nCol;j++){if (pImg[i][j] != 0){labelNeighborPoint(pImg,nRow,nCol,i,j,temp);points.push_back(temp);//outImg[temp.r][temp.c] = (unsigned char)temp.g;if (nBlock <=255){outImg[temp.r][temp.c] = nBlock;}elseoutImg[temp.r][temp.c] = 0;nBlock++;}}return points.size();
}void Label::labelNeighborPoint(unsigned char **pImg,int nRow,int nCol,int i,int j,Point4D &pPStart)
{   static int ttt = 1;int tMark = i * nCol +j;int endMark = nRow * nCol;int tTemp;int maxR=-1, maxC=-1;double sumR = 0, sumC = 0, sumG = 0, maxG = 0;int nCount = 0;int top=nRow,down=0,left=nCol,right=0;vector<int> pBuffD;pBuffD.push_back(tMark);while( pBuffD.size()!=0 ){tMark = pBuffD.back();pBuffD.pop_back();++nCount;sumR += tMark/nCol * pImg[tMark/nCol][tMark%nCol];//灰度加权sumC += tMark%nCol * pImg[tMark/nCol][tMark%nCol];sumG += pImg[tMark/nCol][tMark%nCol];if (maxG < pImg[tMark/nCol][tMark%nCol]){maxR = tMark/nCol;maxC = tMark%nCol;maxG = pImg[tMark/nCol][tMark%nCol];}pImg[tMark/nCol][tMark%nCol] = 0;if (tMark/nCol > down)  down = tMark/nCol;if (tMark%nCol > right) right = tMark%nCol;if (tMark/nCol < top)   top = tMark/nCol;if (tMark%nCol < left)  left = tMark%nCol;tTemp = tMark-nCol-1;       // (i-1,j-1)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0 )pBuffD.push_back(tTemp);tTemp = tMark-nCol  ;       // (i-1,j)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0  )pBuffD.push_back(tTemp);tTemp = tMark-nCol+1;       // (i-1,j+1)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0  )pBuffD.push_back(tTemp);tTemp = tMark       -1;     // (i,j-1)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0 )pBuffD.push_back(tTemp);tTemp = tMark       +1;     // (i,j+1)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0  )pBuffD.push_back(tTemp);tTemp = tMark+nCol-1;       // (i+1,j-1)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0  )pBuffD.push_back(tTemp);tTemp = tMark+nCol  ;       // (i+1,j)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0  )pBuffD.push_back(tTemp);tTemp = tMark+nCol+1;       // (i+1,j+1)if( tTemp>=0 && tTemp<endMark && pImg[tTemp/nCol][tTemp%nCol] != 0  )pBuffD.push_back(tTemp);}if( nCount!=0 ){int w = right - left;int h = down - top;pPStart.s = w>h ? w+1 : h+1;pPStart.c = sumC/sumG+0.5;pPStart.r = sumR/sumG+0.5;pPStart.dc = nCount;pPStart.dr = nCount;//      pPStart.c = maxC;//      pPStart.r = maxR;pPStart.g = ttt;ttt ++;}
}

二、 Two_Pass算法
使用:
// 二值图像标记算法并获得联通区域信息
int LabelBiImgAndMsg(unsigned char *inBiImg,unsigned char *outLabelImg, int *numCandTarget,
int row, int col, std::vector& points)

其中:

ifndef POINT4D

define POINT4D

struct Point4D
{
int r; //目标点区域的质心位置
int c;
double dr; //目标点区域的质心位置的精确值
double dc;
int s; //目标点区域的大小
double g; //目标点区域中最大灰度值
Point4D& operator=(const Point4D &other)
{
if (&other==this)
return *this;
r = other.r;
c = other.c;
dr = other.dr;
dc = other.dc;
s = other.s;
g = other.g;
return *this;

};

};

endif

原始图像 标记图像
512X512图像,耗时10ms
256X256图像,耗时4ms。
代码分析:
1. 生成邻接表:当前行与相邻的上一行进行邻接标记;
2. 由邻接表生成图像映射表,合并邻接表生成连通区域;
3. 有映射表生成标记图像,并获取标记信息。

#include "StdAfx.h"
#include "subfuction.h"
#include "dibapi.h"
#include "QFileIO.h"
#include "method.h"//
//lable 2
//标记参数// 生成邻接表
int GenerateNeighborTable(unsigned char *inBiImg, unsigned char outNbTab[2 * MAXLINK], int *outNbLen, unsigned char *outLabelImg, int outRegionArea[MAXSUBBLOCK + 1], int *outLabelNum, int row, int col)
{// 对二值图像亮像素区域进行初扫描,初步标记,获取初步标记区域之间的邻接表// 返回值,非0 表成功,0 表失败// 输入:二值图像及其行列数// 输出:邻接表outNbTab,它的每一列,第一行和第二行存放连通的两个区域的初步标记号// outNbLen,邻接表的有效长度// outLabelImg,初步标记图像// 区域面积数组outRegionArea,第i个元素表示初步标记号为i的区域的面积,第0个元素无效// outLabelNum,初步标记的区域数,也是outRegionArea的有效长度int startPos, endPos, grayNum;int i, j, k;static int  grayLastLine[MAXSINGLELINK]; // 上一行与当前区域相邻的区域for (i = 0; i < MAXSINGLELINK; i++){grayLastLine[i] = 0;}// 初步标记图像初始化  ***可以省略***/*for (i = 0; i < row; i++){for (j = 0; j < col; j++){outLabelImg[i*col + j] = 0;}}*/// 邻接表初始化for (i = 0; i < 2 * MAXLINK; i++){outNbTab[i] = 0;}// 区域面积初始化for (i = 0; i < MAXSUBBLOCK; i++){outRegionArea[i] = 0;}*outLabelNum  = 0; // 初步标记号*outNbLen = 0; // 邻接表有效长度i=0, j=0;for (i=0; i<row; i++){for (j=0; j<col; j++){if (inBiImg[i*col+j]!=0){// 当前区域,为处于当前行的一线段startPos = j;do{j++;if (j==col)  break;} while (inBiImg[i*col+j]!=0); //查找该行第一个不为0的像素位置endPos = j-1;grayNum = 0;for (k=startPos-1; k<=endPos+1; k++){if (i-1<0)  break; // 图像首行无需察看其上一行if ((k<0)||(k>=col))  continue;if (outLabelImg[(i-1)*col+k] != 0) // 查看上一行与当前区域的相邻区域{if (grayNum>=MAXSINGLELINK){*outNbLen = 0;*outLabelNum = 0;return 0;}grayLastLine[grayNum] = outLabelImg[(i-1)*col+k];grayNum++;while (outLabelImg[(i-1)*col+k]!=0) k++;}}if (grayNum == 0) // 当前区域的上一行不存在其它区域与之相邻{(*outLabelNum)++; // 初步标记号递增if (*outLabelNum > MAXSUBBLOCK){*outNbLen = 0;*outLabelNum = 0;return 0;}if (*outNbLen>=MAXLINK){*outNbLen = 0;*outLabelNum = 0;return 0;}// 邻接表中增加新区域对应的项outNbTab[*outNbLen] = *outLabelNum;outNbTab[*outNbLen+MAXLINK] = *outLabelNum;(*outNbLen)++;// 区域面积数组中,增加新区域对应的项outRegionArea[*outLabelNum] = endPos - startPos + 1;for (k=startPos; k<=endPos; k++)outLabelImg[i*col+k] = *outLabelNum;}else{// 当前区域的标记号,与上一行与之相邻的第一个标记号相同for (k=startPos; k<=endPos; k++)outLabelImg[i*col+k] = grayLastLine[0];outRegionArea[grayLastLine[0]] += endPos - startPos + 1;// 当前区域和上一行与之相邻的标记号建立邻接关系for (k=1; k<grayNum; k++){if (*outNbLen>=MAXLINK){*outNbLen = 0;*outLabelNum = 0;return 0;}outNbTab[*outNbLen] = grayLastLine[0];outNbTab[*outNbLen+MAXLINK] = grayLastLine[k];(*outNbLen)++;}}}}}return 1;}// 生成映射表
void GenerateMap(unsigned char inNbTab[2 * MAXLINK], int nbTabLen, unsigned char outMap[MAXSUBBLOCK+1], int *outNumTrueObj)
{// 根据邻接表生成初步标记号与其真实标记号的映射关系// 输入:邻接表inNbTab 及其有效长度nbTabLen// 输出:映射表outMap,第i个元素表示初步标记号为i的区域的真实标记号// outNumTrueObj,真实目标数,也是真实标记号的最大值static  unsigned char  stack[MAXSINGLELINK];int   i, j, StackPtr, TmpValue;unsigned char *pneighbor0,*pneighbor1,*pneighbor2,*pneighbor3;for (i = 0; i < MAXSINGLELINK; i++){stack[i] = 0;}*outNumTrueObj  = 0;StackPtr = 0;pneighbor0 = &(inNbTab[0]);pneighbor1 = &(inNbTab[MAXLINK]);for (i=0; i<nbTabLen; i++,pneighbor0++,pneighbor1++){if (*pneighbor0!=0) // 邻接表中新的一项{stack[StackPtr] = *pneighbor0;StackPtr++;(*outNumTrueObj)++;*pneighbor0 = 0;*pneighbor1 = 0;}while (StackPtr != 0){// 当前存在于栈中的所有区域,其真实标记号相同StackPtr--;TmpValue = stack[StackPtr];// 初步标记号为TmpValue的区域的真实标记号outMap[TmpValue] = (*outNumTrueObj);pneighbor2 = pneighbor0;pneighbor3 = pneighbor1;for (j=i; j<nbTabLen; j++, pneighbor2++, pneighbor3++){if ((*pneighbor2 == TmpValue) || (*pneighbor3 == TmpValue)){if (*pneighbor2!=*pneighbor3){// 与TmpValue相邻的其它区域其真实标记号与TmpValue相同,故进栈stack[StackPtr] = (*pneighbor2!=TmpValue)? *pneighbor2: *pneighbor3;StackPtr++;}*pneighbor2 = 0;*pneighbor3 = 0;}}}}}void Map2LabelImg(unsigned char *inoutLabelImg,unsigned char inMap[MAXSUBBLOCK], int row ,int col)
{// 由映射表生成标记图像int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (inoutLabelImg[i* col + j] > 0){inoutLabelImg[i* col + j] = 255-inMap[inoutLabelImg[i * col + j]];}}}}// 二值图像标记算法
int LabelBiImg(unsigned char *inBiImg,unsigned char *outLabelImg, int *numCandTarget, int row, int col)
{// 对二值图像进行标记// 输出标记后图像和连通区域数static unsigned char map[MAXSUBBLOCK+1], neighbor[2*MAXLINK];static int tmpArea[MAXSUBBLOCK+1];int tabLen, labelNum; // 邻接表长度与初步标记区域数int i;for (i = 0; i < MAXSUBBLOCK + 1; i++) // 映射表与面积数组初始化{map[i] = 0;tmpArea[i] = 0;}for (i = 0; i < 2 * MAXLINK; i++) // 邻接表初始化{neighbor[i] = 0;}for (i = 0; i < row * col; i++) // 标记图像初始化{outLabelImg[i] = 0;}// 生成邻接表i = GenerateNeighborTable(inBiImg,neighbor, &tabLen, outLabelImg, tmpArea, &labelNum, row, col);if (i != 0){// 由邻接表生成映射表GenerateMap(neighbor, tabLen, map, numCandTarget);// 由映射表生成标记后图像Map2LabelImg(outLabelImg, map, row, col);if (i == 255){return 255;}return 1;}else{for (i = 0; i < row * col; i++){outLabelImg[i] = 0;}*numCandTarget = 0;return 0;}}void GetImgConnectMsg(unsigned char * LabelImg, int row, int col, int numCandTarget,  std::vector<Point4D>& points)
{if (!points.empty()){points.clear();}int i,j,nowNum;Point4D temp;temp.r = 0;temp.c = 0;temp.s = 0;for (i=0;i<numCandTarget;i++){points.push_back(temp);}for (i=0;i<row;i++){for (j=0;j<col;j++){nowNum = LabelImg[i*col + j]-1;if (nowNum>=0){points[nowNum].r = points[nowNum].r + i;points[nowNum].c = points[nowNum].c + j;points[nowNum].s = points[nowNum].s + 1;}}}for (i=0;i<numCandTarget;i++){points[i].r = points[i].r / points[i].s;points[i].c = points[i].c / points[i].s;}}// 二值图像标记算法并获得联通区域信息
int LabelBiImgAndMsg(unsigned char *inBiImg,unsigned char *outLabelImg, int *numCandTarget, int row, int col, std::vector<Point4D>& points)
{// 对二值图像进行标记// 输出标记后图像和连通区域数static unsigned char map[MAXSUBBLOCK+1], neighbor[2*MAXLINK];static int tmpArea[MAXSUBBLOCK+1];int tabLen, labelNum; // 邻接表长度与初步标记区域数int i;for (i = 0; i < MAXSUBBLOCK + 1; i++) // 映射表与面积数组初始化{map[i] = 0;tmpArea[i] = 0;}for (i = 0; i < 2 * MAXLINK; i++) // 邻接表初始化{neighbor[i] = 0;}for (i = 0; i < row * col; i++) // 标记图像初始化{outLabelImg[i] = 0;}// 生成邻接表i = GenerateNeighborTable(inBiImg,neighbor, &tabLen, outLabelImg, tmpArea, &labelNum, row, col);if (i != 0){// 由邻接表生成映射表GenerateMap(neighbor, tabLen, map, numCandTarget);//由标记的图像获取标记信息GetImgConnectMsg(outLabelImg, row, col, *numCandTarget,  points);// 由映射表生成标记后图像Map2LabelImg(outLabelImg, map, row, col);if (i == 255){return 255;}return 1;}else{for (i = 0; i < row * col; i++){outLabelImg[i] = 0;}*numCandTarget = 0;return 0;}}//

三、 DSP上处理:
采用Two_Pass算法
耗时:1.8ms

两种连通区域标记算法相关推荐

  1. 【机器视觉学习笔记】二值图像连通区域提取算法(C++)

    目录 原理 二值图像 连通区域(Connected Component) 连通区域分析(Connected Component Analysis,Connected Component Labelin ...

  2. OpenCV与图像处理学习九——连通区域分析算法(含代码)

    OpenCV与图像处理学习九--连通区域分析算法(含代码) 一.连通区域概要 二.Two-Pass算法 三.代码实现 一.连通区域概要 连通区域(Connected Component)一般是指图像中 ...

  3. 计算机设计复合材料,两种复合材料几何建模算法-计算机辅助设计与图形学学报.PDF...

    两种复合材料几何建模算法-计算机辅助设计与图形学学报 第27 卷 第 1 期 计算机辅助设计与图形学学报 Vol. 27 No.1 2015 年 1 月 Journal of Computer-Aid ...

  4. 两种图像骨架提取算法的研究原理及实现

    图像骨架提取,实际上就是提取目标在图像上的中心像素轮廓.说白了就是以目标中心为准,对目标进行细化,一般细化后的目标都是单层像素宽度.比如输入图像是这样: 输出骨架图像(红色) 关于骨架提取,现存的算法 ...

  5. 两种ICP的改进算法:PLICP与NICP

    前言 在之前的文章中(ICP方法详细推导),我们介绍了ICP的基本思想与详细的推导.本文将介绍ICP方法的两种改进,分别是:PLICP[1]与NICP[2].本文将分别介绍两种改进的基本思想,具体算法 ...

  6. 数据中台推荐系统入门(二):两种经典的推荐算法

    前言 所谓"物以类聚,人以群分",基于用户的协同过滤算法就是基于这个原理来实现的. 你可能听过"啤酒与尿不湿"的故事:通过对一家超市的销售数据分析发现,有很多人 ...

  7. 全排列的java算法_两种常用的全排列算法(java)

    问题:给出一个字符串,输出所有可能的排列. 全排列有多种算法,此处仅介绍常用的两种:字典序法和递归法. 1.字典序法: 如何计算字符串的下一个排列了?来考虑"926520"这个字符 ...

  8. POJ 4151/北大百练 4151 电影节 题解(两种方法)贪心算法基础

    POJ4151 电影节 总时间限制: 1000ms 内存限制: 65536kB 描述: 大学生电影节在北大举办! 这天,在北大各地放了多部电影,给定每部电影的放映时间区间,区间重叠的电影不可能同时看( ...

  9. 两种动态灰狼优化算法

    文章目录 一.理论基础 1.灰狼优化算法 2.第一种动态灰狼优化算法(DGWO1) 3.第二种动态灰狼优化算法(DGWO2) 二.仿真实验与分析 三.参考文献 一.理论基础 1.灰狼优化算法 请参考这 ...

最新文章

  1. html文本框填充颜色逐渐减少,web前端面试题之htmlcss篇
  2. 超强实时人像抠图算法开源,随心所欲背景替换!
  3. Android中调用webservice的工具类
  4. jQuery-动画与特效
  5. python 数列第几项开始,数值超过1200
  6. AngularJS之高级Route【三】(八)
  7. 架构设计 - 自动化运维之架构设计六要点
  8. 数据结构专题(一):1.1顺序表初始化
  9. 机器学习实战(三)朴素贝叶斯NB(Naive Bayes)
  10. [Ext JS6]工作区-Workspace
  11. 一张图学会python高清图-一张图带你学会Python,学习Python的非常简单,附教程
  12. mysql 返回几个组_MySQL – 控制由组返回的行
  13. 过去25年八大计算机病毒:冲击波和震荡波入选
  14. AutoCAD2020简体中文语言包
  15. 七牛sdk 上传视频 - 前端
  16. Python TCP接收/发送信息
  17. python爬虫--爬取网易云音乐评论
  18. Linux设置root权限
  19. 揭秘 | 2021年移动云API大赛决赛大奖花落谁家?
  20. 用python写诗歌网站要注意什么_如何利用深度学习写诗歌(使用Python进行文本生成)...

热门文章

  1. c语言输入三个数从大排到小,自学考试《C语言程序设计》练习题及答案_第3页...
  2. vue e2e 测试
  3. 牛郎织女都去约会了,你还窝在家里?熬夜准备的惊喜送给光棍的你!
  4. Centos下升级git版本
  5. 吴恩达——机器学习(logistic)
  6. 2022金九银十工作潮,怎么样才能成功跳槽面试拿到高薪呢?
  7. FPGA程序如何模块化设计?
  8. 操作系统expected ‘void * (*)(void *)’ but argument is of type ‘void * (*)(void)’
  9. postman脚本编写
  10. 电磁兼容01 EMC概论