web-dev-qa-db-fra.com

Image JPEG avec de mauvaises couleurs

J'ai une méthode qui lit les images, les convertit (taille, format) et les réécrit. Cela a toujours très bien fonctionné, mais maintenant je suis tombé sur des images JPEG (d'une agence de presse) contenant évidemment des métadonnées (IPTC). Lors de la conversion de ces images, les couleurs sont toutes fausses. Ma première hypothèse était que ce sont des images CMJN, mais ce n’est pas le cas.

Le problème doit venir de la lecture, car si je convertis l'image en JPEG plus petit ou en PNG, ça a toujours la même apparence.

Au début, j'ai utilisé ImageIO.read() pour lire l'image. Je reçois maintenant la ImageReader réelle via ImageIO.getImageReadersByMIMEType() et j'ai essayé de dire au lecteur d’ignorer les métadonnées en définissant le paramètre ignoreMetadata de ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) mais sans succès.

Ensuite, j'ai créé une version de l'image sans les métadonnées (à l'aide de Fireworks). Cette image est convertie correctement.

La seule différence que je pouvais découvrir, c’est que, avec l’image qui ne fonctionne pas, la valeur de la variable du lecteur colorSpaceCode est 2 , quelle que soit l’image utilisée, la valeur est 3 . Il y a aussi une outColorSpaceCode qui est 2 pour les deux images.

Comme le commentaire source du lecteur ne dit que Défini par callback de code natif setImageData. Un code d'espace colorimétrique IJG + NIFTY modifié Je suis vraiment bloqué maintenant. Donc, toute aide serait très appréciée.

Vous pouvez obtenir une image originale (~ 3 Mo) en allant ici et en cliquant sur télécharger. L'image de gauche ci-dessous montre ce que je reçois de l'image d'origine, la droite montre à quoi elle devrait ressembler.

wrong colorscorrect colors (after removing metadata)

31
Ridcully

J'ai trouvé une solution maintenant, qui fonctionne, du moins si mon image résultante est aussi un JPEG: Tout d'abord, je lis l'image (à partir d'octet array imageData), et surtout, je lis aussi les métadonnées.

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

Maintenant, je ferais quelques conversions (c'est-à-dire une taille réduite) ... et enfin, j'écrirais le résultat sous forme d'image JPEG. Ici, il est très important de transmettre les métadonnées que nous avons obtenues de l’image originale à la nouvelle 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();

Malheureusement, si j'écrivais une image au format PNG, j'obtiendrais quand même les mauvaises couleurs (même si je transmettais les métadonnées), mais je peux vivre avec cela.

9
Ridcully

J'ai eu des problèmes similaires, la BufferedImage renvoyée est un rendu basé sur un pixel transparent, qui sera défini sur true pour la plupart des fichiers de type png/gif. Mais lors de la conversion en jpeg, cet indicateur doit être défini sur false. Vous devez éventuellement écrire une méthode dans laquelle la conversion est correctement gérée. c'est à dire.:

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

Sinon, cette annotation "marunish" devient le résultat enregistré. :)


5
wbudic

J'ai eu un problème similaire.J'ai dû utiliser:

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

au lieu de

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

Je me heurtais à ce problème et j'ai en fait trouvé une bibliothèque tierce qui s'en occupait pour moi. https://github.com/haraldk/TwelveMonkeys

Tout ce que je devais littéralement faire, c’était d’inclure cela dans mes dépendances maven et les jpeg qui sortaient dans des couleurs étranges ont commencé à être lus normalement. Je n'ai même pas eu besoin de changer une ligne de code.

3
James Fiala

Voici un algorithme pour transformer la "mauvaise" image en une bonne, cependant, je n’ai trouvé aucun moyen de détecter automatiquement si une image sera mal rendue, elle est donc toujours inutile.

Si quelqu'un trouve un moyen de détecter si une image va être rendue mauvaise (autre que la vue), veuillez nous en informer. (comme, où puis-je obtenir cette prétendue valeur 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());
    }
2
Mark Jeronimus

Semble bien ici:

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
Andrew Thompson

J'ai eu un problème similaire en essayant de convertir une image d'un tableau d'octets en Base64. Il semble que le problème soit causé par des images avec un canal alpha. Lorsque vous enregistrez une image avec un canal alpha, celui-ci est également enregistré et certains programmes externes utilisés pour lire l'image interprètent les 4 canaux en tant que CMYK.

Vous avez trouvé une solution de contournement simple en supprimant le canal alpha de BufferedImage. C'est peut-être stupide mais ça a fonctionné pour moi.

//Read the image from a byte array
BufferedImage bImage = ImageIO.read(new ByteArrayInputStream(byteArray));

//Get the height and width of the image
int width = bImage.getWidth();
int height = bImage.getHeight();

//Get the pixels of the image to an int array 
int [] pixels=bImage.getRGB(0, 0,width,height,null,0,width);

//Create a new buffered image without an alpha channel. (TYPE_INT_RGB)
BufferedImage copy = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);

//Set the pixels of the original image to the new image
copy.setRGB(0, 0,width,height,pixels,0,width);
0
Kasun