两种连通区域标记算法

一、 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

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值