二值图像连通域标记算法与代码加载栅格影响,提取像素转点存储shp文件

一、前言

二值图像,顾名思义就是图像的亮度值只有两个状态:黑(0)和白(255)。二值图像在图像分析与识别中有着举足轻重的地位,因为其模式简单,对像素在空间上的关系有着极强的表现力。在实际应用中,很多图像的分析最终都转换为二值图像的分析,比如:医学图像分析、前景检测、字符识别,形状识别。二值化+数学形态学能解决很多计算机识别工程中目标提取的问题。

二值图像分析最重要的方法就是连通区域标记,它是所有二值图像分析的基础,它通过对二值图像中白色像素(目标)的标记,让每个单独的连通区域形成一个被标识的块,进一步的我们就可以获取这些块的轮廓、外接矩形、质心、不变矩等几何参数。

下面是一个二值图像被标记后,比较形象的显示效果,这就是我们这篇文章的目标。

image

二、连通域

在我们讨论连通区域标记的算法之前,我们先要明确什么是连通区域,怎样的像素邻接关系构成连通。在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有2种:4邻接与8邻接。4邻接一共4个点,即上下左右,如下左图所示。8邻接的点一共有8个,包括了对角线位置的点,如下右图所示。

image        image

如果像素点A与B邻接,我们称A与B连通,于是我们不加证明的有如下的结论:

如果A与B连通,B与C连通,则A与C连通。

在视觉上看来,彼此连通的点形成了一个区域,而不连通的点形成了不同的区域。这样的一个所有的点彼此连通点构成的集合,我们称为一个连通区域。

下面这符图中,如果考虑4邻接,则有3个连通区域;如果考虑8邻接,则有2个连通区域。(注:图像是被放大的效果,图像正方形实际只有4个像素)。

image

三、连通区域的标记

连通区域标记算法有很多种,有的算法可以一次遍历图像完成标记,有的则需要2次或更多次遍历图像。这也就造成了不同的算法时间效率的差别,在这里我们介绍2种算法。

第一种算法是现在matlab中连通区域标记函数bwlabel中使的算法,它一次遍历图像,并记下每一行(或列)中连续的团(run)和标记的等价对,然后通过等价对对原来的图像进行重新标记,这个算法是目前我尝试的几个中效率最高的一个,但是算法里用到了稀疏矩阵与Dulmage-Mendelsohn分解算法用来消除等价对,这部分原理比较麻烦,所以本文里将不介绍这个分解算法,取而代这的用图的深度优先遍历来替换等价对。

第二种算法是现在开源库cvBlob中使用的标记算法,它通过定位连通区域的内外轮廓来标记整个图像,这个算法的核心是轮廓的搜索算法,这个我们将在文章中详细介绍。这个算法相比与第一种方法效率上要低一些,但是在连通区域个数在100以内时,两者几乎无差别,当连通区域个数到了 103 103数量级时,上面的算法会比该算法快10倍以上。

四、基于行程的标记

我们首先给出算法的描述,然后再结合实际图像来说明算法的步骤。

1,逐行扫描图像,我们把每一行中连续的白色像素组成一个序列称为一个团(run),并记下它的起点start、它的终点end以及它所在的行号。

2,对于除了第一行外的所有行里的团,如果它与前一行中的所有团都没有重合区域,则给它一个新的标号;如果它仅与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它;如果它与上一行的2个以上的团有重叠区域,则给当前团赋一个相连团的最小标号,并将上一行的这几个团的标记写入等价对,说明它们属于一类。

3,将等价对转换为等价序列,每一个序列需要给一相同的标号,因为它们都是等价的。从1开始,给每个等价序列一个标号。

4,遍历开始团的标记,查找等价序列,给予它们新的标记。

5,将每个团的标号填入标记图像中。

6,结束。

我们来结合一个三行的图像说明,上面的这些操作。

image

第一行,我们得到两个团:[2,6]和[10,13],同时给它们标记1和2。

第二行,我们又得到两个团:[6,7]和[9,10],但是它们都和上一行的团有重叠区域,所以用上一行的团标记,即1和2。

第三行,两个:[2,4]和[7,8]。[2,4]这个团与上一行没有重叠的团,所以给它一个新的记号为3;而[2,4]这个团与上一行的两个团都有重叠,所以给它一个两者中最小的标号,即1,然后将(1,2)写入等价对。

全部图像遍历结束,我们得到了很多个团的起始坐标,终止坐标,它们所在的行以及它们的标号。同时我们还得到了一个等价对的列表。


五、C++实现代码最后数据结果转换成shp文件用Arcgis打开

//测试栅格转矢量*********************************************************************************/
afx_msg void OnRaster2vector();

//连通区域属性结构
typedef struct tagMarkRegion
{
std::list<POINT> MarkPointList;//点列表
RECT rect;
}MarkRegion;


//定义MarkMap 结构,用来存放等价对
typedef struct tagEqualMark
{
int MarkValue1;    //标记值
int MarkValue2;    //标记值
} EqualMark;


//定义MarkMapping 结构,用来存放标记映射关系
typedef struct tagMarkMapping
{
int nOriginalMark;   //第一次扫描的标记
int nMappingMark;  //等价整理之后对应标记
} MarkMapping;


/*
功能说明:将所选出的等价关系,attach到list上里
参数说明:
pEqualMark   等价关系
num1         新的等价关系1
num2         新的等价关系2
nEqualNum    等价数组的个数
plEqualMark  存放等价数组的list
返回值:无
*/
template<typename elemType> void AttachEqualMark(EqualMark &pEqualMark, elemType num1, elemType num2, int & pEqualNum, std::list< EqualMark> & plEqualMark)
{
//num1小的情况
if (num1 < num2)
{
if (pEqualMark.MarkValue1 != num1
|| pEqualMark.MarkValue2 != num2)
{
pEqualMark.MarkValue1 = num1;
pEqualMark.MarkValue2 = num2;
//插入到数组中
pEqualNum++;
plEqualMark.push_back(pEqualMark);
}
}
//num2小的情况
else
{
if (pEqualMark.MarkValue2 != num1
|| pEqualMark.MarkValue1 != num2)
{
pEqualMark.MarkValue1 = num2;
pEqualMark.MarkValue2 = num1;
//插入到数组中
pEqualNum++;
plEqualMark.push_back(pEqualMark);
}
}


}


BOOL MarkImage(BYTE * lpImgBits, int & nMarkNumbers, int iColorType, long nImageWidth, long nImageHeigt, std::list< MarkRegion> &listMarkData);
/***********************************************************************************************/

