imageIO kullanarak, genellikle bir görüntü dosyasını dönüştürme sorunu yaşıyorum ve üzerine yazdıktan sonra, tüm EXIF verilerini kaybeder. İlk önce ayıklamadan, önbelleğe almadan ve sonra yeniden sıfırlamaya gerek kalmadan onu korumanın bir yolu var mı?EXIF verilerini silmeden görüntü yönetme
cevap
Çözümüm, ImageIO, Imgscalr ve Apache commons-görüntüleme özelliklerinin bir kombinasyonunu kullanıyorum. Yazıtın meta verilerle okunmasını birleştiren tek bir kütüphane olmadığı için, bu muhtemelen bellek kullanımı üzerinde oldukça fazladır; İyileştirmeler hoş geldiniz.
ImageReader reader = ImageIO.getImageReadersBySuffix("jpg").next();
(böyle okuyucu varsa da kontrol etmek isteyebilirsiniz):
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;
}
}
Çok teşekkürler. Güzel çalıştı. Tek şey, görünüşte, 'IImageMetadata', Apache Commons Imaging –
için mevcut repoda 'ImageMetadata' olarak isimlendiriliyor. @Rigeborod'dan biraz daha verimli görünen diğer çözümü de kontrol edin. –
ImageIO bu işlevi kendisi var, ama bunun yerine ImageIO.read size ImageReader kullanmanız gerekecektir . Eğer meta kaydedebilirsiniz Şimdi
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
Ve sonra görüntüyü okuyun: Eğer yeni bir resim kaydetmek istediğiniz
BufferedImage bi = reader.read(0);
Sonra girişini ayarlamak gerekir ImageWriter'i kullanmalısınız:
// 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);
Eski bir konu olarak, bu cevabın biraz geç olduğunu düşünüyorum, ancak bu konu hala devam etmekte olduğu için başkalarına yardımcı olabilir.
Bu, benim çözümümden çok daha fazla bellek verimli görünüyor. Sanırım görüntünün bir kopyası hala bellekte, ancak bunu gerçekten engelleyemez. –
Başka bir yerde saklayın, sonra eski bir exif meta ile yeni resmin üzerine yazın. http://www.screaming-penguin.com/node/7485 –
Bu tam olarak – preslavrachev
'dan kaçınmak istediğim şey, meta kopyalamanın problemi nedir? İşte başka bir örnek http://nucleussystems.com/blog/java-copy-exif-data –