1

Linux下图片处理

 1 year ago
source link: https://blog.51cto.com/u_15688123/6007607
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Linux下图片处理

      图片是指由图形、图像等构成的平面媒体。图片的格式很多,但总体上可以分为点阵图和矢量图两大类,我们常用BMP、JPG等格式都是点阵图形,而SWF、CDR、AI等格式的图形属于矢量图形。

有形式的事物,我们看到的,是图画、照片、拓片等的统称。图是技术制图中的基础术语,指用点、线、符号、文字和数字等描绘事物几何特征、形态、位置及大小的一种形式。随着数字采集技术和信号处理理论的发展,越来越多的图片以数字形式存储。

1.常见的图片格式

在嵌入式开发中,常用的图片格式有BMP、JPG/JPEG、PNG等。

  • BMP图片

位图(外语简称:BMP、外语全称:Bitmap),它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。随着Windows操作系统的流行与丰富的Windows应用程序的发,BMP位图格式理所当然地被广泛应用。这种格式的特点是包含的图像信息较丰富,几乎不进行压缩,但由此导致了它与生俱来的缺点——占用磁盘空间过大,所以,BMP在单机上比较流行。

BMP图片格式简单,在不压缩处理下可以最好的保存图片原始颜色格式。

  • JPG图片

JPEG也是最常见的一种图像格式,它是由联合照片专家组(外语全称:Joint Photographic Experts Group)开发并以命名为“ISO 10918-1”,JPEG仅仅是一种俗称而已。JPEG文件的扩展名为.jpg或.jpeg,因其压缩技术十分先进,它用有损压缩方式去除冗余的图像和彩色数据,获取得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像质量。

同时JPEG还是一种很灵活的格式,具有调节图像质量的功能,允许你用不同的压缩比例对这种文件压缩,比如我们最高可以把1.38MB的BMP位图文件压缩至20.3KB。当然我们完全可以在图像质量和文件尺寸之间找到平衡点。

由于JPEG优异的品质和杰出的表现,特别是在网络和光盘读物上,肯定都能找到它的影子。各类浏览器均支持JPEG这种图像格式,因为JPEG格式的文件尺寸较小,下载速度快,使得Web页有可能以较短的下载时间提供大量美观的图像,JPEG同时也就顺理成章地成为网络上最受欢迎的图像格式。

PNG格式

便携式网络图形(外语简称PNG、外语全称:Portable Network Graphics)是一种新兴的网络图像格式。在1994年底,由于Unysis公司宣布GIF拥有专利的压缩方法,要求开发GIF软件的作者须缴交一定费用,由此促使免费的png图像格式的诞生。PNG一开始便结合GIF及JPG两家之长,打算一举取代这两种格式。1996年10月1日由PNG向国际网络联盟提出并得到推荐认可标准,并且大部分绘图软件和浏览器开始支持PNG图像浏览,从此PNG图像格式生机焕发。

PNG是保证最不失真的格式,它汲取了GIF和JPG二者的优点,存贮形式丰富,兼有GIF和JPG的色彩模式;它的另一个特点能把图像文件压缩到极限以利于网络传输,但又能保留所有与图像品质有关的信息,因为PNG是采用无损压缩方式来减少文件的大小,这一点与牺牲图像品质以换取高压缩率的JPG有所不同;它的第三个特点是显示速度很快,只需下载1/64的图像信息就可以显示出低分辨率的预览图像;第四,PNG同样支持透明图像的制作,透明图像在制作网页图像的时候很有用,我们可以把图像背景设为透明,用网页本身的颜色信息来代替设为透明的色彩,这样可让图像和网页背景很和谐地融合在一起。

PNG的缺点是不支持动画应用效果,如果在这方面能有所加强,简直就可以完全替代GIF和JPEG了。

2.BMP图片处理

BMP图片格式简单,可以不需要借助任何第三方库即可完成图片的各种操作处理,接下来将以C语言实现BMP图片的旋转。

2.1 BMP图片旋转处理

  • 原始图片如下
Linux下图片处理_图片处理
  • 顺时针旋转90°效果:
Linux下图片处理_添加水印_02
  • 逆时针旋转90°效果:
Linux下图片处理_图片处理_03
  •  顺时针旋转90°示例
/***********************顺时针旋转90°**************************
**
**形参:const char *new_bmp -- 顺时针90°后图片
** const char *befor_bmp --原始图片
**返回值:0 -- 成功,其他值 -- 失败
**************************************************************/
int BMP_ClockWise_Revolve90(const char *new_bmp,const char *befor_bmp)
{
FILE *fp[2];
fp[0]=fopen(befor_bmp,"rb");
if(fp[0]==NULL)
{
printf("[%s line %d]文件打开失败",__FUNCTION__,__LINE__);
return 1;
}
fp[1]=fopen(new_bmp,"w+b");
if(fp[1]==NULL)
{
printf("[%s line %d]文件打开或创建失败",__FUNCTION__,__LINE__);
return 2;
}
BMP_HEADER bmp_head;
BMP_INFO bmp_info;
fread(&bmp_head,sizeof(BMP_HEADER),1,fp[0]);//读取头数据
if(bmp_head.bfType!=0x4d42)
{
printf("[%s line %d]图片格式错误\n",__FUNCTION__,__LINE__);
fclose(fp[0]);
fclose(fp[1]);
return 3;
}
fwrite(&bmp_head,sizeof(BMP_HEADER),1,fp[1]);//头数据写入到新的文件中
int w,h;//旋转90°宽和高需要互换
fread(&bmp_info,sizeof(BMP_INFO),1,fp[0]);//读取位图数据
h=bmp_info.biHeight;
w=bmp_info.biWidth;
bmp_info.biWidth=h;//旋转后图片宽度
bmp_info.biHeight=w;//旋转后图片高度
fwrite(&bmp_info,sizeof(BMP_INFO),1,fp[1]);//写入位图数据
printf("\n--------------------顺时针旋转90°----------------------\n");
printf("\t旋转后图片宽:%d\n",bmp_info.biWidth);
printf("\t旋转后图片高:%d\n",bmp_info.biHeight);
int befor_oneline_size=w*3;//之前图片一行的字节数
while(befor_oneline_size%4)befor_oneline_size++;//按4字节对齐
int new_oneline_size=bmp_info.biWidth*3;//旋转后图片一行字节数
while(new_oneline_size%4)new_oneline_size++;//按4字节对齐
int i,j;
int offset_count=0;
int rgb=0;
for(i=w-1;i>=0;i--)
{
for(j=0;j<h;j++)
{
offset_count=j*befor_oneline_size+i*3+bmp_head.bfOffBits;//从第一行的最后一个像素点开始
fseek(fp[0],offset_count,SEEK_SET);
fread(&rgb,bmp_info.biBitCount/8,1,fp[0]);
fwrite(&rgb,bmp_info.biBitCount/8,1,fp[1]);
}
if(new_oneline_size>bmp_info.biWidth*3)
{
fwrite(&rgb,new_oneline_size-bmp_info.biWidth*3,1,fp[1]);//补全为4的倍数
}
}
fclose(fp[0]);
fclose(fp[1]);
return 0;
}
  •  逆时针旋转90°示例
/***********************逆时针旋转90°**************************
**
**形参:const char *new_bmp -- 逆时针90°后图片
** const char *befor_bmp --原始图片
**返回值:0 -- 成功,其他值 -- 失败
**************************************************************/
int BMP_antiClockWise_Revolve90(const char *new_bmp,const char *befor_bmp)
{
FILE *fp[2];
fp[0]=fopen(befor_bmp,"rb");
if(fp[0]==NULL)
{
printf("[%s line %d]文件打开失败",__FUNCTION__,__LINE__);
return 1;
}
fp[1]=fopen(new_bmp,"w+b");
if(fp[1]==NULL)
{
printf("[%s line %d]文件打开或创建失败",__FUNCTION__,__LINE__);
return 2;
}
BMP_HEADER bmp_head;
BMP_INFO bmp_info;
fread(&bmp_head,sizeof(BMP_HEADER),1,fp[0]);//读取头数据
if(bmp_head.bfType!=0x4d42)
{
printf("[%s line %d]图片格式错误\n",__FUNCTION__,__LINE__);
fclose(fp[0]);
fclose(fp[1]);
return 3;
}
fwrite(&bmp_head,sizeof(BMP_HEADER),1,fp[1]);//头数据写入到新的文件中
int w,h;//旋转90°宽和高需要互换
fread(&bmp_info,sizeof(BMP_INFO),1,fp[0]);//读取位图数据
h=bmp_info.biHeight;
w=bmp_info.biWidth;
bmp_info.biWidth=h;//旋转后图片宽度
bmp_info.biHeight=w;//旋转后图片高度
fwrite(&bmp_info,sizeof(BMP_INFO),1,fp[1]);//写入位图数据
printf("\n--------------------逆时针旋转90°----------------------\n");
printf("\t旋转后图片宽:%d\n",bmp_info.biWidth);
printf("\t旋转后图片高:%d\n",bmp_info.biHeight);
int befor_oneline_size=w*3;//之前图片一行的字节数
while(befor_oneline_size%4)befor_oneline_size++;//按4字节对齐
int new_oneline_size=bmp_info.biWidth*3;//旋转后图片一行字节数
while(new_oneline_size%4)new_oneline_size++;//按4字节对齐
int i,j;
int offset_count=0;
int rgb=0;
int cnt=0;
unsigned char buff[new_oneline_size];//存放新图片一行字节数
for(i=0;i<w;i++)
{
cnt=0;
for(j=h-1;j>=0;j--)
{
//先读取最后一行的第一个像素点
offset_count=bmp_head.bfOffBits+i*3+j*befor_oneline_size;
fseek(fp[0],offset_count,SEEK_SET);
fread(&rgb,3,1,fp[0]);//读取一个像素点数据
buff[cnt++]=(rgb)&0xff;
buff[cnt++]=(rgb>>8)&0xff;
buff[cnt++]=(rgb>>16)&0xff;
}
fwrite(buff,cnt,1,fp[1]);//将一行颜色数据写入到新文件中
if(cnt!=new_oneline_size)//补全为4的整数倍
{
rgb=0;
fwrite(&rgb,new_oneline_size-cnt,1,fp[1]);
}
}
fclose(fp[0]);
fclose(fp[1]);
return 0;
}

