解析bmp图像(某年全国软件大赛题目)

题目:


BMP是常见的图像存储格式。
如果用来存黑白图像(颜色深度=1),则其信息比较容易读取。

与之相关的数据:

(以下偏移均是从文件头开始)
偏移:10字节, 长度4字节: 图像数据真正开始的位置。
偏移:18字节, 长度4字节: 位图的宽度,单位是像素。
偏移:22字节, 长度4字节: 位图的高度,单位是像素。

从图像数据开始处,每个像素用1个二进制位表示。
从图片的底行开始,一行一行向上存储。

Windows规定图像文件中一个扫描行所占的字节数必须是4字节的倍数,
不足的位均以 0 填充。例如,图片宽度为45像素,实际上每行会占用
8个字节。

可以通过Windows自带的画图工具生成和编辑二进制图像。
需要在“属性”中选择“黑白”,指定为二值图像。
可能需要通过 查看 | 缩放 | 自定义... 把图像变大比例一些,
更易于操作。

图像的左下角为图像数据的开始位置。白色对应1,黑色对应0

我们可以定义:两个点距离如果小于2个像素,则认为这两个点连通。
也就是说:以一个点为中心的九宫格中,围绕它的8个点与它都是连通的。
如:t1.bmp 所示,左下角的点组成一个连通的群体;
而右上角的点都是孤立的。

程序的目标是:根据给定的黑白位图,分析出所有独立连通的群体,
输出每个连通群体的面积。所谓面积,就是它含有的像素的个数。

输入数据固定存在in.bmp中。

如示例的in.bmp,
程序应该输出:
12
81
52
133

该输出表示:共有4个连通群体。
输出的连通体面积间的顺序可以随意。

请编程解决上述问题。

我们测试程序的时候,会使用不同的in.bmp文件。


对于编程题目,要求选手给出的解答完全符合ANSI C标准,不能使用c++特性;
不能使用诸如绘图、中断调用等硬件相关或操作系统相关的API。

 

#include <vector>
#include<fstream>
#include<iostream>
using namespace std;
/*
算法描述,求连通分量的算法,每一个连通分量的节点连成一个链表,把链表存到vector容器中。。
先把每一个像素点储存到一个二维数组中,后把它宽和高都加一个以3为边框的数。。。
然后遍历处理后的数组,访问每一个数的九宫格,如果找到九宫格中还有黑点,则把它连接到对应的vector容器的链表中,
如果没有黑点,则重新创建一个链表,并储存到vector容器中。。。
最后vector容器中链表的个数既为联通分量的个数,每个联通分量的节点数即为此联通分量的像素数。。。


*/
typedef struct TNode   
{  
    int r;//节点的行
	int l;//节点的列
	struct TNode  *next;
}*NodeList;
//定义插入节点函数
bool insertNode(NodeList list,int R,int L){
	if (list==NULL)
		return false;
	NodeList Top=list;
	while(Top->next!=NULL){
		Top=Top->next;	
	}
	Top->next=new TNode;
	Top->next->l=L;
	Top->next->r=R;
	Top->next->next=NULL;
	return true;
}

//定义一个vector容器
typedef vector<TNode> MyVector;
//添加节点,如果m,n存在vector容器中,则吧R,L添加到其所在的链表中.否则重新创建一个链表添加到容器中
bool addBmpTree(MyVector &Vt,int m,int n,int R,int L){
	            TNode T;
				T.r=R;
				T.l=L;
				T.next=NULL;
	NodeList MyNext;
	for (int i=0;i<Vt.size();i++){
		MyNext=&Vt[i];
		do{			
			if (MyNext->r==m&&MyNext->l==n){//如果找到元素	
				//插入节点			
			   insertNode(MyNext,R,L);//插入节点
				return true;
			}
			if (MyNext->next!=NULL)
			MyNext=MyNext->next;//``````````````````````````
			else break;
		}while (true);
		
	} 
	Vt.push_back(T);

}
//查找节点所在的链表,返回错在链表的 vector容器下标号,如果找不到返回-1
int findList(MyVector &bmpVector,int R,int L){
	int n=bmpVector.size();
	TNode *Top;

	for (int i=0;i<bmpVector.size();i++){
		Top=&bmpVector[i];
		if(Top->l==L&&Top->r==R){
			return i;
			
		}
		else{
			while (Top->next!=NULL){	
			    Top=Top->next;
				if(Top->l==L&&Top->r==R){
			    	return i;
				}
			}
		}

	}//end for
  return -1;//如果找不到返回-1
}
//连接vector容器中的两个链表,吧对应下表为P,Q的链表合并
void ConList(MyVector &bmpVector,int P,int Q){
	
	TNode *Top;//指向第一个链表
	Top=&bmpVector[P];
	TNode *Next;//指向第二个链表

	Next=&bmpVector[Q];
    MyVector::iterator myIterator=&bmpVector[Q];
	
   
	if(Top!=NULL){    //遍历链表把节点指到链表尾部
		while (Top->next!=NULL){
			Top=Top->next;
		}
	}
		
	do {   //吧bmpVector[Q]的内容依次查到bmpVector【P】尾部
	Top->next=new TNode;
	Top->next->l=Next->l;
	Top->next->r=Next->r;
	Top->next->next=NULL;
	Top=Top->next;
	if (Next->next!=NULL)
	Next=Next->next;
	else 
		break;
	}while(true);

	myIterator=bmpVector.erase(myIterator);//删除链表
	
}

//读取Vector容器,显示联通分量的个数,和像素点
void readVector(MyVector &bmpVector){
		int n=bmpVector.size();
		cout <<"连通分量的个数为:"<<n<<endl;
		TNode *Top;
		int sum=0;

	for (int i=0;i<bmpVector.size();i++){
		Top=&bmpVector[i];
	//	cout<<Top->l<<" "<<Top->r<<endl;
		sum=1;
		while (Top->next!=NULL){	
		Top=Top->next;
		sum++;
	//	cout<<Top->l<<" "<<Top->r<<endl;
		}
		cout <<"像素个数为: "<<sum<<endl;
	}
}
//读取数组操作
void readArray(bool **map,int row,int col){
int testB[100][100];
MyVector bmpVector;//创建一个Vector容器;
//第一遍遍历为把数组用一层3包起来,方便下面查找
		for(int i=0;i<row+2;i++){
			for(int j=0;j<col+2;j++){
				if (i>=1&&i<row+1&&j>=1&&j<col+1){
					if (*((bool*)map + col*(i-1) + j-1)==1){
						testB[i][j]=0;
					}
					else
						testB[i][j]=1;				
				}else {
					testB[i][j]=3;
				}
		    	cout<<testB[i][j];
			}
			cout<<endl;
		}

//第二次遍历核心数组,构造联通分量
			for(int m=1;m<row+1;m++){
				for(int n=1;n<col+1;n++){
					if (testB[m][n]==1){//当前访问的数是黑点
				    

						if(testB[m][n]==testB[m-1][n-1]){//如果和左上边的相同
						addBmpTree(bmpVector,n-1,m-1,n,m);					
						 continue;
						}else if(testB[m][n]==testB[m-1][n]){//如果和上边的相同
						 addBmpTree(bmpVector,n,m-1,n,m);
						 continue;
						}else if(testB[m][n]==testB[m-1][n+1]){//如果和右上边的相同
						  addBmpTree(bmpVector,n+1,m-1,n,m);						
						 continue;
						}else if(testB[m][n]==testB[m][n-1]){//如果和左边的相同
						addBmpTree(bmpVector,n-1,m,n,m);
						 continue;
						}else{
						addBmpTree(bmpVector,-1,-1,n,m);//如果四周都不同
						}					
					}else {}   //如果是白点
					
				}
			
			}

			//连接联通量
			
			int index1;
			int index2;
			for(int x=1;x<row+1;x++){
				for(int y=1;y<col+1;y++){
					if (testB[x][y]==1){//当前访问的数是黑点
						
						if(testB[x][y+1]==1){//如果与后一个元素相同
						  index1=findList(bmpVector,y,x);
						  index2=findList(bmpVector,y+1,x);						  
						  
						  if (index1!=index2){//如果两个节点不在同一条链表中
							ConList(bmpVector,index1,index2);
						  }	
						  
						}
										
						if(testB[x+1][y-1]==1){//如果与左下方元素相同
						  index1=findList(bmpVector,y,x);
						  index2=findList(bmpVector,y-1,x+1);
						  
						  if (index1!=index2){//如果两个节点不在同一条链表中
							ConList(bmpVector,index1,index2);
						  }						 
						}
						
						if(testB[x][y]==testB[x+1][y]){//如果与下方元素相同
						  index1=findList(bmpVector,y,x);
						  index2=findList(bmpVector,y,x+1);
						  if (index1!=index2){//如果两个节点不在同一条链表中
							ConList(bmpVector,index1,index2);
						  }						 
						}
					
						if(testB[x][y]==testB[x+1][y+1]){//如果与右下方元素相同
						  index1=findList(bmpVector,y,x);
						  index2=findList(bmpVector,y+1,x+1);
						  if (index1!=index2&&index2!=-1){//如果两个节点不在同一条链表中
							ConList(bmpVector,index1,index2);
						  }						 
						}					
					}//==1的if结束
				}//end first for
			}

		readVector(bmpVector);//读取vector容器获得连通分量
}






int main()
{
	//定义bmp图片在内存中的起始位置及宽、高数值
	int static width;
	int static height;
	int start;

	int lewidth;
	int leheight;
	int width_need;
	int height_need;
//如果测试t1.bmp把char bmp[38][64];bool bin[38][64];换成char bmp[25][32];bool bin[25][32];
	fstream ioFile ("in.bmp",ios::in|ios::out|ios::binary);

	//bmp用来储存读出的图
	//    bin用来储存变换后字节
         char bmp[38][64];
  	     bool bin[38][64];

	// char bmp[25][32];
    // bool bin[25][32];



	if(!ioFile)
	{
	  cerr<<"open error!"<<endl;
	  exit(1);
	}

	ioFile.seekg(10,ios::beg);
	ioFile.read((char*)&start,sizeof(start));//读取开始存储图片信息的地方
	
	ioFile.seekg(18,ios::beg);
	ioFile.read((char*)&width,sizeof(width));//储存图片的宽度
	ioFile.seekg(22,ios::beg);
	ioFile.read((char*)&height,sizeof(height));//储存图片的长度	
	

	cout<<"宽度:"<<width<<"长度: "<<height<<endl;
	if(width%8 == 0)
	    lewidth = width/8;
	else
		lewidth = (width/8)+1;

	//将图片元素存储到一个字符数组中,按行字节存储
	ioFile.seekg(start,ios::beg);
	for(int h=0;h<height;h++)
		for(int w=0;w<lewidth;w++)
			ioFile.read((char *)&bmp[height-h-1][w],sizeof(bmp[height-h-1][w]));

	char b_and[]={1,2,4,8,16,32,64,128};
	
	//转换图像信息,转化为字节
	for(int i=0;i<height;i++){
		for(int j=0;j<width;j++){
			if((bmp[i][j/8]&b_and[7-(j%8)]))  
					bin[i][j]=1;
				else 
					bin[i][j]=0;
			
		}

	}

	ioFile.close();
	//调用求联通分量的函数
readArray((bool**)bin,height,width);

/*
for(int mm=0;mm<width;mm++)
   delete[] bin[mm];
delete[] bin;

for(int dd=0;dd<width;dd++)
   delete[] bmp[dd];
delete[] bmp;
*/


return 0;
}


 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
连通问题 解题报告 ——2011软件大赛决赛 C组 本科第5题 原创By BossDong BMP是常见的图像存储格式。 如果用来存黑白图像(颜色深度=1),则其信息比较容易读取。 与之相关的数据: (以下偏移均是从文件头开始) 偏移:10字节, 长度4字节: 图像数据真正开始的位置。 偏移:18字节, 长度4字节: 位图的宽度,单位是像素。 偏移:22字节, 长度4字节: 位图的高度,单位是像素。 从图像数据开始处,每个像素用1个二进制位表示。 从图片的底行开始,一行一行向上存储。 Windows规定图像文件中一个扫描行所占的字节数必须是4字节的倍数, 不足的位均以 0 填充。例如,图片宽度为45像素,实际上每行会占用 8个字节。 可以通过Windows自带的画图工具生成和编辑二进制图像。 需要在“属性”中选择“黑白”,指定为二值图像。 可能需要通过 查看 | 缩放 | 自定义... 把图像变大比例一些, 更易于操作。 图像的左下角为图像数据的开始位置。白色对应1,黑色对应0 我们可以定义:两个点距离如果小于2个像素,则认为这两个点连通。 也就是说:以一个点为中心的九宫格中,围绕它的8个点与它都是连通的。 如:t1.bmp 所示,左下角的点组成一个连通的群体; 而右上角的点都是孤立的。 程序的目标是:根据给定的黑白位图,分析出所有独立连通的群体, 输出每个连通群体的面积。所谓面积,就是它含有的像素的个数。 输入数据固定存在in.bmp中。 如示例的in.bmp, 程序应该输出: 12 81 52 133 该输出表示:共有4个连通群体。 输出的连通体面积间的顺序可以随意。 请编程解决上述问题。 我们测试程序的时候,会使用不同的in.bmp文件。 要求考生把所有函数写在一个文件中。调试好后,存入与考生文件夹下对应题号的“解答.txt”中即可。 相关的工程文件不要拷入。 对于编程题目,要求选手给出的解答完全符合ANSI C标准,不能使用c++特性; 不能使用诸如绘图、中断调用等硬件相关或操作系统相关的API。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值