2012-02-18 25 views
31

Tengo un método que lee imágenes, las convierte (tamaño, formato) y las vuelve a escribir. Esto siempre funcionó muy bien, pero ahora me he encontrado con algunas imágenes JPEG (de una agencia de prensa) que obviamente contienen algunos metadatos (IPTC). Al convertir esas imágenes, los colores son todos incorrectos. Mi primera suposición fue que esas son imágenes CMYK pero no lo son.Imagen JPEG con colores incorrectos

El problema debe provenir de la lectura, ya que no importa si convierto la imagen a JPEG o PNG más pequeños, siempre tiene el mismo aspecto.

Al principio, usé ImageIO.read() para leer la imagen. Ahora obtengo el ImageReader real a través de ImageIO.getImageReadersByMIMEType() e intenté decirle al lector que ignore los metadatos configurando el parámetro ignoreMetadata de ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata), pero no tuvo éxito.

Luego creé una versión de la imagen sin los metadatos (usando Fireworks). Esa imagen se convierte correctamente.

La única diferencia que pude averiguar, es decir, que con la imagen no-trabajar el valor de la variable del lector colorSpaceCode es , whilest con la imagen de trabajo, el valor es . También hay un outColorSpaceCode que es para ambas imágenes.

Como source comment of the reader solo dice Establecer por setImageData devolución de llamada de código nativo. Un código de espacio de color IJG + NIFTY modificado Estoy realmente atrapado ahora. Entonces cualquier ayuda sería muy apreciada.

Puede obtener la imagen original (~ 3 MB) yendo here y haciendo clic en descargar. La imagen de la izquierda a continuación muestra lo que obtengo de la imagen original, la derecha muestra cómo se verá.

wrong colors correct colors (after removing metadata)

+0

He tenido este problema por el tiempo que puedo recordar. Sucede en aproximadamente el 0.1% de los archivos jpg que encuentro. Por ejemplo: http://chan.sankakustatic.com/data/cd/81/cd81a9fa1305b9c1887ab1ac4904d166.jpg Aún no he encontrado una solución para mostrarlos correctamente en un panel. Supongo que es un error en el analizador JPEG de Java. –

+0

Posible superposición: [La imagen cambia de color cuando se guarda con Java] (http://stackoverflow.com/questions/20789043/image-changes-color-when-saved-with-java) –

Respuesta

8

he encontrado una solución ahora, que funciona, al menos si mi imagen resultante es también un JPEG: Primero lea la imagen (de byte array imageData), y lo más importante, también leo los metadatos.

InputStream is = new BufferedInputStream(new ByteArrayInputStream(imageData)); 
Image src = null; 
Iterator<ImageReader> it = ImageIO.getImageReadersByMIMEType("image/jpeg"); 
ImageReader reader = it.next(); 
ImageInputStream iis = ImageIO.createImageInputStream(is); 
reader.setInput(iis, false, false); 
src = reader.read(0); 
IIOMetadata imageMetadata = reader.getImageMetadata(0); 

Ahora me gustaría hacer alguna conversión (es decir, disminuye de tamaño) ... y al final me gustaría escribir el resultado de nuevo como una imagen JPEG. Aquí es más importante pasar los metadatos que obtuvimos de la imagen original al nuevo IIOImage.

Iterator<ImageWriter> iter = ImageIO.getImageWritersByMIMEType("image/jpeg"); 
ImageWriter writer = iter.next(); 
ImageWriteParam iwp = writer.getDefaultWriteParam(); 
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
iwp.setCompressionQuality(jpegQuality); 
ImageOutputStream imgOut = new MemoryCacheImageOutputStream(out); 
writer.setOutput(imgOut); 
IIOImage image = new IIOImage(destImage, null, imageMetadata); 
writer.write(null, image, iwp); 
writer.dispose(); 

Por desgracia, si me gustaría escribir una imagen PNG, sigo teniendo los colores incorrectos (incluso si pasa los metadatos), pero puedo vivir con eso.

0

parece estar bien aquí:

TestImage result

import java.awt.image.BufferedImage; 
import java.net.URL; 
import java.io.File; 
import javax.imageio.ImageIO; 

import javax.swing.*; 

class TestImage { 

    public static void main(String[] args) throws Exception { 
     URL url = new URL("http://i.stack.imgur.com/6vy74.jpg"); 
     BufferedImage origImg = ImageIO.read(url); 

     JOptionPane.showMessageDialog(null,new JLabel(new ImageIcon(origImg))); 

     File newFile = new File("new.png"); 
     ImageIO.write(origImg, "png", newFile); 
     BufferedImage newImg = ImageIO.read(newFile); 

     JOptionPane.showMessageDialog(null,new JLabel(
      "New", 
      new ImageIcon(newImg), 
      SwingConstants.LEFT)); 
    } 
} 
+0

Bueno, las imágenes son el resultado de mi conversiones, la correcta es la que obtengo si elimino los metadatos. Debería haber publicado la imagen original que no funciona, pero es muy grande (aproximadamente 3mb). Lo subiré a alguna parte y agregaré un enlace a la pregunta. Gracias por su código, lo intentaré con la imagen original. – Ridcully

+0

* "pero es muy grande (aproximadamente 3mb)." * Encuentra uno más pequeño que se rompe. No puedo permitirme el ancho de banda de experimentar con un código que necesita una descarga de 3 megas (una o varias veces). –

+0

He intentado subir la imagen original a imgur.com, pero obviamente eliminan las metaetiquetas, porque con la imagen cargada allí, la imagen se ve bien. Ahora he subido la imagen a google-docs, desde allí puedes descargar la imagen original y con tu código, lo obtengo con los colores incorrectos: -/(tuve que cambiar a un código para leer desde el archivo local en lugar de la URL)) – Ridcully

