使用双线性插值法
设计思路
- 使用结构体创建位图文件的头文件和信息头;
- 从命令行获取文件名,创建文件流;
- 按照放缩比例修改位图文件头和位图信息头中相关的数据(分辨率等);
- 创建指针存储位图数据,就是像素颜色数据;
- 双线性插值法处理放缩后的图像颜色;
- 将得到的新数据写入目标位图文件.
具体实现
全局声明
使用伪指令让结构体以一个字节对齐,能正确获取结构体占有的内存;
使用位图中的名词声明类型;
#pragma pack(1)
typedef unsigned char BYTE;
typedef unsigned short int WORD;
typedef unsigned int DWORD;
typedef int LONG;
- 位图文件头的定义:
//14字节的文件头
typedef struct tagBITMAPFILEHEADER{
WORD bfType; //2字节,文件类型
DWORD bfSize; //4字节,文件大小
WORD bfReserved1,bfReserved2; //2字节,保留,必须设置为0
DWORD bfOffBits; //4字节,从头到位图数据的偏移
}BITMAPFILEHEADER;
位图信息头的定义:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //4字节,信息头的大小
LONG biWidth,biHeight; //biWidth:4字节,以像素为单位说明图像的宽度;
//biHeight:4字节,以像素为单位说明图像的高度,同时如果为正,说明位图倒立(即数据表示从图像的左下角到右上角),如果为负说明正向;
WORD biPlanes; //2字节,为目标设备说明颜色平面数,总被设置为1
WORD biBitCount; //2字节,说明比特数/像素数,值有1、2、4、8、16、24、32
DWORD biCompression; //4字节,说明图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩
DWORD biSizeImages; //4字节,说明位图数据的大小,当用BI_RGB格式时,可以设置为0
LONG biXPelsPerMeter; //表示水平分辨率,单位是像素/米,有符号整数
LONG biYPelsPerMeter; //表示垂直分辨率,单位是像素/米,有符号整数
DWORD biClrUsed; //说明位图使用的调色板中的颜色索引数,为0说明使用所有
DWORD biClrImportant; //明对图像显示有重要影响的颜色索引数,为0说明都重要
}BITMAPINFOHEADER;
- 命令行中的第二、第四个参数是原文件名和目标文件名,第三个参数是放缩比例;
char *filename,*result;
filename=argv[1]; //原文件名
result=argv[3]; //目标文件名
FILE *fp;
fp=fopen(filename,"rb");
FILE *rp;
rp=fopen(result,"wb");
char *op;
op=argv[2]; //放缩百分比
double a=(double)atoi(op)/100; //百分比化成小数
- 位图文件头和位图信息头中的数据修改;
BITMAPFILEHEADER src_fh,src_re_fh; //用于储存文件头
BITMAPINFOHEADER src_ih,src_re_ih; //用于储存信息头
fread(&src_fh,sizeof(BITMAPFILEHEADER),1,fp);
fread(&src_ih,sizeof(BITMAPINFOHEADER),1,fp);
unsigned int filename_size;
filename_size=src_ih.biHeight*src_ih.biWidth; //得到原图的像素大小
//文件头和信息头基本类似,先copy
src_re_fh=src_fh;
src_re_ih=src_ih;
src_re_ih.biHeight=(LONG)(src_ih.biHeight*a); //目标位图的高
src_re_ih.biWidth=(LONG)(src_ih.biWidth*a); //目标位图的宽
unsigned int result_size=src_re_ih.biWidth*src_re_ih.biHeight; //目标图的像素大小
src_re_fh.bfSize=54+result_size*sizeof(int);
fwrite(&src_re_fh,sizeof(BITMAPFILEHEADER),1,rp); //修改好的文件头写入到目标文件
fwrite(&src_re_ih,sizeof(BITMAPINFOHEADER),1,rp); //修改好的信息头写入到目标文件
- 创建数据指针;
unsigned char *fp_pixels,*re_pixels; //用于储存和操作位图数据的数组
fp_pixels=(unsigned char*)malloc(sizeof(int)*filename_size); //分配空间
fread(fp_pixels,sizeof(int),filename_size,fp);
re_pixels=(unsigned char*)malloc(sizeof(int)*result_size); //分配空间
- 双线性差值法的实现;
双线性差值法的原理是通过目标点附近的四个点的权重来分配目标点的值,公式如下:
$$
f\left( x,y \right) \approx \left( 1-u \right) \left( 1-v \right) f\left( x_1,y_1 \right) +u\left( 1-v \right) f\left( x_1+1,y_1 \right) +\left( 1-u \right) vf\left( x_1,y_1+1 \right) +uvf\left( x_1+1,y_1+1 \right)
$$
目标点为,对应到原图中的点为,.及其周围的其他三个点,由于放缩后,可能是小数,因此,令,
需要注意坐标和具体像素点的R,G,B三种颜色坐标上的对应关系,24位bmp中一个像素只有3种颜色的数据(3个字节)
代码实现:
for (int x=0;x<src_re_ih.biWidth;x++)
{
for (int y=0;y<src_re_ih.biHeight;y++)
{
double xx,yy;
xx=(double)x/a;
yy=(double)y/a;
double u,v;
u=xx-(int)xx;
v=yy-(int)yy;
//确定是哪几个像素需要被使用
int result_order=x+y*src_re_ih.biWidth;
int filename_order1=(int)xx+(int)yy*src_ih.biWidth;
int filename_order2=((int)(xx)+1)+(int)yy*src_ih.biWidth;
int filename_order3=(int)xx+((int)yy+1)*src_ih.biWidth;
int filename_order4=((int)(xx)+1)+((int)yy+1)*src_ih.biWidth;
//注意讨论边界情况
if ((int)(xx)+1>=src_ih.biWidth)
{
filename_order2=filename_order1;
filename_order4=filename_order3;
}
if ((int)(yy)+1>=src_ih.biHeight)
{
filename_order3=filename_order1;
filename_order4=filename_order2;
}
//对位图数据进行操作
for (int i=0;i<3;i++)
{
re_pixels[3*result_order+i] = (1 - u) * (1 - v) * fp_pixels[3*filename_order1+i] +
u * (1 - v) * fp_pixels[3*filename_order2+i]
+ (1 - u) * v * fp_pixels[3*filename_order3+i] +
u * v * fp_pixels[3*filename_order4+i];
}
}
}
- 得到的数据写入目标位图文件
fwrite(re_pixels,sizeof(int),result_size,rp); //写入位图数据到文件
完整代码
//
// Created by Ablazy on 2022/5/18.
//
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
//伪指令,以1字节对齐,防止自动对齐
#pragma pack(1)
typedef unsigned char BYTE;
typedef unsigned short int WORD;
typedef unsigned int DWORD;
typedef int LONG;
//14字节的文件头
typedef struct tagBITMAPFILEHEADER{
WORD bfType; //2字节,文件类型
DWORD bfSize; //4字节,文件大小
WORD bfReserved1,bfReserved2; //2字节,保留,必须设置为0
DWORD bfOffBits; //4字节,从头到位图数据的偏移
}BITMAPFILEHEADER;
//40位的信息头
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //4字节,信息头的大小
LONG biWidth,biHeight; //biWidth:4字节,以像素为单位说明图像的宽度;
//biHeight:4字节,以像素为单位说明图像的高度,同时如果为正,说明位图倒立(即数据表示从图像的左下角到右上角),如果为负说明正向;
WORD biPlanes; //2字节,为目标设备说明颜色平面数,总被设置为1
WORD biBitCount; //2字节,说明比特数/像素数,值有1、2、4、8、16、24、32
DWORD biCompression; //4字节,说明图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩
DWORD biSizeImages; //4字节,说明位图数据的大小,当用BI_RGB格式时,可以设置为0
LONG biXPelsPerMeter; //表示水平分辨率,单位是像素/米,有符号整数
LONG biYPelsPerMeter; //表示垂直分辨率,单位是像素/米,有符号整数
DWORD biClrUsed; //说明位图使用的调色板中的颜色索引数,为0说明使用所有
DWORD biClrImportant; //明对图像显示有重要影响的颜色索引数,为0说明都重要
}BITMAPINFOHEADER;
int main(int argc,char * argv[])
{
char *filename,*result;
filename=argv[1];
result=argv[3];
FILE *fp;
fp=fopen(filename,"rb");
FILE *rp;
rp=fopen(result,"wb");
char *op;
op=argv[2];
double a=(double)atoi(op)/100;
BITMAPFILEHEADER src_fh,src_re_fh; //用于储存文件头
BITMAPINFOHEADER src_ih,src_re_ih; //用于储存信息头
fread(&src_fh,sizeof(BITMAPFILEHEADER),1,fp);
fread(&src_ih,sizeof(BITMAPINFOHEADER),1,fp);
unsigned int filename_size;
filename_size=src_ih.biHeight*src_ih.biWidth; //得到原图的像素大小
//文件头和信息头基本类似,先copy
src_re_fh=src_fh;
src_re_ih=src_ih;
src_re_ih.biHeight=(LONG)(src_ih.biHeight*a); //目标位图的高
src_re_ih.biWidth=(LONG)(src_ih.biWidth*a); //目标位图的宽
unsigned int result_size=src_re_ih.biWidth*src_re_ih.biHeight; //目标图的像素大小
src_re_fh.bfSize=54+result_size*sizeof(int);
fwrite(&src_re_fh,sizeof(BITMAPFILEHEADER),1,rp); //修改好的文件头写入到目标文件
fwrite(&src_re_ih,sizeof(BITMAPINFOHEADER),1,rp); //修改好的信息头写入到目标文件
unsigned char *fp_pixels,*re_pixels; //用于储存和操作位图数据的数组
fp_pixels=(unsigned char*)malloc(sizeof(int)*filename_size); //分配空间
fread(fp_pixels,sizeof(int),filename_size,fp);
re_pixels=(unsigned char*)malloc(sizeof(int)*result_size); //分配空间
//
//以下是双线性插值法的实现
//
for (int x=0;x<src_re_ih.biWidth;x++)
{
for (int y=0;y<src_re_ih.biHeight;y++)
{
double xx,yy;
xx=(double)x/a;
yy=(double)y/a;
double u,v;
u=xx-(int)xx;
v=yy-(int)yy;
//确定是哪几个像素需要被使用
int result_order=x+y*src_re_ih.biWidth;
int filename_order1=(int)xx+(int)yy*src_ih.biWidth;
int filename_order2=((int)(xx)+1)+(int)yy*src_ih.biWidth;
int filename_order3=(int)xx+((int)yy+1)*src_ih.biWidth;
int filename_order4=((int)(xx)+1)+((int)yy+1)*src_ih.biWidth;
//注意讨论边界情况
if ((int)(xx)+1>=src_ih.biWidth)
{
filename_order2=filename_order1;
filename_order4=filename_order3;
}
if ((int)(yy)+1>=src_ih.biHeight)
{
filename_order3=filename_order1;
filename_order4=filename_order2;
}
//对位图数据进行操作
for (int i=0;i<3;i++)
{
re_pixels[3*result_order+i] = (1 - u) * (1 - v) * fp_pixels[3*filename_order1+i] +
u * (1 - v) * fp_pixels[3*filename_order2+i]
+ (1 - u) * v * fp_pixels[3*filename_order3+i] +
u * v * fp_pixels[3*filename_order4+i];
}
}
}
//
//结束
//
fwrite(re_pixels,sizeof(int),result_size,rp); //写入位图数据到文件
free(fp_pixels);
free(re_pixels);
fclose(fp);
fclose(rp);
return 0;
}
测试样例
测试1
命令行输入:

原图:

结果:

图片属性:

测试2:
命令行输入:

原图:

结果:

