java - quiere - Imagen JPEG con colores incorrectos
que quiere decir alt en html (6)
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.
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, utilicé ImageIO.read()
para leer la imagen. Ahora obtengo el ImageReader
actual a través de ImageIO.getImageReadersByMIMEType()
y traté de 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 descubrir es que, con la imagen que no funciona, el valor de la variable colorSpaceCode del lector es 2 , y la más baja con la imagen de trabajo, el valor es 3 . También hay un outColorSpaceCode
que es 2 para ambas imágenes.
Como el comentario fuente del lector solo dice Establecer por devolución de llamada de código nativo setImageData. 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á.
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 forma de detectar si una imagen se volverá mala (aparte de mirar fijamente), díganos. (por ejemplo, ¿dónde obtengo este valor denominado 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());
}
Encontré una solución ahora, eso funciona, al menos si mi imagen resultante también es JPEG: primero leo 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 haría algo de conversión (es decir, reduciría el tamaño) ... y al final volvería a escribir el resultado como una imagen JPEG. Aquí es más importante pasar los metadatos que obtuvimos de la imagen original a la nueva 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();
Desafortunadamente, si escribo una imagen PNG, sigo recibiendo los colores incorrectos (incluso si paso los metadatos), pero puedo vivir con eso.
Me encontré con este problema, y en realidad encontré una biblioteca de terceros que manejó 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 empezaron a leerse normalmente. Ni siquiera tuve que cambiar una línea de código.
Me parece bien aquí:
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));
}
}
Tuve problemas similares, la imagen BufferedImage
devuelta es una copia basada en si hay un píxel 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, ese sobretono "mariano" se convierte en el resultado guardado. :)
Tuve un problema similar. Tuve que usar:
Image image = java.awt.Toolkit.getDefaultToolkit().getImage(path);
en lugar de
Image image = javax.imageio.ImageIO.read(new File(path));