2

Aquí hay un algoritmo para transformar la imagen "mala" en una buena, sin embargo, no he encontrado ninguna manera de detectar automáticamente si una imagen se procesará mal, por lo que sigue siendo inútil.

Si alguien encuentra una manera de detectar si una imagen se volverá mala (aparte de mirar fijamente), díganos. (como, ¿dónde obtengo este valor llamado colorSpaceCode ?!)

private static void fixBadJPEG(BufferedImage img) 
    { 
     int[] ary = new int[img.getWidth() * img.getHeight()]; 
     img.getRGB(0, 0, img.getWidth(), img.getHeight(), ary, 0, img.getWidth()); 
     for (int i = ary.length - 1; i >= 0; i--) 
     { 
      int y = ary[i] >> 16 & 0xFF; // Y 
      int b = (ary[i] >> 8 & 0xFF) - 128; // Pb 
      int r = (ary[i] & 0xFF) - 128; // Pr 

      int g = (y << 8) + -88 * b + -183 * r >> 8; // 
      b = (y << 8) + 454 * b >> 8; 
      r = (y << 8) + 359 * r >> 8; 

      if (r > 255) 
       r = 255; 
      else if (r < 0) r = 0; 
      if (g > 255) 
       g = 255; 
      else if (g < 0) g = 0; 
      if (b > 255) 
       b = 255; 
      else if (b < 0) b = 0; 

      ary[i] = 0xFF000000 | (r << 8 | g) << 8 | b; 
     } 
     img.setRGB(0, 0, img.getWidth(), img.getHeight(), ary, 0, img.getWidth()); 
    } 
+7

Creo que la respuesta a su pregunta de cómo detectar los archivos JPEG incorrectos se encuentra [aquí] (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4712797) y [aquí] (http: // stackoverflow.com/questions/7676701/java-jpeg-converter-for-odd-image-types). Lo que tienes es un JPEG sin marcador JFIF. El resto de los cargadores de imágenes asumen que los datos son YCbCr en ese caso, a excepción de ImageIO, que asume que es RGB cuando los canales 1 y 2 no son submuestreados. Por lo tanto, verifique si los primeros 4 bytes son FF D8 FF E1, y si es así, si los canales 1 y 2 están submuestreados. Ese es el caso donde necesitas convertir. – uckelman

+0

Puede adaptar el descodificador JPEG parcial en [esta respuesta] (http://stackoverflow.com/questions/672916/how-to-get-image-height-and-width-using-java#2911772) para detectar la conversión condición. – uckelman

4

tuve un problem.I similares tuvo que usar:

Image image = java.awt.Toolkit.getDefaultToolkit().getImage(path); 

en lugar de

Image image = javax.imageio.ImageIO.read(new File(path)); 
5

que había problemas similares, el BufferedImage devuelto es una interpretación basada si hay pixel transparente, que se establecerá como verdadero para la mayoría de los tipos de archivos png/gif. Pero al convertir a jpeg, esta bandera debe establecerse en falso. Es posible que necesite escribir un método, donde la conversión se maneja adecuadamente. es decir:

public static BufferedImage toBufferedImage(Image image) { 
... 
} 

De lo contrario, el sobretono "marunish" se convierte en el resultado guardado. :)


+13

Esta respuesta es inútil sin un código que muestre cómo manejar la conversión. –

3

Me encontré con este problema, y ​​en realidad encontré una biblioteca de terceros que se encargó de esto para mí. https://github.com/haraldk/TwelveMonkeys

Literalmente todo lo que tenía que hacer era incluir esto en mis dependencias maven y los jpegs que salían en colores extraños comenzaban a leerse normalmente. Ni siquiera tuve que cambiar una línea de código.

Cuestiones relacionadas