2012-01-23 33 views
10

imageIOを使用すると、通常は画像ファイルを変換する問題があり、上書きした後はすべてのEXIFデータが失われます。最初に抽出し、キャッシュしてからリセットすることなく保存する方法はありますか?EXIFデータを削除せずに画像を操作する

+2

これを別の場所に保存してから、新しいイメージを古いexifメタで上書きしますか? http://www.screaming-penguin.com/node/7485 –

+0

これは私が避けたいものです。 – preslavrachev

+2

メタのコピーで何が問題になっていますか?ここに別の例がありますhttp://nucleussystems.com/blog/java-copy-exif-data –

答えて

9

ImageIO、ImgscalrとApacheのコモンズイメージングを組み合わせたソリューションです。残念ながら、イメージをそのメタデータと読みとり、メモリ使用量が多すぎるという単一のライブラリはありません。改善は大歓迎です。 (あなたは、このようなリーダーが存在するかどうかもチェックすることをお勧めします)

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

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

ありがとうございました。それはきれいに働いた。唯一のことは、明らかにApache Commons Imagingの現在のリポジトリにある 'IImageMetadata'は 'ImageMetadata'という名前になっていることです。 –

+0

もう少し効率的に見える@Rigeborodの他のソリューションもチェックしてください –

8

ImageIOには、あなたがたImageReaderを使用する必要があります。この機能自体を持っているが、その代わりにImageIO.readの。 そして、あなたが入力を設定する必要があります。

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

今、あなたはあなたのメタデータ保存可能性があります

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

をし、イメージ読む:あなたが新しいイメージを保存したい場合は

BufferedImage bi = reader.read(0); 

を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); 

これは古い話題なので、この回答は少し遅すぎると思うが、この話題は依然としてグーグルであるので、他人を助けるかもしれない。

+0

私の解決策よりも、私はイメージの1つのコピーがまだメモリ内にあると思うが、実際にそれを避けることはできない。 –

関連する問題