您的位置:首页 > 其它

基于区域生长的二值图像标记,去掉区域的细小直线段并选择

2013-02-01 17:21 423 查看

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include "iostream"
#include <fstream>
using namespace std;
//八邻域
static int NeighborDirection8[8][2] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};
static int NeighborDirection4[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
typedef unsigned char Byte;

//定义队列
typedef struct QNode
{
int data;
struct QNode *next;
}QNode;
//采用的链式队列的结构
typedef struct Queue
{
struct QNode* first;
struct QNode* last;
}Queue;

int PopQueue(Queue *queue)
{
QNode *p = NULL;
int data;
if(queue->first == NULL)
{
return -1;
}
p = queue->first;
data = p->data;
if(queue->first->next == NULL)
{
queue->first = NULL;
queue->last = NULL;
}
else
{
queue->first = p->next;
}
free(p);
return data;
}
void PushQueue(Queue *queue, int data)
{
QNode *p = NULL;
p = (QNode*)malloc(sizeof(QNode));
p->data = data;
if(queue->first == NULL)
{
queue->first = p;
queue->last = p;
p->next = NULL;
}
else
{
p->next = NULL;
queue->last->next = p;
queue->last = p;
}
//free(p);
}

void SearchNeighbor(int *bitmap, int width, int height, int *labelmap, int labelIndex, int pixelIndex, Queue *queue)
{
int searchIndex, i, length;
labelmap[pixelIndex] = labelIndex;
length = width * height;
for(i = 0;i < 8;i++)
{
searchIndex = pixelIndex + NeighborDirection8[i][0] * width + NeighborDirection8[i][1];
if(searchIndex > 0 && searchIndex < length &&
bitmap[searchIndex] == 255 && labelmap[searchIndex] == 0)
{
labelmap[searchIndex] = labelIndex;
PushQueue(queue, searchIndex);
}
}
}

//主调用函数
int ConnectedComponentLabeling(int *bitmap, int width, int height, int *labelmap)
{
int cx, cy, popIndex , labelIndex = 0;
int index;
Queue *queue = NULL;
queue = (Queue*)malloc(sizeof(Queue));
queue->first = NULL;
queue->last = NULL;
/*memset(labelmap, 0, width * height);*/
for(cy = 1; cy < height - 1; cy++)
{
for(cx = 1; cx < width - 1; cx++)
{
index = cy * width + cx;
if(bitmap[index] == 255 && labelmap[index] == 0)
{
labelIndex++;
SearchNeighbor(bitmap, width, height, labelmap, labelIndex, index, queue);
popIndex = PopQueue(queue);
while(popIndex > -1)
{
SearchNeighbor(bitmap, width, height, labelmap, labelIndex, popIndex, queue);
popIndex = PopQueue(queue);
}
}
}
}
free(queue);
return labelIndex;
}

void chooselabelarea(RECT *arearect)
{
char chFileName[]="1.bmp";
IplImage *pImage=NULL;
int nRegionNumber=0;
int nImageWidth=0, nImageHeight=0, i=0, j=0;

//OpenCV输入图像
pImage = cvLoadImage( chFileName, CV_LOAD_IMAGE_GRAYSCALE);
assert(pImage!=NULL);
nImageWidth=pImage->width, nImageHeight=pImage->height;

int *binaryImage = (int *)calloc(nImageWidth*nImageHeight, sizeof(int));
int *labeImage = (int *)calloc(nImageWidth*nImageHeight, sizeof(int));
assert(binaryImage!=NULL && labeImage!=NULL);

//提取图像数据,并将像素值转换为1
for ( i=0; i<nImageHeight; i++ )
{
for ( j=0; j<nImageWidth; j++ )
{
int temp = (uchar)(*(pImage->imageData+i*pImage->widthStep+j));
if(temp > 245)
binaryImage[i*nImageWidth+j] = 0;
else
binaryImage[i*nImageWidth+j] = 255;
}
}

for ( i=0; i<nImageHeight; i++ )
{
for ( j=0; j<nImageWidth; j++ )
{
int temp = binaryImage[i*nImageWidth+j];
*(pImage->imageData+i*pImage->widthStep+j) = (uchar)temp;
}
}
cvNamedWindow("二值图");
cvShowImage("二值图", pImage);
cvWaitKey(0);

nRegionNumber = ConnectedComponentLabeling(binaryImage, nImageWidth, nImageHeight, labeImage);//连通域标记

//保存标记数组
ofstream outFile;
outFile.open("E:\\label.txt");
int temp = 0;
for ( i=0; i<nImageHeight; i++ )
{
for ( j=0; j<nImageWidth; j++ )
{
temp = labeImage[i*nImageWidth+j];
outFile<<temp<<" ";
}
outFile<<endl;
}
outFile.close();
//输出检测到的连通区域数目
printf("检测到连通区域数目为:%d\n",nRegionNumber);

//存储各个标记区域的标记的点数、横坐标最大最小值、纵坐标最大最小值
int *areaCharacter = (int *)calloc(nRegionNumber*5, sizeof(int));
for(i = 0; i < nRegionNumber; i++)
{
areaCharacter[i*5+2] = nImageWidth;
areaCharacter[i*5+4] = nImageHeight;
}
for ( i=0; i<nImageHeight; i++ )
{
for ( j=0; j<nImageWidth; j++ )
{
int temp = labeImage[i*nImageWidth+j];
if(temp > 0)
{//查找连通域的特征点
int k = (temp-1)*5;
areaCharacter[k] += 1;//该标记的点数
if(j > areaCharacter[k+1])
areaCharacter[k+1] = j;//横坐标最大
if(j < areaCharacter[k+2])
areaCharacter[k+2] = j;//横坐标最小值
if(i > areaCharacter[k+3])
areaCharacter[k+3] = i;//纵坐标最大
if(i < areaCharacter[k+4])
areaCharacter[k+4] = i;//纵坐标最小值
}
}
}

//选择连通域
int num = 0;
for(int n = 0; n < nRegionNumber; n++)
{
if(areaCharacter[n*5] < 100)//点数大小
areaCharacter[n*5] = 0;
else if(areaCharacter[n*5+1]-areaCharacter[n*5+2] <= 5)//列宽太小
areaCharacter[n*5] = 0;
else if(areaCharacter[n*5+3]-areaCharacter[n*5+4] <= 5)//行宽太小
areaCharacter[n*5] = 0;
else
num++;
}

int num1 = 0;//去掉直线段并重新分连通域后的连通域个数
int flag = 0;
//去线段更新连通域的特征值,若连段位于连通域中间则对该连通域重新分区
for(int n = 0; n < nRegionNumber; n++)
{
flag = 0;
if(areaCharacter[n*5] > 0)//剩余的连通域处理
{
int premaxrow = 0;//该连通域前面所有列的行最大值
int preminrow = nImageHeight;
int startcol = -1;//用于去直线段
int endcol = -1;
int linepoints = 0;//直线段点数
int maxrow = 0;//直线段的前面所有列的行最大值
int minrow = nImageHeight;
int npoints = 0;//直线段上噪点的统计
flag = 1;
num1++;

//按列搜索该标记值的高度差
for(i = areaCharacter[n*5+2];i < areaCharacter[n*5+1];i++)//列
{
int maxrow1 = 0;//当前列的行最大值
int minrow1 = nImageHeight;
for(j = areaCharacter[n*5+4];j < areaCharacter[n*5+3];j++)//行
{
if((labeImage[j*nImageWidth+i]) == (n+1))
{
if(j > maxrow1)
maxrow1 = j;
if(j < minrow1)
minrow1 = j;
if(j > premaxrow)
premaxrow = j;
if(j < preminrow)
preminrow = j;
}
}
if(maxrow1 - minrow1 <= 5)//直线段
{
maxrow = MAX(maxrow, maxrow1);
minrow = MIN(minrow, minrow1);
npoints = 0;
}
else
{
npoints++;
}
if(maxrow-minrow >= 0 && maxrow-minrow <= 5 && npoints <= 3)
{
linepoints++;
endcol = i;
if(startcol == -1)//一条直线段只更新一次起始点
startcol = i;
}
else if(linepoints > 10)//直线段
{
//去掉直线段
for(int k = minrow; k < minrow+5; k++)
{
for(int l = startcol; l < endcol-npoints; l++)
{
if(labeImage[k*nImageWidth+l] == n+1)
{
labeImage[k*nImageWidth+l] = 0;
areaCharacter[n*5]--;
}
}
}

//直线段两边都含有区域时,对其进行拆分,重新标记后部分的区域
if(endcol < areaCharacter[n*5+1]-5 && startcol > areaCharacter[n*5+2]+5)//直线段位于区域中间
{
//num1++;
flag = 1;
nRegionNumber++;
areaCharacter = (int *)realloc(areaCharacter, nRegionNumber*5*sizeof(int));
int m = (nRegionNumber-1)*5;
areaCharacter[m] = areaCharacter[n*5];
areaCharacter[m+1] = areaCharacter[n*5+1];
areaCharacter[m+2] = endcol-npoints;//areaCharacter[n*5+2];
//areaCharacter[m+3] = areaCharacter[n*5+3];
//areaCharacter[m+4] = areaCharacter[n*5+4];
maxrow1 = 0;
minrow1 = nImageHeight;
for(int k = endcol-npoints; k < areaCharacter[n*5+1]; k++)
{
for(int l = areaCharacter[n*5+4]; l < areaCharacter[n*5+3]; l++)
{
if(labeImage[l*nImageWidth+k] == n+1)
{
labeImage[l*nImageWidth+k] = nRegionNumber;
areaCharacter[n*5]--;
if(l > maxrow1)
maxrow1 = l;
if(l < minrow1)
minrow1 = l;
}
}
}
areaCharacter[m+3] = maxrow1;//areaCharacter[n*5+3];
areaCharacter[m+4] = minrow1;//areaCharacter[n*5+4];
areaCharacter[m] -= areaCharacter[n*5];

//更新分区后当前标记区域的矩形区域
areaCharacter[n*5+1] = startcol;
areaCharacter[n*5+3] = premaxrow;
areaCharacter[n*5+4] = preminrow;
preminrow = nImageHeight;
premaxrow = 0;

}
else if(startcol < areaCharacter[n*5+2]+5)//直线段位于区域的左侧
{
areaCharacter[n*5+1] = endcol-npoints;
}
else if(startcol > areaCharacter[n*5+2]+15)//直线段位于区域右侧
{
areaCharacter[n*5+2] = startcol;
}

startcol = -1;
endcol = -1;
linepoints = 0;
maxrow = 0;
minrow = nImageHeight;
}
else
{
startcol = -1;
endcol = -1;
linepoints = 0;
maxrow = 0;
minrow = nImageHeight;
}
//直线段的最后一个列是该连通域的最后一列时
if(j == areaCharacter[n*5+3] && linepoints > 10 && minrow != nImageHeight)
{
//去掉直线段
for(int k = minrow; k < minrow+5; k++)
{
for(int l = startcol; l < endcol-npoints; l++)
{
if(labeImage[k*nImageWidth+l] == n+1)
{
labeImage[k*nImageWidth+l] = 0;
areaCharacter[n*5]--;
}
}
}
}
}
}
if(areaCharacter[n*5] < 100)
{//连通域更新
areaCharacter[n*5] = 0;
if(flag)
num1--;
}
}
printf("剩余的连通区域数目为:%d\n",num1);

//在水平方向上对连通域进行选择
for(i = 0; i < nRegionNumber; i++)
{
for(j = 0; j < nRegionNumber; j++)
{
int k = 5*i;
int l = 5*j;
if((i == j) || (0 == areaCharacter[k]) || (0 == areaCharacter[l]))
continue;
if(areaCharacter[k+1] < areaCharacter[l+2])//i连通域在j的左边
{
if(areaCharacter[k] > areaCharacter[l]*3 && areaCharacter[l+1] >= nImageWidth-1)
{//左区域时右区域的10倍,且右区域与边界相连,此时认为右边的物体只出来一部分
areaCharacter[l] = 0;
}
else//
{
areaCharacter[k] = 0;
}
}
else if(areaCharacter[k+1] < areaCharacter[l+1]
&& areaCharacter[k+2] < areaCharacter[l+2])//i连通域对j只是偏左
{
if(areaCharacter[k] > areaCharacter[l]*3 && areaCharacter[l+1] >= nImageWidth-1)
{//左区域时右区域的10倍,且右区域与边界相连,此时认为右边的物体只出来一部分
areaCharacter[l] = 0;
}
else//
{
areaCharacter[k] = 0;
}
}
else if(areaCharacter[k+1] < areaCharacter[l+1]
&& areaCharacter[k+2] > areaCharacter[l+2])//区域i在区域j内
{
areaCharacter[k] = 0;
}
}
}

arearect->right = 0;
arearect->left = 0;
arearect->bottom = 0;
arearect->top = 0;
for(i = 0; i < nRegionNumber; i++)
{
int k = i*5;
if(areaCharacter[k] != 0)
{
if(arearect->right < areaCharacter[k+1])
{//若剩余的连通域个数大于1,则选择右边的
arearect->right = areaCharacter[k+1];
arearect->left = areaCharacter[k+2];
arearect->bottom = areaCharacter[k+3];
arearect->top = areaCharacter[k+4];
}
}
}
cvSetZero(pImage);
for ( i=arearect->top; i<arearect->bottom; i++ )
{
for ( j=arearect->left; j<arearect->right; j++ )
{
*(pImage->imageData+i*pImage->widthStep+j) = (uchar)255;
}
}
cvNamedWindow("标记结果");
cvShowImage("标记结果", pImage);
cvWaitKey(0);

free(binaryImage);
free(labeImage);
free(areaCharacter);
cvDestroyAllWindows();
cvReleaseImage(&pImage);

}

void main()
{
RECT arearect;
arearect.right = 0;
arearect.left = 0;
arearect.bottom = 0;
arearect.top = 0;
chooselabelarea(&arearect);
system("pause");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: