将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;
}
测试结果
测试结果二
原图(太大,看信息图,特意修改了宽度,字节补齐数为2 )
缩小图