2012-01-23 13 views
10

Używając imageIO, zazwyczaj mam problem z przekształceniem pliku obrazu, a po jego zastąpieniu, traci on wszystkie swoje dane EXIF. Czy istnieje sposób na zachowanie go bez wcześniejszego wyodrębniania, buforowania go, a następnie resetowania?Manipulowanie obrazem bez usuwania jego danych EXIF ​​

+2

Przechowuj go w innym miejscu, a następnie zastąp nowe zdjęcie starą metą exif? http://www.screaming-penguin.com/node/7485 –

+0

Dokładnie tego chcę uniknąć – preslavrachev

+2

jaki jest problem z kopiowaniem mety? oto inny przykład http://nucleussystems.com/blog/java-copy-exif-data –

Odpowiedz

9

Oto moje rozwiązanie wykorzystujące połączenie Imageio, Imgscalr i Apache-obrazowania. Szkoda, że ​​nie ma jednej biblioteki, która łączy czytanie obrazu z jego metadanymi, sprawiając, że prawdopodobnie jest to nadmierne użycie pamięci; Usprawnienia mile widziane.

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

Wielkie dzięki. To działało ładnie. Jedyną rzeczą jest to, że najwyraźniej 'IImageMetadata' ma nazwę 'ImageMetadata' w bieżącym repozytorium dla Apache Commons Imaging –

+0

Sprawdź także inne rozwiązanie z @Rigeborod, które wygląda na nieco bardziej wydajne –

8

ImageIO mają tę samą funkcjonalność, ale zamiast ImageIO.read trzeba będzie użyć ImageReader:

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

(możesz też sprawdzić, czy istnieje taki czytnik) . Następnie trzeba ustawić wejście:

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

Teraz możesz zapisać metadane:

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

a następnie odczytać obraz:

BufferedImage bi = reader.read(0); 

Kiedy chcesz zapisać nowy obraz , powinieneś używać 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); 

Jak to jest stary temat, myślę, że ta odpowiedź jest nieco za późno, ale może pomóc innym, ponieważ ten temat jest nadal w stanie googlable.

+0

To wygląda na znacznie wydajniejszą pamięć niż moje rozwiązanie, Domyślam się, że jedna kopia obrazu jest nadal w pamięci, ale tak naprawdę nie może tego uniknąć. –

Powiązane problemy