用vc++实现连通区域标记
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
示等方面,很多文章都有详细的叙述,这里就不再赘述了。
利用VC++的Classwizard创建cImage类,增加如下成员变
量。
Iong m_JngWldth: Iong m_JngwldthBytes:
//图像宽 //图像宽所占字节数
Iong m_jngHeight:
//图像高
long m』IngBltsCount:
件数据,初始化ⅡUngwidth、m_lngwidthBytes、Ⅱd“gHeight、
m』ln班itscount、HdpImgBits等成员变量。增加showBMPFile
()成员函数来显示BMP文件。
由于版面原因,ReadBMPFile()和ShowBMPFile()成员函数
的具体实现就不在此给出了,读ห้องสมุดไป่ตู้可以向杂志社购买源代码光
//图像数据所占bit数
LPBYTE m_JpImgBits:
//图像数据区域指针
LPBYTE m_JpImgBitsMove://数据区域移动指针
m}mJpnMark:
//标记数组指针
Int}mJpnMarkMove:
//标记数组移动指针
int m』MarkNumbe rs:
//标记的区域个数
给cIrr-age类增加ReadBMPFile()成员函数来得到BMP文
Ma rkValuel):
DlnnerLlstAdd一>AddTalI(《void {)IEqualMa rk GetHead()
Ma rkVaIue2):
exLlst AddTaif((voId¥)pl nnerLlstAdd): lEauaIMark RemoveHead():
/¥定义pFindVaIuel和pFindVaIue2.存放在所有内层链表 中找到特定值的某个内层链表的头指针,也就是外层链表的某 个元素值:*/
ASSERT{m-』pnMa rk!=NULL):
mJpnMa rkMove=m_JpnMa rk: )
::memset《(LPBYTE)m_jpnMark,0,m』IngBitsCount木4):
int nMarkVaIue=1:
/+每次标识的值.nMa rkValue会在后边递增.来表示不同的
区域,从1开始标记*/
Int nMa×Ma rkValue=O:
//记录最大的标识的值
int i,j:
//循环控制变量
/+定义存放等价对的链表,其元素是EquaIMa rk类型,定义
Iist是为了节约存储空问要使用C|ist,应该#lncIude<Afx-
temOl h> }/
CList<EquaIMark,EqualMa rk>『EqualMark: //初始化图像移动指针
的判断。 Note2:可以先对等价对进行排序,每次都保证MarkVal—
uel<MarkValue2,这样易于管理等价对。
Note3:在实际工作中,连续寻找出的等价对很容易重
复,将本次找出的等价对和链表中保存的最后一个等价对相比 较,如果不相等的话再存入等价对链表,这样可以大大降低链
表中等价对的重复。 Note4:第一次扫描之后,nMarkValue一1即为nMa)【Mark—
//pInne rListAdd,每次向exList中添加的新元素
CPtrList。pInnerListAdd=new CPtrList:
ASSERT(pInne rLjstAdd!=NULL):
/+添加第一个等价对到e×Llst的第一个元素所指向的h
ne r L』jst中
¥/
p JnnerLjstAdd一)AddTa JI(f vojd 半 )JEquaIMark GetHead f)
//标记值2
)EqualMark:
//定义MarkMap结构,用来存放标记映射关系
typedef struct tagMarkMapping
{ jnt n0 rlginaMark: //第一次扫描后的标记 m nMapplngMa rk://等价整理之后对应标记
)Ma rkMapping: 下面给出MarkImage()成员函数较为完整的代码。
pFlndVaIue2=NuLL: whffe(posE×Efem j {
plnnerList=(cPtrList{)exList GetAt{posE×Elem): if《pInne rList一>Find((void术)IEquaIMa rk GetHead《).Mark—
P{ …
pFindVaIuel=pInnerList: }
if(pfnnerList一>Flnd((void¥)『EqualMark.GetHead()Mark. va Jue2)) {
pFindValue2=plnne rList: } exLjst GetNext f posE×E Jem): )
//该等价对中两个值都在已经整理过的等价关系中 ff(pFindVafuel&&pFindVaIue2) f //当两个地址不一样时,对链表进行调整 if(pFindValue 1 1=pFindValue2)
{
pFindVaIuel一>AddTall(pFindVaIue2): /8清除链表元素,通过new得到的cptrList类型,必须采用 deIete进行删除,否则会造成内存泄露.。/
PosmoN posDelete=exL随Find((Void术)听ndVaIue2):
pFindVa『ue2一>RemoVeA||(): de Jete pFIndVaJue2j exLIst RemoveAt(posDeIete):
;)
/8只在已经整理过的等价关系中找到Vafuel,那么将vaufe2 加到VaIuel所在的链表中*/ eIse if(pFindValuel)
1
pF}ndVaJuel一>Add下a』f((vofd¥)iEquafMark GetHead()Ma rk、
P) 2卜
eIse lf(pFindValue2) (
pInnerLfstAdd一>AddTaif(f vofd¥}fEquafMark GetHead(J Ma rk— VaIue2):
e×List AddTail((void半)pInnerListAdd):
f) I //去掉此时等价对的头元素
用阳卡争|饕现唐≯’雾蓬通区匆《稼萄参I
|i |i。;::
玉案静。。豫玲。褰建永金赞
一、引言
用图像处理方法做目标检测的一般顺序是:图像预处 理、边缘检测、阈值分割、区域标记、形状判断分析。
进行区域标记之前的图像一般已经被处理为二值图像。 如图1所示,二值图像中可能有多个连通区域。进行图像检 测的时候往往关心的是每个连通区域各自的特性。这就需要 使用区域标记的方法把不同的连通区域区分开来。
适合标记方形、圆形等规则的形状,并不是很实用。像素标 记法和游程连通性分析是两种实用的方法,能够标记出符合 连通性质的所有连通区域。这里我们只介绍易于理解的像素 标记法,游程连通性分析可以参看《图像处理和分析上册》 章毓晋编著P.206的介绍。
假设图像中目标像素的灰度为1,背景像素灰度为o。 标记算法只对目标像素进行标记,而不针对背景像素。
首先对一幅图像从左到右、从上到下进行扫描。假如当 前像素的灰度值为0,就移到下一个扫描位置。假如当前像 素的灰度值为1,检查它左、左上、上、右上这4个相邻像 素(根据所采用的扫描次序,当我们到达当前像素时这4个 相邻像素已经被处理过)。如果上述4个相邻像素的灰度值 都为0,就给当前像素一个新的标记值。如果4个相邻像素 中只有一个像素P的灰度值为l,就把P像素的标记值赋给 当前像素。如果4个相邻像素中有m(1<m<=4)个像素的 灰度值为l,则按照左、左上、上、右上的优先顺序,确定 当前像素的标记值。然后对这m个像素所拥有的标记值做等 价对,并将其归入一个等价对数组中。例如,4个像素的灰 度值都为1时,将左边像素的标记值赋给当前像素,然后做 出左等价左上,左等价上,左等价右上三个等价对,最后将 这三个等价对加入等价对数组。第一次扫描结束后,所有灰 度值为1的点都已经被标记过,但有些标记是等价的。
盘或直接发信到wholeh叩e@mailst.xjtu.edu.cn来要索取本文 的完整代码。
为了保持原来图像数据的完整性,标记值不对原来的图像
数据造成破坏。程序中另外申请一块与图像数据大小相同的
int型内存区域来存放标记,并将这段内存区域初始化为0。
像素标记算法的编程难点是对第一次找出的所有等价对进
行判断,整理出全部的等价关系并做出重新映射。而第一次扫
描时,进行等价对的寻找则相对简单的多。
填加两个struct,用于完成等价变换。
//定义MarkMap结构,用来存放等价对
typedef struct tagEqualMark
{ m MarkVaIuel: //标记值1
int Ma rkVaIue2:
pFIndVaIue2一>AddTalI((VoId:#)IEquaIMark GetHead f)Mark.
P) ¨:
/。:等价对中两个值在整理过的等价关系中都没有找到,则在 exList中增加新元素二一/
芦,
o
cPtrList 8 pfnnef LfstAdd=new cPtrList: pInnerLIstAdd一>AddTa{I(f VoId¥)JEqua JMark GetHead()Mark— valuel):
整理等价对数组,把等价对整理为等价关系。进行第二 次图像扫描,根据整理所得的等价关系来进行重新标记。第 二次扫描结束之后,所有灰度值为1的目标区域都被标记了 不同的标记值。根据不同的标记值就可以区分不同的连通区 域。
四、算法实现
本文采用Vc++实现“像素标记法”。这里所处理的
图像为二值化之后的8位BMP图像。关于图像的读入、显
Long CImage::Ma rkImage(BYTE bobjectG ray) { //bobjectG ray为目标的灰度值,文本可以设为255 /+申请标记数组空间,记得要在CImage类的构造函数中将 m-jpnMark初始化为NULL {/ if(m_jpnMark==NULL) ( m_JpnMark=new int【m』IngBitsCount 1:
图1 经过前期处理所得到的二值图像
二、连通区域
像素间的连通性是确定区域的一个重要概念。在二维图 像中,假设目标像素周围有m(m<=8)个相邻的像素,如果 该像素灰度与这m个像素中某~个点A的灰度相等,那么 称该像素与点A具有连通性。常用的连通性有4连通和8连 通。4连通一般选取目标像素的上、下、左、右四个点。8 连通则选取目标像素在二维空间中所有的相邻像素。我们以 下讨论的都采用了在实践中常选用的8连通区域。
将所有具有连通性的像素作为一个区域则构成了一个连 通区域。那么对于图1,每个分离的白色目标区域被认为是 一个连通区域。要注意的是,黑色背景同时也是一个连通区 域。当然背景也可能被目标分成多个连通区域。目标和背景 都是相对而言的。
三、标记算法
图像处理中有很多不同的标记算法。有些简单的算法只
蕊赫淼秘 万方数据
m_JpImgBitsMove=m_JplmgBits: /+进行第一次扫描,将所得的等价对《EquaIMark类型)加到
lEqualMa rk链表中使用nMarkValue来进行每一次新的标记, 标记之后将其值加1
由于版面关系,这部分代码也同样略去不写。作者提出以
下几点编程时要注意的地方。
Notel:图像的四周像素并不会有8个相邻的像素。这时 就要根据上、下、左、右四种不同的情况做不同的寻找等价对
Value。
/*定义双层链表的外层链表,它的元素是一个指向内层链表 的指针内层链表的型别也是CptrList,其元素是标记值 +/ CPtrLlst e×List:
CPtrList水DlnnerList:
POSITf0N pOsE×Elem:
if(1EauaIMark GetCount()!=O)
{
CPtrList半DFlndValuel=NULL: CPtrList:。DFlndValue2=NULL:
//整理剩余的等价对 while(!{EqualMark lsEmpty()) (
万方数据
posExEIem=exLjst.GetHeadPos Jtjon f)j pFlndVaIuel=NuLL: