|
昨天早上朋友传给我一张图片,曾被此君的一些恶作剧图片吓倒过,这次刚开始也不敢打开。这张图片在winxp下缩略图显示与打开后的内容不一样,让几个同学看了一下,他们都说看过了。就是前段时间网上流传的‘一张令所有人吃惊的图片’,是一张椅子的图片,但是,如果你的系统是XP,把它下载后保存到任意一个文件夹中,打开文件夹,用缩略图的方式查看,会看到图片的缩略图是一个机器女人坐在地上。
原图: 缩略图:
很惊奇,但直觉的反映就是这张图片可能被存储了别的信息或修改了头文件信息。
经过一番研究之后,知道了原理:JPEG标准在文件中记录了一些EXIF信息,缩略图是一幅较小的JPEG图片,存储在EXIF信息段。而Windows在第一次显示缩略图时先读当前目录中的"Thumbs.db"这个文件,其实这是一个缩略图数据库,从而来判断是否有该图片的缩略图。如果不存在"Thumbs.db"文件或者该库中不存在该图片的缩略图,那么Windows会尝试取图片中的EXIF信息,判断是否存在缩略图数据。如果图片中EXIF信息中不存在缩略图信息或信息错误,那么Windows就会用插值的方法重新生成缩略图(如果可能则保存到当前目录中的"Thumbs.db"缩略图数据库中)。 对于修改缩略图方法有用ultraEdit直接编辑文件替换EXIF信息或用exifer这样的工具,这不是本文关心的内容,本文介绍用java实现的方法。
程序把一张jpg格式图片的缩略图用另一张图的内容替换,实现的效果跟上面一样
原图: 缩略图:
实现原理就是抽取一张图片的信息,然后替换掉另一张图片EXIF的Thumbnail。注意抽取的图片长宽比例要与原图一致,否则没有效果,在程序中我已经自动判断并调整了。下面是抽取信息的一个主要方法:
java 代码
-
public static byte[] getThumbnailImage(InputStream ip,int widthRate,int heightRate) throws IOException
-
-
//根据文件名字符串,按长宽比例放缩抽取该文件的ThumbnailImage,此方法调用到getThumbnailImage(),返回byte数组
-
private byte[] extractThumbnail(String fileStr,int widthRate,int heightRate)
-
-
向另一张图片写入Thumbnail的方法,用到mediautil库:
-
public void writeThumbnail(byte newThumbnail[],String fileStr)
-
-
主方法,thumbnailFile为要抽取缩略图信息的图片,destfile为目标图片也就是把它的缩略图用thumbnailFile信息替换掉
-
public void test(String thumbnailFile,String destfile){
-
BufferedImage buf=null;
-
int wRate=0;
-
int hRate=0;
-
try{
-
//获取destfile的长和宽,供放缩thumbnailFile使用
-
buf=readImage(destfile);
-
wRate=buf.getWidth();
-
hRate=buf.getHeight();
-
}catch(Exception e){e.printStackTrace();}
-
finally{
-
byte[] bt=extractThumbnail(thumbnailFile,wRate,hRate);//抽取thumbnailFile数据,存入bt中
-
writeThumbnail(bt,destfile);//向文件名为destfile的图片中写入bt中的thumbnail数据
-
}
-
———————————————下面是全部源代码(小实验,代码写得很乱)———————————————
-
import java.io.*;
-
import java.util.Date;
-
import java.awt.*;
-
import java.awt.image.*;
-
import java.util.*;
-
import javax.imageio.*;
-
import javax.imageio.stream.ImageInputStream;
-
import mediautil.gen.directio.*;
-
import mediautil.gen.Log;
-
import java.awt.geom.AffineTransform;
-
import mediautil.image.ImageResources;
-
import mediautil.image.jpeg.LLJTran;
-
import mediautil.image.jpeg.AbstractImageInfo;
-
import mediautil.image.jpeg.Exif;
-
import mediautil.image.jpeg.Entry;
-
import mediautil.image.jpeg.LLJTranException;
-
public class TestExif {
-
/**
-
* Utility Method to get a Thumbnail Image in a byte array from an
-
* InputStream to a full size image. The full size image is read and scaled
-
* to a Thumbnail size using Java API.
-
*/
-
private static byte[] getThumbnailImage(InputStream ip) throws IOException{
-
return getThumbnailImage(ip,0,0);
-
}
-
-
public static byte[] getThumbnailImage(InputStream ip,int widthRate,int heightRate) throws IOException {
-
ImageReader reader;
-
ImageInputStream iis = ImageIO.createImageInputStream(ip);
-
reader = (ImageReader) ImageIO.getImageReaders(iis).next();
-
reader.setInput(iis);
-
BufferedImage image = reader.read(0);
-
iis.close();
-
-
int t, longer, shorter;
-
if(widthRate>0&&heightRate>0){
-
longer = widthRate;
-
shorter = heightRate;
-
}else{
-
longer = image.getWidth();
-
shorter = image.getHeight();
-
}
-
-
//按传入参数的长宽比例放缩
-
double factor = 160/(double)image.getWidth();
-
double factor2 =(160* (double)shorter)/((double)longer*image.getHeight());
-
AffineTransform tx = new AffineTransform();
-
tx.scale(factor, factor2);
-
AffineTransformOp affineOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
-
image = affineOp.filter(image, null);
-
-
// Write Out the Scaled Image to a ByteArrayOutputStream and return the bytes
-
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(2048);
-
String format = "JPG";
-
ImageIO.write(image, format, byteStream);
-
System.out.println(byteStream.toByteArray());
-
return byteStream.toByteArray();
-
}
-
public byte[] extractThumbnail(String fileStr){
-
return extractThumbnail(fileStr,0,0);
-
}
-
-
//根据文件名字符串,按长宽比例放缩抽取该文件的ThumbnailImage,返回byte数组
-
private byte[] extractThumbnail(String fileStr,int widthRate,int heightRate) {
-
byte newThumbnail[]=null;
-
try{
-
InputStream fip = new FileInputStream(fileStr); // No need to buffer
-
SplitInputStream sip = new SplitInputStream(fip);
-
//Create a substream for LLJTran to use
-
InputStream subIp = sip.createSubStream();
-
LLJTran llj = new LLJTran(subIp);
-
llj.initRead(LLJTran.READ_HEADER, true, true);
-
sip.attachSubReader(llj, subIp);
-
newThumbnail= getThumbnailImage(sip,widthRate,heightRate);
-
sip.wrapup();
-
fip.close();
-
llj.freeMemory();
-
String msg = llj.getErrorMsg();
-
if(msg != null){
-
System.out.println("Error in LLJTran While Loading Image: " + msg);
-
Exception e = llj.getException();
-
if(e != null){
-
System.out.println("Got an Exception, throwing it..");
-
throw e;
-
}
-
System.exit(1);
-
}
-
}catch(Exception e){System.out.println("extractThumbnail"+e);}
-
return newThumbnail;
-
}
-
//向另一张图片写入Thumbnail的方法,用到mediautil库:
-
public void writeThumbnail(byte newThumbnail[],String fileStr){
-
try{
-
InputStream fip = new FileInputStream(fileStr);
-
LLJTran llj = new LLJTran(fip);
-
llj.read(LLJTran.READ_ALL,true);
-
-
AbstractImageInfo imageInfo = llj.getImageInfo();
-
//important!!!! If the Image does not have an Exif Header create a dummy Exif
-
//Header
-
if(!(imageInfo instanceof Exif)){
-
System.out.println("Adding a Dummy Exif Header");
-
llj.addAppx(LLJTran.dummyExifHeader, 0,
-
LLJTran.dummyExifHeader.length, true);
-
}
-
-
// Set the new Thumbnail
-
if(llj.setThumbnail(newThumbnail, 0, newThumbnail.length,ImageResources.EXT_JPG))
-
System.out.println("Successfully Set New Thumbnail");
-
fip = new BufferedInputStream(new FileInputStream(fileStr));
-
OutputStream out = new BufferedOutputStream(new FileOutputStream("3.jpg"));
-
//
-
llj.xferInfo(fip, out, LLJTran.REPLACE, LLJTran.REPLACE);
-
fip.close();
-
out.close();
-
// Cleanup
-
llj.freeMemory();
-
}catch(Exception e){System.out.println("writeThumbnail"+e);}
-
}
-
-
public BufferedImage readImage(InputStream in,String type)throws IOException{
-
Iterator readers = ImageIO.getImageReadersByFormatName(type);
-
ImageReader reader = (ImageReader)readers.next();
-
ImageInputStream iis = ImageIO.createImageInputStream(in);
-
reader.setInput(iis,true);
-
BufferedImage img = reader.read(0);
-
return img;
-
}
-
public BufferedImage readImage(String fileName) throws IOException{
-
String type = fileName.substring(fileName.lastIndexOf(".")+1);
-
return readImage(new FileInputStream(fileName), type);
-
}
-
-
public void test(String thumbnailFile,String destfile){ BufferedImage buf=null;
-
int wRate=0;
-
int hRate=0;
-
try{
-
buf=readImage(destfile);
-
wRate=buf.getWidth();
-
hRate=buf.getHeight();
-
}catch(Exception e){e.printStackTrace();}
-
finally{
-
byte[] bt=extractThumbnail(thumbnailFile,wRate,hRate);
-
writeThumbnail(bt,destfile);
-
}
-
}
-
-
public static void main(String arg[]){
-
TestExif t= new TestExif();
-
t.test("11.jpg", "22.jpg"); //用11.jpg的数据替换22.jpg的缩略图
-
}
-
}
|