void C***View::OnRaster2vector()
{
//加载栅格图像
GDALAllRegister();
OGRRegisterAll();//记得添加驱动注册  
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
const char* pszSrcFile = "E:\\zhangakang\\Raster\\wsiearth-0.tif";
const char* pszDstFile = "E:\\zhangakang\\Raster\\shape.shp";
GDALDataset* poSrcDS = (GDALDataset*)GDALOpen(pszSrcFile, GA_ReadOnly);
if (poSrcDS == NULL)
{
return ;
}


//
long nImgSizeX = poSrcDS->GetRasterXSize();
long nImgSizeY = poSrcDS->GetRasterYSize();
int bandcount = poSrcDS->GetRasterCount();


BYTE* pafScan = new byte[nImgSizeX*nImgSizeY*bandcount];
poSrcDS->RasterIO(GF_Read, 0, 0, nImgSizeX, nImgSizeY, pafScan, nImgSizeX, nImgSizeY, GDT_Byte, bandcount, 0, 0, 0, 0);


/** adfGeoTransform存储一些基准点的坐标和每一个pixel表示的长度等
GT[0],GT[3]是左上角坐标,
GT[1],GT[5]是图像总向横向分辨率,就是每个像素代表的长度,
GT[2],GT[4]是旋转、放大、缩小相关 */
double adfGeoTransform[6];
poSrcDS->GetGeoTransform(adfGeoTransform);


//获取投影坐标信息
const char* projRef = poSrcDS->GetProjectionRef();


// 创建输出矢量文件  
OGRSFDriver *poDriver;
poDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("ESRI Shapefile");
if (poDriver == NULL)
{
GDALClose((GDALDatasetH)poSrcDS);
return ;
}


//根据文件名创建输出矢量文件  
OGRDataSource* poDstDS = poDriver->CreateDataSource(pszDstFile);
if (poDstDS == NULL)
{
GDALClose((GDALDatasetH)poSrcDS);
return ;
}


/* 定义空间参考,与输入图像相同 projRef将会存储test.tif中的坐标信息 **/
OGRSpatialReference *poSpatialRef = new OGRSpatialReference(projRef);
OGRLayer* poLayer = poDstDS->CreateLayer("ResultwkbPoint", poSpatialRef, wkbPoint, NULL);
if (poDstDS == NULL)
{
GDALClose((GDALDatasetH)poSrcDS);
OGRDataSource::DestroyDataSource(poDstDS);
delete poSpatialRef;
poSpatialRef = NULL;
return ;
}
OGRFieldDefn ofieldDefLat("lat", OFTReal); //创建属性表,只有一个字段即“Segment”,里面保存对应的栅格的像元值  
poLayer->CreateField(&ofieldDefLat);
OGRFieldDefn ofieldDefLng("Lng", OFTReal); //创建属性表,只有一个字段即“Segment”,里面保存对应的栅格的像元值  
poLayer->CreateField(&ofieldDefLng);


//定义
double x(0.0), y(0.0);


//获取联通标记域 点列表
int markNums(0);
int colorType(0);
std::list< MarkRegion> mMarkRegionList;
if (MarkImage(pafScan, markNums, colorType, nImgSizeX, nImgSizeY, mMarkRegionList))
{
//遍历mMarkRegionList二值连通域标记列表
MarkRegion m_MarkRegion;
list<MarkRegion>::iterator itor;
for (itor = mMarkRegionList.begin(); itor != mMarkRegionList.end(); itor++)
{
//遍历单个联通区域点坐标列表
m_MarkRegion = *itor;
std::list< POINT> ::iterator itor2;
for (itor2 = m_MarkRegion.MarkPointList.begin(); itor2 != m_MarkRegion.MarkPointList.end(); itor2++)
{
POINT m_Point = *itor2;
x = m_Point.x;
y = m_Point.y;


x = adfGeoTransform[0] + x * adfGeoTransform[1] + adfGeoTransform[1]/2;
y = adfGeoTransform[3] + y * adfGeoTransform[5] + adfGeoTransform[5]/2;


//创建要素
OGRFeature *poFeature = NULL;
poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn());//创建一个要素         
poFeature->SetField(0, x);//设置要素中字段的值
poFeature->SetField(1, y);//设置要素中字段的值


//存储到图层要素中,创建点坐标
OGRPoint pt;
pt.setX(x);            //设置点的x坐标         
pt.setY(y);            //设置点的y坐标         
poFeature->SetGeometry(&pt);


if (poLayer->CreateFeature(poFeature) != OGRERR_NONE)
{
//          printf("Failed to create feature in shapefile \n");  
//          exit(1);  
}
OGRFeature::DestroyFeature(poFeature);


}
}


}
GDALClose(poSrcDS); //关闭文件  
OGRDataSource::DestroyDataSource(poDstDS);
}


