48 votes

Impossible de lire une image JPEG en utilisant ImageIO.read(File file)

J'ai des problèmes pour lire ce fichier JPEG à l'aide de ImageIO.read(File file) - une exception est levée avec le message "Unsupported Image Type".

J'ai essayé d'autres images JPEG, et elles semblent fonctionner correctement.

La seule différence que j'ai pu repérer est que ce fichier semble inclure une vignette - est-ce que cela est connu pour causer des problèmes avec ImageIO.read() ?

Troublesome image

EDITAR:

Ajouté l'image résultante :

Strange colors

0 votes

Il serait utile de voir la trace de la pile de l'exception.

8 votes

Veuillez restaurer les images !

53voto

haraldK Points 5751

Vieux poste, mais pour référence future :

Inspiré par cette question et les liens trouvés ici, j'ai écrit un plugin JPEGImageReader pour ImageIO qui supporte les modèles de couleur CMYK (à la fois avec le modèle de couleur original, ou implicitement converti en RGB à la lecture). Le lecteur effectue également une conversion de couleur correcte, en utilisant le profil ICC intégré dans le flux JPEG, contrairement aux autres solutions mentionnées ici.

Il s'agit d'un simple Java qui ne nécessite pas de JAI. Le code source et les distributions binaires sont disponibles gratuitement à l'adresse suivante github.com/haraldk/TwelveMonkeys et est couvert par une licence de type BSD.

Une fois installé, il vous permet de lire les JPEG CMYK en utilisant les fonctions suivantes ImageIO.read(...) comme ça :

File cmykJPEGFile = new File(/*path*/);
BufferedImage image = ImageIO.read(cmykJPEGFile);

C'est-à-dire que dans la plupart des cas, il n'est pas nécessaire de modifier votre code.

1 votes

Merci. Je souhaitais quelque chose comme ça. Avez-vous un readme / docs ? :) ou dois-je simplement vérifier les tests ? Merci encore

4 votes

Désolé, la documentation est rare pour le moment. Cependant, c'est un plugin ImageIO, donc si vous voulez juste lire un JPEG CMYK, faites ce qui suit : Construisez les JARs en utilisant Maven, placez-les dans le classpath et ImageIO.read(cmykJPEGFile) devrait fonctionner. N'hésitez pas à demander, s'il y a quelque chose de spécifique que vous aimeriez faire :-)

1 votes

Cool, joli plugin que vous avez là.

39voto

medopal Points 5070

Le "modèle de couleur" de votre image est CMYK, JPEGImageReader (la classe interne qui lit votre fichier) ne lit que le modèle de couleur RGB.

Si vous insistez pour lire des images CMYK, vous devrez les convertir, essayez ce code.

UPDATE

Lire une image CMYK en RGB BufferedImage.

    File f = new File("/path/imagefile.jpg");

    //Find a suitable ImageReader
    Iterator readers = ImageIO.getImageReadersByFormatName("JPEG");
    ImageReader reader = null;
    while(readers.hasNext()) {
        reader = (ImageReader)readers.next();
        if(reader.canReadRaster()) {
            break;
        }
    }

    //Stream the image file (the original CMYK image)
    ImageInputStream input =   ImageIO.createImageInputStream(f); 
    reader.setInput(input); 

    //Read the image raster
    Raster raster = reader.readRaster(0, null); 

    //Create a new RGB image
    BufferedImage bi = new BufferedImage(raster.getWidth(), raster.getHeight(), 
    BufferedImage.TYPE_4BYTE_ABGR); 

    //Fill the new image with the old raster
    bi.getRaster().setRect(raster);

MISE À JOUR - Mars 2015 - Ajout d'images de simulation

Les images originales ont été retirées de la boîte de dépôt de l'OP. J'ajoute donc de nouvelles images (pas les originales) qui simulent le problème qui se produisait avec elles.

Première image Voici à quoi ressemble une image RVB normale.

Image RGB

Deuxième image est la façon dont la même image apparaîtra dans le modèle de couleur CMYK.

Vous ne pouvez pas réellement voir à quoi cela ressemble sur le web car l'hôte le convertira en RVB. Pour voir exactement à quoi cela ressemble, prenez l'image RVB et passez-la dans un convertisseur RVB vers CMJN.

Troisième image Voici à quoi ressemblera l'image CMYK lorsqu'elle sera lue puis écrite en utilisant Java ImageIO.

Image CMYK read through Java RGB

Le problème qui s'est produit avec OP est qu'ils avaient quelque chose comme l'image 2 qui lève une exception lorsque vous essayez de la lire.

0 votes

Excellent, je vais l'essayer. Cela fonctionnera-t-il aussi pour les images RVB, ou dois-je détecter le type d'image d'une manière ou d'une autre ?

0 votes

Vous trouverez de nombreuses façons de détecter le modèle de couleur, ma préférée est d'utiliser JPEGImageReader, s'il jette Unsupported Image Type exception, alors il s'agit très probablement de CMYK.

1 votes

Cela fonctionne assez bien, mais les couleurs sont mélangées. Voir la nouvelle image que j'ai jointe à la question. Avez-vous des conseils à ce sujet ? Merci !

18voto

Codo Points 28685

Je suis un peu en retard à la fête. Mais cela vaut probablement la peine que je poste ma réponse car aucune des réponses ne résout vraiment le problème.

La solution nécessite Sanselan (ou Apache Commons Imaging comme on l'appelle maintenant) et elle requiert un profil de couleur CMYK raisonnable (fichier .icc). Vous pouvez obtenir ce dernier auprès d'Adobe ou d'eci.org.

Le problème de base est que Java ne peut lire que les fichiers JPEG en RVB. Si vous avez un fichier CMYK, vous devez faire la distinction entre CMYK normal, Adobe CMYK (avec des valeurs inversées, c'est-à-dire 255 pour pas d'encre et 0 pour un maximum d'encre) et Adobe CYYK (une variante avec des couleurs inversées également).

public class JpegReader {

    public static final int COLOR_TYPE_RGB = 1;
    public static final int COLOR_TYPE_CMYK = 2;
    public static final int COLOR_TYPE_YCCK = 3;

    private int colorType = COLOR_TYPE_RGB;
    private boolean hasAdobeMarker = false;

    public BufferedImage readImage(File file) throws IOException, ImageReadException {
        colorType = COLOR_TYPE_RGB;
        hasAdobeMarker = false;

        ImageInputStream stream = ImageIO.createImageInputStream(file);
        Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
        while (iter.hasNext()) {
            ImageReader reader = iter.next();
            reader.setInput(stream);

            BufferedImage image;
            ICC_Profile profile = null;
            try {
                image = reader.read(0);
            } catch (IIOException e) {
                colorType = COLOR_TYPE_CMYK;
                checkAdobeMarker(file);
                profile = Sanselan.getICCProfile(file);
                WritableRaster raster = (WritableRaster) reader.readRaster(0, null);
                if (colorType == COLOR_TYPE_YCCK)
                    convertYcckToCmyk(raster);
                if (hasAdobeMarker)
                    convertInvertedColors(raster);
                image = convertCmykToRgb(raster, profile);
            }

            return image;
        }

        return null;
    }

    public void checkAdobeMarker(File file) throws IOException, ImageReadException {
        JpegImageParser parser = new JpegImageParser();
        ByteSource byteSource = new ByteSourceFile(file);
        @SuppressWarnings("rawtypes")
        ArrayList segments = parser.readSegments(byteSource, new int[] { 0xffee }, true);
        if (segments != null && segments.size() >= 1) {
            UnknownSegment app14Segment = (UnknownSegment) segments.get(0);
            byte[] data = app14Segment.bytes;
            if (data.length >= 12 && data[0] == 'A' && data[1] == 'd' && data[2] == 'o' && data[3] == 'b' && data[4] == 'e')
            {
                hasAdobeMarker = true;
                int transform = app14Segment.bytes[11] & 0xff;
                if (transform == 2)
                    colorType = COLOR_TYPE_YCCK;
            }
        }
    }

    public static void convertYcckToCmyk(WritableRaster raster) {
        int height = raster.getHeight();
        int width = raster.getWidth();
        int stride = width * 4;
        int[] pixelRow = new int[stride];
        for (int h = 0; h < height; h++) {
            raster.getPixels(0, h, width, 1, pixelRow);

            for (int x = 0; x < stride; x += 4) {
                int y = pixelRow[x];
                int cb = pixelRow[x + 1];
                int cr = pixelRow[x + 2];

                int c = (int) (y + 1.402 * cr - 178.956);
                int m = (int) (y - 0.34414 * cb - 0.71414 * cr + 135.95984);
                y = (int) (y + 1.772 * cb - 226.316);

                if (c < 0) c = 0; else if (c > 255) c = 255;
                if (m < 0) m = 0; else if (m > 255) m = 255;
                if (y < 0) y = 0; else if (y > 255) y = 255;

                pixelRow[x] = 255 - c;
                pixelRow[x + 1] = 255 - m;
                pixelRow[x + 2] = 255 - y;
            }

            raster.setPixels(0, h, width, 1, pixelRow);
        }
    }

    public static void convertInvertedColors(WritableRaster raster) {
        int height = raster.getHeight();
        int width = raster.getWidth();
        int stride = width * 4;
        int[] pixelRow = new int[stride];
        for (int h = 0; h < height; h++) {
            raster.getPixels(0, h, width, 1, pixelRow);
            for (int x = 0; x < stride; x++)
                pixelRow[x] = 255 - pixelRow[x];
            raster.setPixels(0, h, width, 1, pixelRow);
        }
    }

    public static BufferedImage convertCmykToRgb(Raster cmykRaster, ICC_Profile cmykProfile) throws IOException {
        if (cmykProfile == null)
            cmykProfile = ICC_Profile.getInstance(JpegReader.class.getResourceAsStream("/ISOcoated_v2_300_eci.icc"));
        ICC_ColorSpace cmykCS = new ICC_ColorSpace(cmykProfile);
        BufferedImage rgbImage = new BufferedImage(cmykRaster.getWidth(), cmykRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
        WritableRaster rgbRaster = rgbImage.getRaster();
        ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace();
        ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null);
        cmykToRgb.filter(cmykRaster, rgbRaster);
        return rgbImage;
    }
}

Le code essaie d'abord de lire le fichier en utilisant la méthode habituelle, qui fonctionne pour les fichiers RVB. S'il échoue, il lit les détails du modèle de couleur (profil, marqueur Adobe, variante Adobe). Ensuite, il lit les données brutes des pixels (raster) et effectue toutes les conversions nécessaires (YCCK vers CMYK, couleurs inversées, CMYK vers RGB).

Je ne suis pas tout à fait satisfait de ma solution. Si les couleurs sont généralement bonnes, les zones sombres sont légèrement trop lumineuses, en particulier le noir n'est pas complètement noir. Si quelqu'un sait ce que je pourrais améliorer, je serais heureux de l'entendre.

0 votes

C'est la meilleure réponse que j'ai trouvée à ce sujet, mais ne voulez-vous pas fermer l'ImageInputStream dans un bloc final ?

0 votes

Merci pour l'extrait de code, qui fonctionne bien. (ISOcoated_v2_300_eci.icc peut être trouvé ici : humburg.de/?page=4 )

7voto

Peter Points 41

ImageIO.read() ->

File filePath = new File("C:\\Users\\chang\\Desktop\\05036877.jpg");
com.sun.image.codec.jpeg.JPEGImageDecoder jpegDecoder =  JPEGCodec.createJPEGDecoder (new FileInputStream(filePath));

BufferedImage image = jpegDecoder.decodeAsBufferedImage();

4 votes

Extrait de la documentation de l'API : Note that the classes in the com.sun.image.codec.jpeg package are not part of the core Java APIs. They are a part of Sun's JDK and JRE distributions. Although other licensees may choose to distribute these classes, developers cannot depend on their availability in non-Sun implementations. We expect that equivalent functionality will eventually be available in a core API or standard extension.

0voto

raymi Points 1085

Vous pouvez également consulter ce lien : http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4799903

Il y a un commentaire qui montre comment convertir le CMYK en RGB.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X