工信部软件大赛决赛题----统计连通图的个数和面积


题目要求:

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<iostream>
#include<fstream>
using namespace std;
//检测得到每个连通图的面积,通过递归检测相邻的八个位置是否是0。(设为2表示已检测过,这样可避免重复)
int perArea(int x,int y,int height,int width,int array[][100]){
   int area=1;
   //上
   if(x>0&&array[x-1][y]==0){
      array[x-1][y]=2;
      area=area+perArea(x-1,y,height,width,array);
   }else
        array[x-1][y]=2;
   //下
   if(x<height&&array[x+1][y]==0){
      array[x+1][y]=2;
      area=area+perArea(x+1,y,height,width,array);
   }else
         array[x+1][y]=2;
   //左
   if(y>0&&array[x][y-1]==0){
        array[x][y-1]=2;
       area=area+perArea(x,y-1,height,width,array);
   }else
       array[x][y-1]=2;
   //右
   if(y<width&&array[x][y+1]==0){
    array[x][y+1]=2;
       area=area+perArea(x,y+1,height,width,array);
   }else
        array[x][y+1]=2;
   //左上
if(y<width&&array[x-1][y-1]==0){
    array[x-1][y-1]=2;
       area=area+perArea(x-1,y-1,height,width,array);
   }else
        array[x-1][y-1]=2;

   //右上
   if(y<width&&array[x-1][y+1]==0){
    array[x-1][y+1]=2;
       area=area+perArea(x-1,y+1,height,width,array);
   }else
        array[x-1][y+1]=2;
   //左下
   if(y<width&&array[x+1][y-1]==0){
    array[x+1][y-1]=2;
       area=area+perArea(x+1,y-1,height,width,array);
   }else
        array[x+1][y-1]=2;
   //右下
 if(y<width&&array[x+1][y+1]==0){
    array[x+1][y+1]=2;
       area=area+perArea(x+1,y+1,height,width,array);
   }else
        array[x+1][y+1]=2;
 return area;
}
//遍历数组得到连通图的个数及每个连通图的面积
void compute(int array[][100],int height,int width)
{
    for(int i=0;i<height;i++)
    {
        for(int j=0;j<width;j++)
        {
         if(array[i][j]==0)
         {
           int area=perArea(i,j,height,width,array);
               area=(area>1)?area-1:area;
               cout<<"连通图面积为:"<<area<<endl;
         }
         else
             array[i][j]=2;
        }
    }
}
//读取图片并对图片进行处理
void readPic(char filename[100])
{
    int start,height,width;//定义图片的开始,高,宽;
    int use_and[]={128,64,32,16,8,4,2,1};//用于与运算
    ifstream infile(filename,ios::binary);
    if(!infile)
    {
     cout<<"Open error!"<<endl;
     abort();
    }
    infile.seekg(10,ios::beg);
    infile.read((char*)&start,4);
    cout<<"开始位置:"<<start<<endl;
    infile.seekg(18);
    infile.read((char*)&width,4);
    cout<<"宽:"<<width<<endl;
    infile.seekg(22);
    infile.read((char*)&height,4);
    cout<<"实际高:"<<height<<endl;
    if(width%8!=0)
        width=width+(8-width%8);
    cout<<"理论高:"<<width;
//读取in.bmp文件,并把数据存入pic数组中
    char pic[100][100];
    infile.seekg(start);
     for(int i=0;i<height;i++)
         for(int j=0;j<(width/8);j++)
         {
             infile.read((char *)&pic[height-i-1][j],1);           
         }
         cout<<endl;

//关闭文件流
infile.close();

//把图像转化为二进制放入数组binary中
         int binary[100][100];
cout<<"bmp图片的二进制形式:"<<endl;
         for(int m=0;m<height;m++){
             for(int n=0;n<width;n++){
                   if(pic[m][n/8]&use_and[n%8])
                    binary[m][n]=1;
                  else
                      binary[m][n]=0;
                 cout<<binary[m][n];
               }
            cout<<endl;
         }
//调用函数得到连通图的个数和每个连通图的面积
compute(binary,height,width);    

}
int main()
{
    char str[100];
    cout<<"请输入文件的路径及名称:";
    cin>>str;
    readPic(str);
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值