- 问题描述:
编写一个程序,可以在命令行输入参数,完成指定文件的缩放,并存储到新文件,命令行参数如下
zoom file1.bmp 200 file2.bmp
第一个参数为可执行程序名称,第二个参数为原始图像文件名,第三个参数为缩放比例(百分比),第四个参数为新文件名
- 问题分析:
- 综述:
要实现对bmp图像操作,首先要知道bmp图像的存储机理(图像信息如何以二进制信息存储)及文件包含的内容;其次知晓基本的图像缩放算法即可;最后要知晓如何读取和存储文件信息。
- 实现时的困难:
- 定义何种类型变量?
- 文件大小如何读取、计算?
- 像素信息如何读取、写入?
- 文件大小如何读取、计算?
- 定义何种类型变量?
- 对应解决方案
- 首先创建全局变量,如src_:原图宽高;dst_:缩放后宽高;各函数都可对其进行访问;其次用一个字符数组typedef unsigned char pixel[3];存储一个像素的RGB信息;对于缩放比例用double height_zoom_rate,width_zoom_rate;操作更精准,计算缩放后像素用类型转换即可,如:dst_width=(int)(width_zoom_rate*src_width); 而在访问文件文件头和信息头时,定义unsigned char *buf=NULL,*bui=NULL;方便逐个字节访问,也可以处理小端对齐。
- 即如何根据字节小端对齐计算文件大小;前面已经用buf、bui逐个访问文件头,信息头的每个字节数据,故如:b_size=bui[23]161616161616+bui[22]16161616+bui[21]1616+bui[20];
- 首先创建全局变量,如src_:原图宽高;dst_:缩放后宽高;各函数都可对其进行访问;其次用一个字符数组typedef unsigned char pixel[3];存储一个像素的RGB信息;对于缩放比例用double height_zoom_rate,width_zoom_rate;操作更精准,计算缩放后像素用类型转换即可,如:dst_width=(int)(width_zoom_rate*src_width); 而在访问文件文件头和信息头时,定义unsigned char *buf=NULL,*bui=NULL;方便逐个字节访问,也可以处理小端对齐。
可以读取图像大小;而:
f_size=dst_width*dst_height+54;则可计算操作后的文件大小
1. 每个像素的RGB信息各占一个字节,用fgetc即可读取每个字节。读取完一行的像素后,通过计算填充字节个数(pad_num=(4-(src_width*3)%4)%4;),再用fgetc读掉填充字节,即可读取下一行。写入时采用三个字节为一组(即一个像素的RGB信息),结合最近邻插入法用fwrite写入fwrite(&info[si*src_width+sj],sizeof(pixel),1,fp_n);
再用fputc(0,fp_n)写入填充字节数。
#define _CRT_SECURE_NO_WARNINGS
#include”bmp.h”
extern double height_zoom_rate,width_zoom_rate;
int main(int argc, char* argv[]) {
//打开文件
FILE*fp=fopen(argv[1],”rb”),*fp_n = fopen(argv[3], “wb”);
//读取文件头、信息头
read_bmp_head(fp);
//读取像素信息
read_bmp_data(fp);
//计算长宽缩放比例
width_zoom_rate=height_zoom_rate=(atoi(argv[2]) /100.0);
//结合最近邻插入法写入缩放后的图像信息
write_data(fp_n);
//展示操作好的图像信息
read_bmp_head(fp_n);
//关闭两个图像文件
fclose(fp);
fclose(fp_n);
return 0;
}
/头文件使用说明:
在主程序中:extern double height_zoom_rate,width_zoom_rate;并赋值方可正常运行
包含三个函数:
void read_bmp_head(FILEfp);
void read_bmp_data(FILEfp);
void write_data(FILE fp_n);
*/
#include<stdio.h>
#include<stdlib.h>
//改对取模数为1个字节(默认为8,只可是2^n)
#pragma pack(1)
//存储一个像素信息的三元素字符数组
typedef unsigned char pixel[3];
//文件头信息
typedef struct tagBITMAPFILEHEADER
{
unsigned short bfType; //保存图片类型。 ‘BM’(1-2字节)
unsigned long bfSize; //位图文件的大小,以字节为单位(3-6字节,低位在前)
unsigned short bfReserved1;//位图文件保留字,必须为0(7-8字节)
unsigned short bfReserved2;//位图文件保留字,必须为0(9-10字节)
unsigned long bfOffBits; //RGB数据偏移地址,位图数据的起始位置,以相对于位图(11-14字节,低位在前)
}BITMAP_FILE_HEADER;
//信息头信息
typedef struct tagBITMAPINFOHEADER
{
unsigned long biSize; //本结构所占用字节数(15-18字节)
unsigned long biWidth; //位图的宽度,以像素为单位(19-22字节)
unsigned long biHeight; //位图的高度,以像素为单位(23-26字节)
unsigned short biPlanes; //目标设备的级别,必须为1(27-28字节)
unsigned short biBitCount; //每个像素所需的位数,必须是1(双色)(29-30字节),4(16色),8(256色)16(高彩色)或24(真彩色)之一
unsigned long biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
unsigned long biSizeImage; //位图的大小(其中包含了为了补齐每行字节数是4的倍数而添加的空字节),以字节为单位(35-38字节)
unsigned long biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
unsigned long biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
unsigned long biClrUsed; //位图实际使用的颜色表中的颜色数(47-50字节)
unsigned long biClrImportant; //位图显示过程中重要的颜色数(51-54字节)
}BITMAP_INFO_HEADER;
//全局变量说明:src_:原图宽高;dst_:缩放后宽高;pad_num:每行补齐空字节数;
//f_size: bfSize; b_size: biSizeImage; _zoom_rate: 宽高缩放倍数;sign:是否没计算过宽高标志
int sign1=1;
int src_width,src_height,dst_width,dst_height;
int pad_num,f_size,b_size;
double height_zoom_rate,width_zoom_rate;
unsigned char *buf=NULL,*bui=NULL;
//创建两个结构体存储文件头和信息头信息
BITMAP_FILE_HEADER file_head;
BITMAP_INFO_HEADER info_head;
//info指向像素信息的头地址,以info[i](=*(info+i))访问第i个像素
pixel *info=NULL;
//读取文件头和信息头的信息
void read_bmp_head(FILE*fp){
if ( fp == NULL ) {
printf("file doesn't exist!\n");
exit(0);
}
rewind(fp);
//读取信息
fread(&file_head, sizeof(BITMAP_FILE_HEADER), 1, fp);
fread(&info_head, sizeof(BITMAP_INFO_HEADER), 1, fp);
//因为信息是小端对齐,以字节为单位方便读取
buf=(unsigned char*)&file_head;
printf("\nbfType:\t\t%c %c",buf[1],buf[0]);
f_size=buf[5]*16*16*16*16*16*16+buf[4]*16*16*16*16+buf[3]*16*16+buf[2];
printf("\nbfSize:\t\t%02x %02x %02x %02x ---- %d",buf[5],buf[4],buf[3],buf[2],f_size);
printf("\nbfReserved1:\t%02x %02x\nbfReserved2:\t%02x %02x",buf[7],buf[6],buf[9],buf[8]);
printf("\nbf0ffbits:\t%02x %02x %02x %02x",buf[13],buf[12],buf[11],buf[10]);
printf("\n\n");
bui=(unsigned char*)&info_head;
printf("\nbiSize:\t\t%02x %02x %02x %02x",bui[3],bui[2],bui[1],bui[0]);
src_width=bui[7]*16*16*16*16*16*16+bui[6]*16*16*16*16+bui[5]*16*16+bui[4];
src_height=bui[11]*16*16*16*16*16*16+bui[10]*16*16*16*16+bui[9]*16*16+bui[8];
printf("\nbiWidth:\t%02x %02x %02x %02x ---- %d",bui[7],bui[6],bui[5],bui[4],src_width);
printf("\nbiHeight:\t%02x %02x %02x %02x ---- %d",bui[11],bui[10],bui[9],bui[8],src_height);
printf("\nbiPlans:\t%02x %02x",bui[13],bui[12]);
printf("\nbiBitCount:\t%02x %02x",bui[15],bui[14]);
printf("\nbiCompression:\t%02x %02x %02x %02x",bui[19],bui[18],bui[17],bui[16]);
b_size=bui[23]*16*16*16*16*16*16+bui[22]*16*16*16*16+bui[21]*16*16+bui[20];
printf("\nbiSizeImage:\t%02x %02x %02x %02x ---- %d",bui[23],bui[22],bui[21],bui[20],b_size);
printf("\nbiXPelsPerMeter:%02x %02x %02x %02x",bui[27],bui[26],bui[25],bui[24]);
printf("\nbiYPelsPerMeter:%02x %02x %02x %02x",bui[31],bui[30],bui[29],bui[28]);
printf("\nbiClrUsed:\t%02x %02x %02x %02x",bui[35],bui[34],bui[33],bui[32]);
printf("\nbiClrImportant:\t%02x %02x %02x %02x\n",bui[39],bui[38],bui[37],bui[36]);
sign1=0;
}
//将照片像素信息读取,通过全局变量info访问
void read_bmp_data(FILE*fp){
void read_bmp_head(FILE*fp);
if ( fp == NULL ) {
printf("file doesn't exist!\n");
exit(0);
}
//判断是否计算好了图像的长宽,没有则执行
if(sign1){
rewind(fp);
read_bmp_head(fp);
}
int i,j,k;
//填充字节数
pad_num=(4-(src_width*3)%4)%4;
//开辟空间
info=(pixel*)malloc(src_width*src_height*sizeof(pixel));
//读取像素信息RGB
for(i=0;i<src_height;i++){
for(j=0;j<src_width;j++){
info[i*src_width+j][0]=fgetc(fp);
info[i*src_width+j][1]=fgetc(fp);
info[i*src_width+j][2]=fgetc(fp);
}
//读掉填充字节
for(k=0;k<pad_num;k++)fgetc(fp);
}
}
//将信息写入新文件
void write_data(FILE* fp_n) {
rewind(fp_n);
dst_width=(int)(width_zoom_rate*src_width);
dst_height=(int)(height_zoom_rate*src_height);
//文件头信息修改、拷贝(小对齐,故用&、>>位操作)
int f_size=dst_width*dst_height+54,i,j,k;
buf[2]=0x000000FF&f_size;
buf[3]=(0x0000FF00&f_size)>>8;
buf[4]=(0x00FF0000&f_size)>>16;
buf[5]=(0xFF000000&f_size)>>24;
for(i=0;i<14;i++)fputc(buf[i],fp_n);
//信息头信息修改、拷贝
bui[4]=0x000000FF&dst_width;
bui[5]=(0x0000FF00&dst_width)>>8;
bui[6]=(0x00FF0000&dst_width)>>16;
bui[7]=(0xFF000000&dst_width)>>24;
bui[8]=0x000000FF&dst_height;
bui[9]=(0x0000FF00&dst_height)>>8;
bui[10]=(0x00FF0000&dst_height)>>16;
bui[11]=(0xFF000000&dst_height)>>24;
pad_num= (4-(dst_width*3)%4)%4;
b_size=dst_height*(dst_width+pad_num)*3;
bui[20]=0x000000FF&b_size;
bui[21]=(0x0000FF00&b_size)>>8;
bui[22]=(0x00FF0000&b_size)>>16;
bui[23]=(0xFF000000&b_size)>>24;
for(i=0;i<40;i++)fputc(bui[i],fp_n);
int si,sj;
//像素信息写入
for(i=0;i<dst_height;i++){
si=i/height_zoom_rate;
for(j=0;j<dst_width;j++){
//最近邻插入法
sj=j/width_zoom_rate;
fwrite(&info[si*src_width+sj],sizeof(pixel),1,fp_n);
}
for(k=0;k<pad_num;k++)fputc(0,fp_n);
}
/*
//不行的写法!低效且数值越界
for(i=0;i<dst_height;i++){
for(j=0;j<dst_width;j++){
//最近邻插入法
fwrite(&info[(int)(i*src_width/height_zoom_rate+j/width_zoom_rate)],sizeof(pixel),1,fp_n);
}
for(k=0;k<pad_num;k++)fputc(0,fp_n);
}
*/
}
本文转自 https://blog.csdn.net/qq_32971095/article/details/124532108,如有侵权,请联系删除。