每次逛百度图片时,很好奇它的图片是怎么从模糊到清晰变化的,开始以为时代码上的处理,直到后来无意发现来jpg的编码格式是有Baseline JPEG(标准型)和Progressive JPEG(渐进式)两种。两种格式有相同尺寸以及图像数据,他们的扩展名也是相同的,唯一的区别是显示的方式不同。

Baseline JPEG

这种类型的JPEG文件存储方式是按从上到下的扫描方式,把每一行顺序的保存在JPEG文件中。打开这个文件显示它的内容时,数据将按照存储时的顺序从上到下一行一行的被显示出来,直到所有的数据都被读完,就完成了整张图片的显示。如果文件较大或者网络下载速度较慢,那么就会看到图片被一行行加载的效果,这种格式的JPEG没有什么优点。

Progressive JPEG

Progressive JPEG文件包含多次扫描,这些扫描顺寻的存储在JPEG文件中。打开文件过程中,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片变得越来越清晰。这种格式的主要优点是在网络较慢的情况下,可以看到图片的轮廓知道正在加载的图片大概是什么。百度图片就是这种。
渐进式图片带来的好处是可以让用户在没有下载完图片就可以看到最终图像的大致轮廓,一定程度上可以提升用户体验。

格式转换

Linux

检测是否为progressive jpeg

1
2
3
4
5
6
//转换前,结果为:None,表明非渐进式
[root@master1 ~]# identify -verbose pj.jpg | grep Interlace
Interlace: None
//转换后,结果为:JPEG,表明是渐进式
[root@master1 ~]# identify -verbose pj-p.jpg | grep Interlace
Interlace: JPEG

将basic jpeg转换成progressive jpeg

1
convert basic.jpg -interlace Plane progressive.jpg

PHP

1
2
3
4
5
6
<?php 
$im= imagecreatefromjpeg('basic.jpg');
imageinterlace($im, 1);
imagejpeg($im, './progressive.jpg', 100);
imagedestroy($im);
?>

Python

1
2
3
4
5
6
7
8
9
10
importPIL 
fromexceptions importIOError

img =PIL.Image.open("D:\\basic.jpg")
destination ="D:\\progressive.jpeg"
try:
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)
exceptIOError:
PIL.ImageFile.MAXBLOCK =img.size[0] *img.size[1]
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)

jpegtran

下载地址:http://jpegclub.org/jpegtran.exe

1
2
//命令
jpegtran -copy none -optimize -progressive basic.jpg progressive.jpg

C#

1
2
3
4
5
6
7
8
using(Image source = Image.FromFile(@"D:\\basic.jpg")) { 
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType == "image/jpeg");
EncoderParameters parameters = newEncoderParameters(3);
parameters.Param[0] = newEncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
parameters.Param[1] = newEncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced);
parameters.Param[2] = newEncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, (int)EncoderValue.RenderProgressive);
source.Save(@"D:\progressive.jpg", codec, parameters);
}

JAVA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.tuzki.sannychan;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
public class ProgressiveJPEG {
public static void main(String[] args) throws Exception {
File file=new File("D:/basic.jpg");
BufferedImage image = ImageIO.read(file);
Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer=null;
while(it.hasNext()) {
writer=it.next();
break;
}
if(writer!=null) {
ImageWriteParam params = writer.getDefaultWriteParam();
params.setProgressiveMode(ImageWriteParam.MODE_DEFAULT);
//params.setCompressionQuality(0.8f);
ImageOutputStream output = ImageIO.createImageOutputStream(new File("D:/progressive.jpg"));
writer.setOutput(output);
writer.write(null,new IIOImage(image,null,null), params);
output.flush();
writer.dispose();
}
}
}