一、 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;
};
};
#endif
class 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