/*
功能说明:快速二值图像连通域标记
参数说明:lpImgBits,表示图象数据区指针
nMarkNumbers,表示标记数量
iColorType,表示被标记颜色的值(,)
nImageWidth,表示图象的宽
nImageHeight,表示图象的高
返回值:BOOL类型,TRUE,表示成功;FLASE,表示失败
*/
BOOL C***View::MarkImage(BYTE * lpImgBits, int & nMarkNumbers, int iColorType, long nImageWidth, long nImageHeigt, std::list< MarkRegion> &listMarkData)
{
BYTE * lpImgBitsMove = NULL;//lpImgBitsMove,表示图象数据区偏移指针
int * lpMark = NULL;//lpMark,表示标记数据指针
int * lpMarkMove = NULL;//lpMarkMove,表示标记数据偏移指针
//iColorType为目标的图像值
long lSize = nImageWidth*nImageHeigt;
lpMark = new int[lSize + 1];
lpMarkMove = lpMark;
::memset(lpMark, 0, (lSize + 1)*sizeof(int));


int nMarkValue = 1;
/* 每次标识的值,nMarkValue会在后边递增,
来表示不同的区域,从开始标记。*/
int nMaxMarkValue = 0;     //记录最大的标识的值
int i, j;                 //循环控制变量


/* 定义存放等价对的链表,其元素是EqualMark类型,
定义list是为了节约存储空间。要使用Clist,
应该#include <Afxtempl.h>。 */
std::list<EqualMark> lEqualMark;


//初始化图像移动指针
lpImgBitsMove = lpImgBits;


/*进行第一次扫描,将所得的等价对(EqualMark类型)加到lEqualMark链表中。
使用nMarkValue来进行每一次新的标记,标记之后将其值加。


Note1:图像的四周像素并不会有个相邻的像素。这时就要根据上、下、左、
右四种不同的情况做不同的寻找等价对的判断。
Note2:可以先对等价对进行排序,每次都保证MarkValue1<MarkValue2,
这样易于管理等价对。
Note3:在实际工作中,连续寻找出的等价对很容易重复,将本次找出的等价对
和链表中保存的最后一个等价对相比较,如果不相等的话再存入等价对链表,
这样可以大大降低链表中等价对的重复。
Note4:第一次扫描之后,nMarkValue-1即为nMaxMarkValue。*/


/************************************************************************/
//下面为补充代码,完成对图像的第一次扫描


//初始化图像数组和标识数组的指针
int nEqualNum = 0;
EqualMark tempEqualMark;    //用以暂时存放每次找到的等价关系
lpMarkMove = lpMark;
lpImgBitsMove = lpImgBits;




//标记图像的第一行、第一列的像素(只有这一个像素)
if (*lpImgBitsMove == iColorType)
{
*lpMarkMove = nMarkValue++;
}
lpMarkMove++;
lpImgBitsMove++;


//标记图像的第一行,此时不会出现等价的情况
for (i = 1; i < nImageWidth; i++)
{
//需要标记的情况
if (*lpImgBitsMove == iColorType)
{
//前面没有被标记过,则开始一个新的标记
if (*(lpMarkMove - 1) == (!iColorType))
{
*lpMarkMove = nMarkValue++;
}
//前面被标记过,则跟随前一个标记
else
{
*lpMarkMove = *(lpMarkMove - 1);
}
}
lpMarkMove++;
lpImgBitsMove++;
}


//除第一行之外的标记,此时会出现等价的关系
for (j = 1; j < nImageHeigt; j++)
{
lpImgBitsMove = lpImgBits + j*nImageWidth;
lpMarkMove = lpMark + j*nImageWidth;


//对每行的第一个点做处理,总体就是对图像的最左列做处理
//只需要检视上,右上两个点
if (*lpImgBitsMove == iColorType)
{
//<上>位置被标记过
if (*(lpMarkMove - nImageWidth) != 0)
{
//跟随<上>标记
*lpMarkMove = *(lpMarkMove - nImageWidth);
if (*(lpMarkMove - nImageWidth) != *(lpMarkMove - nImageWidth + 1) && *(lpMarkMove - nImageWidth + 1) != 0)
{
//<上><右上>等价标记
AttachEqualMark(tempEqualMark, *(lpMarkMove - nImageWidth), *(lpMarkMove - nImageWidth + 1), nEqualNum, lEqualMark);
}


}
//<上>没有标记,此时一定不会存在等价关系
else
{
if (*(lpMarkMove - nImageWidth + 1) != 0)
{
*lpMarkMove = *(lpMarkMove - nImageWidth + 1);   //跟随<右上>标记
}
//<上>、<右上>都没有标记,则开始新的标记
else
{
*lpMarkMove = nMarkValue++;
}
}
}
lpMarkMove++;
lpImgBitsMove++;


//对每行的中间点做标记处理,此时存在<左>、<左上>、<上>、<右上> 4种情况
for (i = 1; i <= nImageWidth - 1; i++)
{
//需要标记
if ((*lpImgBitsMove) == iColorType)
{
//<左>被标记过
if (*(lpMarkMove - 1) != 0)
{
*lpMarkMove = *(lpMarkMove - 1);          //跟随<左>


if (*(lpMarkMove - 1) != *(lpMarkMove - nImageWidth - 1) && *(lpMarkMove - nImageWidth - 1) != 0)
{
//标记<左>、<左上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - 1), *(lpMarkMove - nImageWidth - 1), nEqualNum, lEqualMark);
}


if (*(lpMarkMove - 1) != *(lpMarkMove - nImageWidth) && *(lpMarkMove - nImageWidth) != 0)
{
//标记<左>、<上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - 1), *(lpMarkMove - nImageWidth), nEqualNum, lEqualMark);
}


if (*(lpMarkMove - 1) != *(lpMarkMove - nImageWidth + 1) && *(lpMarkMove - nImageWidth + 1) != 0)
{
//标记<左>、<右上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - 1), *(lpMarkMove - nImageWidth + 1), nEqualNum, lEqualMark);
}
}
//<左>未被标记过
else
{
//<左上>被标记过
if (*(lpMarkMove - nImageWidth - 1) != 0)
{
*lpMarkMove = *(lpMarkMove - nImageWidth - 1);


if (*(lpMarkMove - nImageWidth - 1) != *(lpMarkMove - nImageWidth) && *(lpMarkMove - nImageWidth) != 0)
{
//标记<左上>、<上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - nImageWidth - 1), *(lpMarkMove - nImageWidth), nEqualNum, lEqualMark);
}


if (*(lpMarkMove - nImageWidth - 1) != *(lpMarkMove - nImageWidth + 1) && *(lpMarkMove - nImageWidth + 1) != 0)
{
//标记<左上>、<右上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - nImageWidth - 1), *(lpMarkMove - nImageWidth + 1), nEqualNum, lEqualMark);
}




}
//<左>、<左上>未标记过
else
{
if (*(lpMarkMove - nImageWidth) != 0)
{
*lpMarkMove = *(lpMarkMove - nImageWidth);        //跟随<上>标记


if (*(lpMarkMove - nImageWidth) != *(lpMarkMove - nImageWidth + 1) && *(lpMarkMove - nImageWidth + 1) != 0)
{
//标记<上>和<右上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - nImageWidth), *(lpMarkMove - nImageWidth + 1), nEqualNum, lEqualMark);
}
}
//<左>、<左上>、<上>未标记过,此时不存在等价关系
else
{
if (*(lpMarkMove - nImageWidth + 1) != 0)
{
*lpMarkMove = *(lpMarkMove - nImageWidth + 1);       //跟随<右上>标记
}
//<左>、<左上>、<上>、<右上>未标记过,则开始新的标记值
else
{
*lpMarkMove = nMarkValue++;
}


}    //<左>、<左上>、<上>未标记过结束
}   //<左>、<左上>未标记过结束
}  //<左>未被标记过结束
}     // else 不需要标记


lpMarkMove++;
lpImgBitsMove++;
}       //中间点处理的结束


//对每行的最后一个点做处理,总体就是对图像的最左列做处理
//此时存在<左>、<左上>、<上> 3种情况


//需要标记
if ((*lpImgBitsMove) == iColorType)
{
//<左>被标记过
if (*(lpMarkMove - 1) != 0)
{
*lpMarkMove = *(lpMarkMove - 1);


if (*(lpMarkMove - 1) != *(lpMarkMove - nImageWidth - 1) && *(lpMarkMove - nImageWidth - 1) != 0)
{
//标记<左>、<左上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - 1), *(lpMarkMove - nImageWidth - 1), nEqualNum, lEqualMark);
}


if (*(lpMarkMove - 1) != *(lpMarkMove - nImageWidth) && *(lpMarkMove - nImageWidth) != 0)
{
//标记<左>、<上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - 1), *(lpMarkMove - nImageWidth), nEqualNum, lEqualMark);
}


}
//<左>未被标记过
else
{
if (*(lpMarkMove - nImageWidth - 1) != 0)
{
*lpMarkMove = *(lpMarkMove - nImageWidth - 1);    //跟随<左上>


if (*(lpMarkMove - nImageWidth - 1) != *(lpMarkMove - nImageWidth) && *(lpMarkMove - nImageWidth) != 0)
{
//标记<左上>、<上>等价
AttachEqualMark(tempEqualMark, *(lpMarkMove - nImageWidth - 1), *(lpMarkMove - nImageWidth), nEqualNum, lEqualMark);
}


}
//<左>、<左上>未标记过
else
{
if (*(lpMarkMove - nImageWidth) != 0)
{
*lpMarkMove = *(lpMarkMove - nImageWidth);   //跟随<上>标记
}
//<左>、<左上>、<上>未标记过,则开始新的标记值
else
{
*lpMarkMove = nMarkValue++;
}


}
}
}  //对每行的最后一个点做处理,总体就是对图像的最左列做处理


}     //"除第一行之外的标记"的结束


//因为在每次标记完之后,nMarkValue都会自动++
//所以要通过(-1)操作来记录所标记的最大的个数


nMaxMarkValue = nMarkValue - 1;
/************************************************************************/
/* 定义双层链表的外层链表,它的元素是一个指向内层链表的指针。
内层链表的型别也是CptrList,其元素是标记值。*/
CPtrList exList;
CPtrList * pInnerList;
POSITION posExElem;


if (lEqualMark.size() != 0)
{
// pInnerListAdd,每次向exList中添加的新元素
CPtrList * pInnerListAdd = new CPtrList;
ASSERT(pInnerListAdd != NULL);


/* 添加第一个等价对到exList的第一个元素所指向的InnerList中。 */
pInnerListAdd->AddTail((void *)lEqualMark.front().MarkValue1);
pInnerListAdd->AddTail((void *)lEqualMark.front().MarkValue2);
exList.AddTail((void *)pInnerListAdd);
lEqualMark.pop_front();


/* 定义pFindValue1和pFindValue2,存放在所有内层链表中找到特定值
的某个内层链表的头指针,也就是外层链表的某个元素值。*/
CPtrList * pFindValue1 = NULL;
CPtrList * pFindValue2 = NULL;


//整理剩余的等价对
while (!lEqualMark.empty())
{
posExElem = exList.GetHeadPosition();
pFindValue1 = NULL;
pFindValue2 = NULL;


while (posExElem)
{
pInnerList = (CPtrList *)exList.GetAt(posExElem);
if (pInnerList->Find((void *)lEqualMark.front().MarkValue1))
{
pFindValue1 = pInnerList;
}
if (pInnerList->Find((void *)lEqualMark.front().MarkValue2))
{
pFindValue2 = pInnerList;
}
exList.GetNext(posExElem);
}


//该等价对中两个值都在已经整理过的等价关系中
if (pFindValue1 && pFindValue2)
{
//当两个地址不一样时,对链表进行调整
if (pFindValue1 != pFindValue2)
{
pFindValue1->AddTail(pFindValue2);
/* 清除链表元素,通过new得到的CptrList 类型,
必须采用delete进行删除,否则会造成内存泄露。*/
POSITION posDelete = exList.Find((void *)pFindValue2);
pFindValue2->RemoveAll();
delete pFindValue2;
exList.RemoveAt(posDelete);
}
}
/* 只在已经整理过的等价关系中找到Value1,那么将Vaule2加到Value1所在的链表中。*/
else if (pFindValue1)
{
pFindValue1->AddTail((void *)lEqualMark.front().MarkValue2);
}
else if (pFindValue2)
{
pFindValue2->AddTail((void *)lEqualMark.front().MarkValue1);
}
/* 等价对中两个值在整理过的等价关系中都没有找到,则在exList中增加新元素。*/
else
{
CPtrList * pInnerListAdd = new CPtrList;
pInnerListAdd->AddTail((void *)lEqualMark.front().MarkValue1);
pInnerListAdd->AddTail((void *)lEqualMark.front().MarkValue2);
exList.AddTail((void *)pInnerListAdd);
}
//去掉此时等价对的头元素
lEqualMark.pop_front();
}  // while ( !lEqualMark.IsEmpty() )循环结束
}  // if ( lEqualMark.GetCount() !=0 )语句结束
else
{
/* 等价对链表大小为0,说明第一次扫描之后没有产生等价对,标记已经完成。*/
int nMarkRegion = 0;   //图像中连通区域个数
//记录连通区域的个数
nMarkRegion = nMarkValue - 1;
nMarkNumbers = nMarkRegion;
if (nMarkRegion == 0)
return FALSE;
//统计连通域属性信息
MarkRegion *pRegionData = new MarkRegion[nMarkNumbers + 1];
for (int MarkNo = 0; MarkNo<nMarkNumbers; MarkNo++)
{
pRegionData[MarkNo].rect.left = -1;
pRegionData[MarkNo].rect.right = -1;
pRegionData[MarkNo].rect.bottom = -1;
pRegionData[MarkNo].rect.top = -1;
pRegionData[MarkNo].MarkPointList.clear();
}


for (j = 0; j<nImageHeigt; j++)
{
lpMarkMove = lpMark + j*nImageWidth;
for (i = 0; i<nImageWidth; i++)
{
if (*lpMarkMove > 0)
{
if (pRegionData[*lpMarkMove - 1].rect.left == -1)
{
pRegionData[*lpMarkMove - 1].rect.left = i;
pRegionData[*lpMarkMove - 1].rect.right = i;
pRegionData[*lpMarkMove - 1].rect.bottom = j;
pRegionData[*lpMarkMove - 1].rect.top = j;
POINT ptInsert;
ptInsert.x = i;
ptInsert.y = j;
pRegionData[*lpMarkMove - 1].MarkPointList.push_back(ptInsert);
}
else
{
POINT ptInsert;
ptInsert.x = i;
ptInsert.y = j;
pRegionData[*lpMarkMove - 1].MarkPointList.push_back(ptInsert);
if (pRegionData[*lpMarkMove - 1].rect.left>i)
{
pRegionData[*lpMarkMove - 1].rect.left = i;
}
if (pRegionData[*lpMarkMove - 1].rect.right<i)
{
pRegionData[*lpMarkMove - 1].rect.right = i;
}
if (pRegionData[*lpMarkMove - 1].rect.top<j)
{
pRegionData[*lpMarkMove - 1].rect.top = j;
}
if (pRegionData[*lpMarkMove - 1].rect.bottom>j)
{
pRegionData[*lpMarkMove - 1].rect.bottom = j;
}
}
}
lpMarkMove++;
}
}
for (i = 0; i<nMarkNumbers; i++)
{
listMarkData.push_back(pRegionData[i]);
}
if (pRegionData)
{
delete[]pRegionData;
pRegionData = NULL;
}
return TRUE;
}


/*等价关系整理完成,下面建立第一次扫描的标记值和
第二次扫描的标记值之间的映射关系。*/


int nTotalEqualNum = 0; //列入等价关系的标记个数
int nMarkRegion = 0;   //图像中连通区域个数


posExElem = exList.GetHeadPosition();
while (posExElem)
{
pInnerList = (CPtrList *)exList.GetAt(posExElem);
nTotalEqualNum += pInnerList->GetCount();
exList.GetNext(posExElem);
}
nMarkRegion = nMaxMarkValue - nTotalEqualNum + exList.GetCount();


/* 定义第一次扫描和第二次扫描之间的映射向量,要使用vector,
应该#include <vector>并且使用std命名空间。*/
std::vector<MarkMapping> vMarkMap(nMaxMarkValue);


//初始化映射向量,令其做自身映射
for (i = 0; i<nMaxMarkValue; i++)
{
vMarkMap[i].nOriginalMark = i + 1;
vMarkMap[i].nMappingMark = i + 1;
}


POSITION posInnerElem; //InnerList中元素的位置
int nMin;              //InnerList中最小值
int nIndex = 0;


posExElem = exList.GetHeadPosition();
/* while循环实现了如下功能:找到每个等价组中最小的标记值,
然后将映射向量中nMappingMark设定为其所在等价组的最小的标记值。*/
while (posExElem)
{
pInnerList = (CPtrList *)exList.GetAt(posExElem);
nMin = (int)pInnerList->GetHead();
posInnerElem = pInnerList->GetHeadPosition();
pInnerList->GetNext(posInnerElem);


while (posInnerElem)
{
if ((int)pInnerList->GetAt(posInnerElem)<nMin)
{
nMin = (int)pInnerList->GetAt(posInnerElem);
}
pInnerList->GetNext(posInnerElem);
}


/* 根据每组等价关系中的最小的标记值对Mapping向量做出调整。*/
posInnerElem = pInnerList->GetHeadPosition();
while (posInnerElem)
{
nIndex = (int)pInnerList->GetAt(posInnerElem) - 1;
vMarkMap[nIndex].nMappingMark = nMin;
pInnerList->GetNext(posInnerElem);
}
exList.GetNext(posExElem);
}


/* 将映射向量nMappingMark中不重复的部分找出并对其进行排序。
使用find()和sort()这两种泛型算法,应该#include <algorithm>。*/
std::vector <int> vSortMark(nMarkRegion); //排序向量
nIndex = 0;


for (i = 0; i<nMaxMarkValue; i++)
{
if (find(vSortMark.begin(), vSortMark.end(), vMarkMap[i].nMappingMark)
== vSortMark.end())
{
vSortMark[nIndex++] = vMarkMap[i].nMappingMark;
}
}
sort(vSortMark.begin(), vSortMark.end());


/* 根据排序后的标记在vSortMark向量中的位置,对映射向量做出重新调整。*/
std::vector<int>::iterator itFind;
std::vector<int>::iterator itBegin;
itBegin = vSortMark.begin();


for (i = 0; i<nMaxMarkValue; i++)
{
itFind = find(vSortMark.begin(), vSortMark.end(), vMarkMap[i].nMappingMark);
vMarkMap[i].nMappingMark = (itFind - itBegin + 1);
}


//根据映射向量对标记数组进行调整
for (j = 0; j<nImageHeigt; j++)
{
lpMarkMove = lpMark + j*nImageWidth;
for (i = 0; i<nImageWidth; i++)
{
if (*lpMarkMove != 0)
{
*lpMarkMove = vMarkMap[*lpMarkMove - 1].nMappingMark;
}
lpMarkMove++;
}
}


//删除链表结构中通过new得到的元素
posExElem = exList.GetHeadPosition();
while (posExElem)
{
pInnerList = (CPtrList *)exList.GetAt(posExElem);
pInnerList->RemoveAll();
delete pInnerList;
exList.GetNext(posExElem);
}
exList.RemoveAll();


//记录连通区域的个数
nMarkNumbers = nMarkRegion;


if (nMarkRegion == 0)
return FALSE;


//统计连通域属性信息
MarkRegion *pRegionData = new MarkRegion[nMarkNumbers + 1];
for (int MarkNo = 0; MarkNo<nMarkNumbers; MarkNo++)
{
pRegionData[MarkNo].rect.left = -1;
pRegionData[MarkNo].rect.right = -1;
pRegionData[MarkNo].rect.bottom = -1;
pRegionData[MarkNo].rect.top = -1;
pRegionData[MarkNo].MarkPointList.clear();
}


//long offset = 0;
for (j = 0; j<nImageHeigt; j++)
{
lpMarkMove = lpMark + j*nImageWidth;
for (i = 0; i<nImageWidth; i++)
{
//offset = j*nImageWidth+i;
//lpMarkMove = lpMark+offset;  
if (*lpMarkMove > 0)
{
if (pRegionData[*lpMarkMove - 1].rect.left == -1)
{
pRegionData[*lpMarkMove - 1].rect.left = i;
pRegionData[*lpMarkMove - 1].rect.right = i;
pRegionData[*lpMarkMove - 1].rect.bottom = j;
pRegionData[*lpMarkMove - 1].rect.top = j;
POINT ptInsert;
ptInsert.x = i;
ptInsert.y = j;
pRegionData[*lpMarkMove - 1].MarkPointList.push_back(ptInsert);
}
else
{
POINT ptInsert;
ptInsert.x = i;
ptInsert.y = j;
pRegionData[*lpMarkMove - 1].MarkPointList.push_back(ptInsert);
if (pRegionData[*lpMarkMove - 1].rect.left>i)
{
pRegionData[*lpMarkMove - 1].rect.left = i;
}
if (pRegionData[*lpMarkMove - 1].rect.right<i)
{
pRegionData[*lpMarkMove - 1].rect.right = i;
}
if (pRegionData[*lpMarkMove - 1].rect.top>j)
{
pRegionData[*lpMarkMove - 1].rect.top = j;
}
if (pRegionData[*lpMarkMove - 1].rect.bottom<j)
{
pRegionData[*lpMarkMove - 1].rect.bottom = j;
}
}
}
lpMarkMove++;
}
}
for (i = 0; i<nMarkNumbers; i++)
{
listMarkData.push_back(pRegionData[i]);
}
if (pRegionData)
{
delete[]pRegionData;
pRegionData = NULL;
}
return TRUE;


}


图片展示





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值