两种连通区域标记算法
来源:互联网 发布:淘宝自制护肤品北海 编辑:程序博客网 时间:2024/05/17 06:38
一、 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; } else outImg[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
- 两种连通区域标记算法
- 多值连通区域标记算法
- 区域标记-8连通与4连通算法
- 二值图像连通区域标记法,两步法
- 连通区域的标记
- 多值连通区域标记算法的matlab实现
- 广度优先搜索写的连通区域标记算法
- 连通区域快速标记的two-pass算法及其实现
- 二值图的连通区域标记
- 连通域标记的几种算法
- 连通分量标记算法
- matlab练习程序(二值图像连通区域标记法,两步法)
- 区域标记算法
- 区域标记算法
- 区域标记算法
- matlab多值连通区域标记
- 二值的连通区域标记
- Matlab实现迭代法连通区域标记
- GenyMotion无法下载或无法登录
- 【Android】Parse 开发
- Eclipse打包apk报错原因分析(一)
- [jvm解析系列][二]Java堆的详细讲解和对象的分配过程和访问
- 有效处理Java异常的三个原则,你知道吗?
- 两种连通区域标记算法
- 奖学金-贪心法
- 【LeetCode】310. Minimum Height Trees
- Maven项目管理
- Ubuntu下vi编辑器方向键变成字母的解决方法
- poj 1505 Copying Books 动态规划
- 三方登录和分享 的坑
- C 结构和其他数据形式
- 8张图理解Java