高效获取 jpeg 图片的尺寸
获取图片的尺寸有三种方法:
1.将整个图片文件加载成 BufferedImage 后获取其尺寸;
2.用 ImageReader 快捷获取
3.即下文所陈述的方式
做过简陋的测试,效率自 1 至 3 逐步递增。
ImageInfo.java
/** * @author kodeyang */ public class ImageInfo { private int height; private int width; public ImageInfo(int height, int width) { super(); this.height = height; this.width = width; } public int getWidth() { return width; } public int getHeight() { return height; } @Override public String toString() { return "height: " + height + ", width: " + width; } }
import java.io.IOException; import java.io.InputStream; /** * @author yang3wei * */ public class JpegInfoReader { private static final byte TAG_START = (byte) 0xff; private static final byte START_OF_IMAGE = (byte) 0xd8; private static final byte END_OF_IMAGE = (byte) 0xd9; private static final byte START_OF_FRAME = (byte) 0xc0; private static final byte RESTART_MODULO_START = (byte) 0xd0; private static final byte RESTART_MODULO_END = (byte) 0xd7; private static final byte START_OF_SCAN = (byte) 0xda; public static ImageInfo getImageInfo(InputStream in) throws IOException { // 0-1: store JPEG tag // 2-3: store JPEG tag length // 4-5: store JPEG image height // 6-7: store JPEG image width byte[] seg = new byte[8]; // read JPEG START_OF_IMAGE tag if (in.read(seg, 0, 2) == -1) { return null; } // if the first two bytes is not 0xff, 0xd8, // that is the image format is not JPEG if (seg[0] != TAG_START || seg[1] != START_OF_IMAGE) { return null; } while (true) { // read JPEG data tag, offset 0 must be 0xff if (in.read(seg, 0, 2) == -1) { return null; } // if tag does not start with 0xff, // the image format is not JPEG if (seg[0] != TAG_START) { return null; } // Ignore JPEG RESTART_MODULO tag if (seg[1] >= RESTART_MODULO_START && seg[1] <= RESTART_MODULO_END) { continue; } // find JPEG format START_OF_SCAN part, // data that starts with poisition is JPEG compression image data, // that never contains image meta information if (seg[1] == START_OF_SCAN) { return null; } // find JPEG format END_OF_IMAGE tag, finish scan if (seg[1] == END_OF_IMAGE) { return null; } // read JPEG data tag length if (in.read(seg, 2, 2) == -1) { return null; } // find START_OF_FRAME tag if (seg[1] == START_OF_FRAME) { break; } // skip JPEG data segement byte[] skip = new byte[toInt(seg, 2) - 2]; if (in.read(skip) == -1) { return null; } } // ignore JPEG image precision byte if (in.read() == -1) { return null; } // read JPEG image height and width bytes if (in.read(seg, 4, 4) == -1) { return null; } return new ImageInfo(toInt(seg, 4), toInt(seg, 6)); } private static int toInt(byte[] bys, int start) { return ((bys[start] & 0xff) << 8) | (bys[start + 1] & 0xff); } }
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * @author yang3wei * */ public class JpegInfoReaderUsage { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { InputStream in = null; try { in = new FileInputStream("d:/images.jpg"); ImageInfo info = JpegInfoReader.getImageInfo(in); System.out.println(info); } finally { if (in != null) { in.close(); } } } }
提问:
1.class 前面带 static 是什么用法呢,
2.另外,宽高都拿到了,为什么还要将 宽高丢到一个 ImageInfo 对象里面呢,直接使用整形数组的结果不是更快捷?
回答:
1:内部类使用 static 的话,一般说明这个内部类不需要使用到外部类中的任何成员变量或者成员方法,也就是说这个内部类相对于较为独立。
如果有需要使用到外部类中数据的话,则需要把 static 去掉。不对外部类数据进行引用时,内部类应使用 static 类。
2:对于这个问题,举个很简单的例子,如果我还想获取图像长宽的 DPI 信息,以及颜色深度信息,或者是 JPEG 中 Exif 信息的话,那使用 int[] 就没办法表示了。
在面向对象程序设计中,返回值应尽量少使用 int[], String[], Map<String, Object> 之类的返回值,因为这样的返回值不是自描述的,
为什么 int[0] 是表示长度呢?int[0] 就不能表示宽度么?
我很能理解你认为返回 int[] 的理由是什么,可能是因为 new 一个对象比较耗时和占用内存空间吧?
实际上并不是这样的,new 一个对象而且这个类的构造中没有任何复杂耗时的操作,对于系统消耗来说可以忽略不计,
而对于 HotSpot 的 JVM 而言,其内存分配的速度是大于 C 语言中 malloc 的内存分配速度。
在 HotSpot 的 JVM 中一个 new 操作只占用了十几个 CPU 指令的时间。new 一个对象和 new 一个 int 数据的效率是等价的。
另外,new 一个对象所分配的内存更是可以忽略不计了,只是在 JVM 的 PermGen 方法区中多存放一些类的信息,以及方法的信息。
补充:软件开发 , Java ,