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

#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");
}


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值