2.2 BMP图片水印添加

  • 添加水印效果:
Linux下图片处理_旋转_04

水印添加实现原理:先获取图片大小,然后通过内存映射方式(mmap函数)将文件映射到内存中,接下来对内存的操作即为对文件的操作,内存地址偏移到BMP图的RGB颜色位置。将要显示的汉字加载到RGB颜色中即可。其中实现水印添加的核心函数为画点函数,实现如下:

/*
画点函数
形参:x,y --要绘制的位置
color --颜色值
*/
void Image_DrawPoint(int x,int y,int color)
{
unsigned char *p=(height-y-1)*width*3+x*3+image;//光标位置
/*写入rgb颜色*/
*p=color&0xff;
*(p+1)=(color>>8)&0xff;
*(p+2)=(color>>16)&0xff;
}

实现画点函数之后,即可调用汉字字库(点阵字库或者矢量字库均可),这里以点阵字库为例,按照点阵字库的算法将要显示的汉字点阵码取出,再调用画点函数完成汉字绘制,实现如下:

/*显示汉字*/
void Image_DisplayFont(int x,int y,int size,int color,const unsigned char *font)
{
u8 *p=NULL;
u8 H,L;
u32 addr=0;//汉字偏移地址
u16 font_size=size*size/8;//汉字点阵大小(宽度保证为8的倍数)
H=*font;//汉字高字节
L=*(font+1);//汉字的低字节
if(L<0x7F)L-=0x40;
else L-=0x41;
H-=0x81;
addr=(190*H+L)*font_size;//汉字所在点阵中的偏移地址
p=malloc(font_size);
if(p==NULL)
{
printf("申请空间失败\r\n");
return ;
}
if(size == 32)//GBK_32
{
fseek(font_fp, addr, SEEK_SET);//从文件头开始偏移
fread(p,font_size,1,font_fp);
}
else
{
free(p);//释放空间
return ;
}
int i=0,j=0;
int x0=x;
unsigned char temp;
for(i=0;i<size*size/8;i++)//一个汉字的字节数
{
temp=p[i];//取一个字节
for(j=0;j<8;j++)//一个字节8位
{
if(temp&0x80)
{
Image_DrawPoint(x0,y,color);
}
temp<<=1;//继续描绘下一个点
x0++;
}
if(x0-x == size)
{
x0=x;
y++;
}
}
free(p);//释放空间
}

有了上述函数,即可实现任意字符串的水印添加(要添加ascii码则需要进一步添加ascii的点阵码信息即可)。

2.3 BMP图片的结构体定义格式

BMP图片相比于其它格式图片要简单得多,格式定义为两个结构体,固定大小为54个字节,机构体定义如下:

#pragma pack(1) /* 必须在结构体定义之前使用,这是为了让结构体中各成员按1字节对齐*/
/*图片头*/
typedef struct BitMapFileHEADER
{
unsigned short bfType; //保存图片类型。 'BM'
unsigned int bfSize; //图片文件的总大小,以字节为单位(3-6字节,低位在前)
unsigned short bfReserved1;//位图文件保留字,必须为0(7-8字节)
unsigned short bfReserved2;//位图文件保留字,必须为0(9-10字节)
unsigned int bfOffBits; //RGB数据偏移地址,位图数据的起始位置,以相对于位图(11-14字节,低位在前)//文件头的偏移量表示,以字节为单位
}BMP_HEADER;

/*图片信息*/
typedef struct BitMapFileInfo{
unsigned int biSize; //本结构所占用字节数(15-18字节)
unsigned int biWidth; //位图的宽度,以像素为单位(19-22字节)
unsigned int biHeight; //位图的高度,以像素为单位(23-26字节)
unsigned short biPlanes; //目标设备的级别,必须为1(27-28字节)
unsigned short biBitCount; //每个像素所需的位数,必须是1(双色)(29-30字节),4(16色),8(256色)16(高彩色)或24(真彩色)之一
unsigned int biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
unsigned int biSizeImage; //位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
unsigned int biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
unsigned int biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
unsigned int biClrUsed; //位图实际使用的颜色表中的颜色数(47-50字节)
unsigned int biClrImportant; //位图显示过程中重要的颜色数(51-54字节)
}BMP_INFO;
#pragma pack()

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK