题目:
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; }