两种连通区域标记算法

来源:互联网 发布:淘宝自制护肤品北海 编辑:程序博客网 时间: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

0 0
原创粉丝点击