将BMP格式图片缩小生成另一个图片文件

设计算法,要求把一张任意尺寸的 BMP 图片等比例且不失真的缩小为原来的 1/2,并生成一张新的 BMP 图片,要求 BMP 图片的路径都需要通过命令行进行传递

设计思路:

1.定义一个空间 buf存放从原图片读取的颜色分量(注意考虑BMP文件的字节补齐数量);

2.定义一个空间 half_buf存放缩小到1/2的颜色分量。我采用的是每行间隔一个颜色分量取数据;

3.将half_buf空间中的颜色分量写入颜色分量数据,同样要考虑字节补齐。

实现代码

/**
 * file name:BMP_reduction.c
 * author   : liaojx2016@126.com
 * date     : 2024-05-12
 * function : Reduce the BMP image by half to generate a new image file
 * note     : None
 * CopyRight (c)   2024  liaojx2016@126.com  Right Reseverd
 *
**/
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
//取消字节对齐,因为需要的结构体是 14字节和 40字节大小
#pragma pack(1)
//定义图像文件头结构体
typedef struct BMP_FileHeader
{
    char                bfType[2];        //位图文件的类型,必须为BM 
    int                 bfSize;           //文件大小,以字节为单位
    unsigned short int  bfReserverd1;     //位图文件保留字,必须为0 
    unsigned short int  bfReserverd2;     //位图文件保留字,必须为0 
    unsigned int        bfbfOffBits;      //位图文件头到数据的偏移量,以字节为单位
}BMP_FileHeader_t;
//定义图像信息头结构体
typedef  struct BMP_InfoHeader
{ 
    unsigned int biSize;                       //该结构大小,字节为单位
     int  biWidth;                            //图形宽度以象素为单位
     int  biHeight;                            //图形高度以象素为单位
    unsigned short int  biPlanes;              //目标设备的级别,必须为1 
    unsigned short int  biBitcount;            //颜色深度,每个象素所需要的位数
    unsigned short int  biCompression;         //位图的压缩类型
    unsigned int  biSizeImage;                 //位图的大小,以字节为单位
    unsigned int  biXPelsPermeter;             //位图水平分辨率,每米像素数
    unsigned int  biYPelsPermeter;             //位图垂直分辨率,每米像素数
    unsigned int  biClrUsed;                   //位图实际使用的颜色表中的颜色数
    unsigned int  biClrImportant;              //位图显示过程中重要的颜色数
}BMP_InfoHeader_t;

#pragma pack()      //开启字节对齐

int main(int argc, char const *argv[])
{
    //判断通过命令行传入图片文件是否有效
    if (2 != argc)
    {
        printf("import argument is invalid\n");
        return -1;
    }
    //打开图片文件
    FILE *bmp_fp = fopen(argv[1],"rb");

    if (NULL == bmp_fp)
    {
        printf("fopen picture file error\n");
        return -1;
    }
    //判断是否打开成功
     //定义图像文件头和信息头的结构体变量
     BMP_FileHeader_t Fhead1,Fhead2;
     BMP_InfoHeader_t info_head1,info_head2;

     //读取图像文件头信息头的数据到结构体变量里
     fread(&Fhead1,1,14,bmp_fp);
     fread(&info_head1,1,40,bmp_fp);
     //输出原图像信息
     printf("the BMP picture size = %d;width is %d;height is %d\n",
                Fhead1.bfSize,
                info_head1.biWidth,
                info_head1.biHeight);
    //定义变量记录原图中的宽,高,字节补齐数量
    int prev_wight = info_head1.biWidth;
    int prev_height = info_head1.biHeight;
    int prev_add_byte = (4-(prev_wight*3)%4)%4;
    printf("pre_bmp add bytes = %d\n",prev_add_byte);
    //申请堆空间,存放原图颜色分量
    char *bmpbuf=(char*)calloc(prev_wight*prev_height*3,1);
    //读取原图文件的颜色分量到空间bmpbuf中
    for (int i = 0; i < prev_height; i++)
    {
        fread(bmpbuf+3*prev_wight*i,1,prev_wight*3,bmp_fp); //读取一行的颜色分量
        fseek(bmp_fp,prev_add_byte,SEEK_CUR);   //图片文件的光标偏移字节补齐的数量
    }
    //关闭源图像文件
    fclose(bmp_fp);
    //将原图片文件头和信息头复制到缩小图片的结构体中
     memcpy(&Fhead2,&Fhead1,14);
     memcpy(&info_head2,&info_head1,40); 

    //设置缩小图片的宽,高,字节补齐数量
    int wight = info_head2.biWidth/2;
    int height = info_head2.biHeight/2;
    int add_byte = (4-(wight*3)%4)%4;
    printf("bmp add bytes = %d\n",add_byte);

    //修改缩小图片的文件头和信息头数据
     Fhead2.bfSize = (wight*3+add_byte)*height + 54;
     info_head2.biWidth = wight;
     info_head2.biHeight = height;
     //输出缩小图片的大小,宽和高
     printf("the BMP picture size = %d;width is %d;height is %d\n",
                Fhead2.bfSize,
                info_head2.biWidth,
                info_head2.biHeight);
    
    //申请堆空间,存放缩小图片的颜色分量
    char *half_bmpbuf=(char*)calloc(wight*height*3,1);
    int offset;
    //从 bmpbuf空间中读取原图文件的颜色分量到空间half_bmpbuf
    for (int i = 0; i < height; i++)
    {
        for (int j = 0,k=0; j < wight*3; j++,k++)
        {
            half_bmpbuf[i*wight*3+j] = bmpbuf[2*i*prev_wight*3+k];  //读取一个字节数据
            if (k%3 ==2 )
            {
                //此时已经读取完一个颜色分量,bmpbuf的地址加3, 间隔掉一个颜色分量,做缩小的动作
                k+=3;
            }
        }
    }
    //打开存放缩小图片的文件,若没有,则创建
    FILE *half_bmp_fp = fopen("0half.bmp","wb");
    //将所有图片信息复制到缩小的图片文件
    fwrite(&Fhead2,1,14,half_bmp_fp);       //写入文件头数据
    fwrite(&info_head2,1,40,half_bmp_fp);   //写入信息头数据
    //写入颜色分量数据
    for (int i = 0; i < height; i++)
    {
        //从halfbuf空间总读取一行颜色分量数据到缩小图片文件
        fwrite(half_bmpbuf+wight*3*i,1,wight*3,half_bmp_fp);    
         //考虑BMP图像文件字节补齐
	 offset = 0;
         while (offset++ < add_byte)
         {
            fputc(0,half_bmp_fp);
         }
    }

    //释放堆内存
    free(bmpbuf);
    free(half_bmpbuf);
    //关闭缩小图像文件
    fclose(half_bmp_fp);
    return 0;
}



测试结果

image

image

测试结果二
原图(太大,看信息图,特意修改了宽度,字节补齐数为2 )
image
缩小图
image
image

posted @ 2024-05-13 00:18  沉舟道人  阅读(32)  评论(0编辑  收藏  举报