2012-01-23 34 views
10

Sử dụng imageIO, tôi thường gặp sự cố khi chuyển đổi tệp hình ảnh và sau khi ghi đè hình ảnh, nó sẽ mất tất cả dữ liệu EXIF ​​của nó. Có cách nào để bảo tồn nó mà không cần giải nén nó trước, lưu vào bộ nhớ đệm và sau đó đặt lại nó?Thao tác hình ảnh mà không xóa dữ liệu EXIF ​​

+2

Lưu trữ ở nơi khác sau đó ghi đè hình ảnh mới bằng meta exif cũ? http://www.screaming-penguin.com/node/7485 –

+0

Đây chính xác là những gì tôi muốn tránh – preslavrachev

+2

vấn đề với việc sao chép meta là gì? đây là một ví dụ khác http://nucleussystems.com/blog/java-copy-exif-data –

Trả lời

9

Đây là giải pháp của tôi bằng cách sử dụng kết hợp ImageIO, Imgscalr và Apache commons-hình ảnh. Thật đáng tiếc là không có thư viện nào kết hợp việc đọc hình ảnh với siêu dữ liệu của nó, làm cho điều này có thể khá là quá mức khi sử dụng bộ nhớ; Cải tiến chào đón.

import java.awt.image.BufferedImage; 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import javax.imageio.ImageIO; 
import org.apache.commons.imaging.ImageReadException; 
import org.apache.commons.imaging.ImageWriteException; 
import org.apache.commons.imaging.Imaging; 
import org.apache.commons.imaging.common.IImageMetadata; 
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; 
import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter; 
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; 
import org.apache.commons.io.IOUtils; 
import org.imgscalr.Scalr; 


public class ImageData { 

    private byte[] imageData; 


    public ImageData(InputStream instream) throws IOException { 
     imageData = IOUtils.toByteArray(instream); 
     instream.close(); 
    } 


    public synchronized void resize(int maxDimension) throws IOException, ImageReadException, ImageWriteException { 
     // Resize the image if necessary 
     BufferedImage image = readImage(imageData); 
     if (image.getWidth() > maxDimension || image.getHeight() > maxDimension) { 

      // Save existing metadata, if any 
      TiffImageMetadata metadata = readExifMetadata(imageData); 
      imageData = null; // allow immediate GC 

      // resize 
      image = Scalr.resize(image, maxDimension); 

      // rewrite resized image as byte[] 
      byte[] resizedData = writeJPEG(image); 
      image = null; // allow immediate GC 

      // Re-code resizedData + metadata to imageData 
      if (metadata != null) { 
       this.imageData = writeExifMetadata(metadata, resizedData); 
      } else { 
       this.imageData = resizedData; 
      } 
     } 
    } 

    private TiffImageMetadata readExifMetadata(byte[] jpegData) throws ImageReadException, IOException { 
     IImageMetadata imageMetadata = Imaging.getMetadata(jpegData); 
     if (imageMetadata == null) { 
      return null; 
     } 
     JpegImageMetadata jpegMetadata = (JpegImageMetadata)imageMetadata; 
     TiffImageMetadata exif = jpegMetadata.getExif(); 
     if (exif == null) { 
      return null; 
     } 
     return exif; 
    } 


    private byte[] writeExifMetadata(TiffImageMetadata metadata, byte[] jpegData) 
           throws ImageReadException, ImageWriteException, IOException { 
     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     new ExifRewriter().updateExifMetadataLossless(jpegData, out, metadata.getOutputSet()); 
     out.close(); 
     return out.toByteArray(); 
    } 


    private BufferedImage readImage(byte[] data) throws IOException { 
     return ImageIO.read(new ByteArrayInputStream(data)); 
    } 

    private byte[] writeJPEG(BufferedImage image) throws IOException { 
     ByteArrayOutputStream jpegOut = new ByteArrayOutputStream(); 
     ImageIO.write(image, "JPEG", jpegOut); 
     jpegOut.close(); 
     return jpegOut.toByteArray(); 
    } 

    public synchronized void writeJPEG(OutputStream outstream) throws IOException { 
     IOUtils.write(imageData, outstream); 

    } 

    public synchronized byte[] getJPEGData() { 
     return imageData; 
    } 

} 
+0

Cảm ơn rất nhiều. Nó hoạt động rất tốt. Điều duy nhất là rõ ràng 'IImageMetadata' được đặt tên là 'ImageMetadata' trong repo hiện tại cho Apache Commons Imaging –

+0

Bạn cũng có thể kiểm tra giải pháp khác từ @Rigeborod trông có vẻ hiệu quả hơn một chút –

8

ImageIO làm có chức năng này chính nó, nhưng thay vì ImageIO.read bạn sẽ cần phải sử dụng ImageReader:

ImageReader reader = ImageIO.getImageReadersBySuffix("jpg").next(); 

(bạn có thể muốn cũng để kiểm tra xem người đọc như vậy tồn tại) . Sau đó, bạn cần phải thiết lập đầu vào:

reader.setInput(ImageIO.createImageInputStream(your_imput_stream)); 

Bây giờ bạn có thể tiết kiệm siêu dữ liệu của bạn:

IIOMetadata metadata = reader.getImageMetadata(0); 
          // As far as I understand you should provide 
          // index as tiff images could have multiple pages 

Và sau đó đọc hình ảnh:

BufferedImage bi = reader.read(0); 

Khi bạn muốn lưu hình ảnh mới , bạn nên sử dụng ImageWriter:

// I'm writing to byte array in memory, but you may use any other stream 
ByteArrayOutputStream os = new ByteArrayOutputStream(255); 
ImageOutputStream ios = ImageIO.createImageOutputStream(os); 

Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg"); 
ImageWriter writer = iter.next(); 
writer.setOutput(ios); 

//You may want also to alter jpeg quality 
ImageWriteParam iwParam = writer.getDefaultWriteParam(); 
iwParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
iwParam.setCompressionQuality(.95f); 

//Note: we're using metadata we've already saved. 
writer.write(null, new IIOImage(bi, null, metadata), iwParam); 
writer.dispose(); 

ImageIO.write(bi, "jpg", ios); 

Vì đó là chủ đề cũ, tôi đoán câu trả lời này hơi muộn một chút, nhưng có thể giúp người khác vì chủ đề này vẫn còn googlable.

+0

Điều này có vẻ hiệu quả hơn bộ nhớ so với giải pháp của tôi, Tôi đoán một bản sao của hình ảnh vẫn còn trong bộ nhớ nhưng thực sự không thể tránh được điều đó. –

Các vấn đề liên quan