001package net.gdface.image; 002 003import java.awt.Rectangle; 004import java.awt.image.BufferedImage; 005import java.io.ByteArrayInputStream; 006import java.io.File; 007import java.io.FileInputStream; 008import java.io.FileNotFoundException; 009import java.io.IOException; 010import java.util.Iterator; 011 012import javax.imageio.ImageIO; 013import javax.imageio.ImageReadParam; 014import javax.imageio.ImageReader; 015import javax.imageio.ImageTypeSpecifier; 016import javax.imageio.stream.ImageInputStream; 017import javax.imageio.stream.MemoryCacheImageInputStream; 018 019import net.gdface.utils.Assert; 020import net.gdface.utils.FaceUtilits; 021 022/** 023 * 图像数据处理对象<br> 024 * {@link #open()}可以在不将图像全部解码加载到内存而获取图像的基本信息<br> 025 * {@link #read(ImageReadParam)}使用内存做cache读取(不使用临时文件做cache)<br> 026 * {@link #read(Rectangle, ImageTypeSpecifier)}可以对图像指定区域解码 027 * @author guyadong 028 * 029 */ 030public class LazyImage extends BaseLazyImage implements ImageMatrix{ 031 private Rectangle rectangle=null; 032 private ImageReader imageReader; 033 private MemoryCacheImageInputStream imageInputstream; 034 private BufferedImage bufferedImage=null; 035 /** 036 * 通过{@link ImageReader}来读取图像基本信息,检查图像数据有效性 037 * @return 038 * @throws UnsupportedFormatException 039 * @throws NotImageException 040 */ 041 @SuppressWarnings("unchecked") 042 @Override 043 public LazyImage open() throws UnsupportedFormatException, NotImageException { 044 try { 045 if(bufferedImage == null){ 046 Iterator<ImageReader> it = ImageIO.getImageReaders(getImageInputstream()); 047 if (it.hasNext()) 048 try { 049 imageReader = it.next(); 050 imageReader.setInput(getImageInputstream(), true, true); 051 this.suffix = imageReader.getFormatName().trim().toLowerCase(); 052 this.width = imageReader.getWidth(0); 053 this.height = imageReader.getHeight(0); 054 } catch (Exception e) { 055 throw new UnsupportedFormatException(e); 056 } 057 else { 058 // 没有找到对应的图像解码则招聘异常 059 throw new NotImageException("NOT FOUND MATCHED ImageReader"); 060 } 061 }else{ 062 this.width = bufferedImage.getWidth(); 063 this.height = bufferedImage.getHeight(); 064 } 065 return this; 066 } finally { 067 if (autoClose) 068 try { 069 close(); 070 } catch (IOException e) { 071 throw new RuntimeException(e); 072 } 073 } 074 } 075 076 077 /** 078 * 对图像数据解码生成 {@link BufferedImage}对象 079 * 080 * @param param 081 * 图像读取参数对象,为null使用默认参数<br> 082 * 参见 {@link ImageReader#getDefaultReadParam()} 083 * @return 084 * @throws UnsupportedFormatException 085 * @see ImageReadParam 086 */ 087 protected BufferedImage read(ImageReadParam param) throws UnsupportedFormatException { 088 try { 089 if(null==this.bufferedImage||null!=param){ 090 ImageReader imageReader=getImageReader(); 091 if(null==imageReader.getInput()) 092 imageReader.setInput(getImageInputstream(), true,true); 093 BufferedImage bi = imageReader.read(0, null==param?imageReader.getDefaultReadParam():param); 094 if (null==param) 095 this.bufferedImage = bi; 096 else 097 return bi; 098 } 099 return this.bufferedImage; 100 } catch (Exception e) { 101 throw new UnsupportedFormatException(e); 102 } finally { 103 if(autoClose) 104 try { 105 close(); 106 } catch (IOException e) { 107 throw new RuntimeException(e); 108 } 109 } 110 } 111 public BufferedImage read() throws UnsupportedFormatException{ 112 return read(null); 113 } 114 /** 115 * 对图像数据指定的区域解码 116 * 117 * @param rect 118 * 解码区域对象, 默认({@code null})全图解码<br> 119 * 参见 {@link ImageReadParam#setSourceRegion(Rectangle)} 120 * @param destinationType 121 * 目标图像的所需图像类型,默认为null, <br> 122 * 例如用此参数可以在解码时指定输出的图像类型为RGB,<br> 123 * 如下代码 生成 destinationType参数对象:<br> 124 * {@code 125 * ImageTypeSpecifier destinationType=ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR); 126 * }<br> 127 * 用上面的ImageTypeSpecifier对象来调用此方法返回的BufferedImage对象中的raster成员(通过BufferedImage.getData()获取) 128 * 的getDataElements()方法返回的就是包含RGB数据byte[]类型的数组<br> 129 * 而直接用BufferedImage.getRGB()方式只能获取ARGB类型的int[]数组 参见 130 * {@link ImageReadParam#setDestinationType(ImageTypeSpecifier) } 131 * @return 132 * @throws UnsupportedFormatException 133 * @see #read(ImageReadParam) 134 */ 135 public BufferedImage read(Rectangle rect, ImageTypeSpecifier destinationType) throws UnsupportedFormatException { 136 ImageReadParam param = getImageReader().getDefaultReadParam(); 137 if (rect != null && !rect.equals(getRectangle())) 138 param.setSourceRegion(rect); 139 param.setDestinationType(destinationType); 140 if(null!=destinationType) 141 param.setDestination(destinationType.createBufferedImage(width, height)); 142 return read(param); 143 } 144 @Override 145 public byte[] getMatrixRGBA() throws UnsupportedFormatException{ 146 if (matrixRGBA==null){ 147 matrixRGBA=ImageUtil.getMatrixRGBA(read()); 148 } 149 return matrixRGBA; 150 } 151 152 @Override 153 public byte[] getMatrixRGB() throws UnsupportedFormatException{ 154 if (matrixRGB==null){ 155 matrixRGB=ImageUtil.getMatrixRGB(read()); 156 } 157 return matrixRGB; 158 } 159 160 @Override 161 public byte[] getMatrixBGR() throws UnsupportedFormatException{ 162 if (matrixBGR==null){ 163 matrixBGR=ImageUtil.getMatrixBGR(read()); 164 } 165 return matrixBGR; 166 } 167 /** 168 * 对图像数据指定的区域解码返回灰度图像数据 169 * @return 灰度图像矩阵数据 170 * @throws UnsupportedFormatException 171 */ 172 @Override 173 public byte[] getMatrixGray() throws UnsupportedFormatException{ 174 if(null==matrixGray){ 175 matrixGray = ImageUtil.getMatrixGRAY(read()); 176 } 177 return matrixGray; 178 179 } 180 @Override 181 public byte[] wirtePNGBytes(){ 182 try { 183 if("PNG".equalsIgnoreCase(getSuffix())){ 184 if(getImgBytes() != null){ 185 return getImgBytes(); 186 } 187 } 188 return ImageUtil.wirtePNGBytes(read()); 189 } catch (UnsupportedFormatException e) { 190 throw new RuntimeException(e); 191 } 192 } 193 @Override 194 public byte[] wirteJPEGBytes(){ 195 try { 196 if("JPEG".equalsIgnoreCase(getSuffix())){ 197 if(getImgBytes() != null){ 198 return getImgBytes(); 199 } 200 } 201 return ImageUtil.wirteJPEGBytes(read(),0.9f); 202 } catch (UnsupportedFormatException e) { 203 throw new RuntimeException(e); 204 } 205 } 206 /** 207 * 创建并打开对象 208 * @param imgBytes 209 * @return 210 * @throws NotImageException 211 * @throws UnsupportedFormatException 212 * @see #LazyImage(byte[]) 213 * @see #open() 214 */ 215 public static LazyImage create(final byte[] imgBytes) throws NotImageException, UnsupportedFormatException { 216 return new LazyImage(imgBytes).open(); 217 } 218 /** 219 * 创建对象 220 * @param bufferedImage 221 * @see #LazyImage(BufferedImage) 222 * @see #open() 223 * @return 224 */ 225 public static LazyImage create(final BufferedImage bufferedImage) { 226 try { 227 return new LazyImage(bufferedImage).open(); 228 } catch (ImageErrorException e) { 229 throw new RuntimeException(e); 230 } 231 } 232 /** 233 * 用本地图像文件创建对象 234 * @param file 235 * @param md5 {@code file}的MD5较验码,可以为null 236 * @return 237 * @throws NotImageException 238 * @throws UnsupportedFormatException 239 */ 240 public static LazyImage create(final File file, String md5) throws NotImageException, UnsupportedFormatException { 241 try { 242 return new LazyImage(file, md5).open(); 243 } catch (FileNotFoundException e) { 244 throw new RuntimeException(e); 245 } 246 } 247 248 /** 249 * 多源创建对象 250 * @param src 251 * @return 252 * @throws NotImageException 253 * @throws UnsupportedFormatException 254 * @see #LazyImage(Object) 255 * @see FaceUtilits#getBytesNotEmpty(Object) 256 */ 257 public static <T> LazyImage create(final T src) throws NotImageException, UnsupportedFormatException { 258 try { 259 return new LazyImage(src).open(); 260 } catch (IOException e) { 261 throw new RuntimeException(e); 262 } 263 } 264 265 /** 266 * @param bufferedImage 已经解码的图像数据,为null或为空,则抛出 {@link IllegalArgumentException} 267 */ 268 public LazyImage(BufferedImage bufferedImage) 269 { 270 Assert.notNull(bufferedImage, "bufferedImage"); 271 this.bufferedImage = bufferedImage; 272 } 273 /** 274 * @param imgBytes 图像数据,{@code imgBytes}为null或为空,则抛出 {@link IllegalArgumentException} 275 */ 276 public LazyImage(byte[] imgBytes) { 277 super(imgBytes); 278 } 279 280 /** 281 * 用本地图像文件创建对象 282 * @param src 283 * @param md5 284 * @throws FileNotFoundException 285 */ 286 public LazyImage(File src, String md5) throws FileNotFoundException { 287 super(src, md5); 288 } 289 290 /** 291 * 多源创建对象 292 * @param src 293 * @throws IOException 294 * @see FaceUtilits#getBytesNotEmpty(Object) 295 */ 296 public <T>LazyImage(T src) throws IOException { 297 this(FaceUtilits.getBytesNotEmpty(src)); 298 } 299 300 /** 301 * 返回{@link ImageInputStream}对象<br> 302 * 如果 {@link #imageInputstream} 为{@code null},则根据 {@link #imgBytes}或 {@link #localFile}创建 303 * @return imageInputstream 304 */ 305 private ImageInputStream getImageInputstream() { 306 if (null == imageInputstream) { 307 if (null == imgBytes) { 308 if (null == localFile) 309 throw new IllegalArgumentException( 310 "while isValidImage be true localFile & imgBytes can't be NULL all"); 311 try { 312 this.imageInputstream = new MemoryCacheImageInputStream(new FileInputStream(localFile)); 313 } catch (FileNotFoundException e) { 314 throw new RuntimeException(e); 315 } 316 }else{ 317 this.imageInputstream = new MemoryCacheImageInputStream(new ByteArrayInputStream(imgBytes)); 318 } 319 } 320 return imageInputstream; 321 } 322 323 /** 324 * 返回{@link ImageReader}对象<br> 325 * 如果 {@link #imageReader}为{@code null},则根据 {@link #suffix}创建,失败抛出 326 * @return imageReader {@link ImageReader}对象 327 * @throws IllegalStateException 无法根据{@link #suffix}获取{@link ImageReader} 328 */ 329 private ImageReader getImageReader() throws IllegalStateException { 330 if (null == imageReader) { 331 Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(suffix); 332 if (it.hasNext()) 333 imageReader = it.next(); 334 else 335 throw new IllegalStateException(String.format("invalid suffix %s", suffix)); 336 } 337 return imageReader; 338 } 339 340 /** 341 * 释放资源 342 * @throws IOException 343 */ 344 public void close() throws IOException{ 345 if(null!=imageReader){ 346 imageReader.dispose(); 347 imageReader=null; 348 } 349 if(null!=imageInputstream){ 350 imageInputstream.close(); 351 imageInputstream=null; 352 } 353 super.close(); 354 } 355 @Override 356 public void finalize() throws Throwable { 357 this.bufferedImage=null; 358 this.rectangle=null; 359 super.finalize(); 360 } 361 362 363 /** 364 * 获取图像矩形对象 365 * @return rectangle 366 */ 367 public Rectangle getRectangle() { 368 if(null==rectangle) 369 rectangle=new Rectangle(0,0,width,height); 370 return rectangle; 371 } 372}