34 Stimmen

JPEG-Bild mit falschen Farben

Ich habe eine Methode, die Bilder liest, sie konvertiert (Größe, Format) und zurückschreibt. Das hat immer sehr gut funktioniert, aber jetzt bin ich auf einige JPEG-Bilder gestoßen (von einer Presseagentur), die offensichtlich einige Metadaten (IPTC) enthalten. Bei der Konvertierung dieser Bilder sind die Farben völlig falsch. Meine erste Vermutung war, dass es sich um CMYK-Bilder handelt, aber das ist nicht der Fall.

Das Problem muss beim Lesen liegen, denn egal, ob ich das Bild in ein kleineres JPEG oder ein PNG konvertiere, es sieht immer gleich aus.

Am Anfang habe ich ImageIO.read() um das Bild zu lesen. Ich erhalte jetzt die aktuelle ImageReader vía ImageIO.getImageReadersByMIMEType() und versucht, dem Leser mitzuteilen, dass er Metadaten ignorieren soll, indem er die ignoreMetadata Parameter von ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) hatte aber keinen Erfolg.

Dann habe ich eine Version des Bildes ohne die Metadaten erstellt (mit Fireworks). Dieses Bild wurde korrekt konvertiert.

Der einzige Unterschied, den ich feststellen konnte, ist, dass bei dem nicht funktionierenden Bild der Wert der Variablen des Lesers colorSpaceCode es 2 , während beim Arbeitsbild der Wert 3 . Außerdem gibt es eine outColorSpaceCode das ist 2 für beide Bilder.

Da die Quelle: Kommentar des Lesers sagt nur Wird durch den nativen Code-Callback setImageData gesetzt. Ein geänderter IJG+NIFTY-Farbraum-Code Ich stecke jetzt wirklich fest. Jede Hilfe wäre also sehr willkommen.

Sie können das Originalbild (~3 MB) über folgende Adresse abrufen aquí und klicken Sie auf Herunterladen. Das linke Bild unten zeigt, was ich vom Originalbild erhalte, das rechte zeigt, wie es aussehen sollte.

wrong colors correct colors (after removing metadata)

3voto

Reto Höhener Punkte 4609

Beachten Sie, dass Probleme in verschiedenen Phasen auftreten können:

  • Lesen
  • Schreiben (bei Übergabe von ARGB anstelle von RGB an ImageIO.write() )
  • Rendering (Fotobetrachtungsprogramm des Betriebssystems, Browser usw.)

Die letzte Begegnung mit diesem Problem war beim Hochladen von png screenshots erstellt von Greenshot , Lesen mit ImageIO , Skalierung und dann Schreiben mit ImageIO como jpeg (Ihr typisches Miniaturbildverfahren).

Meine Lösung für die Schriftseite: Alphakanal entfernen, damit die Browser das Bild nicht als YMCK interpretieren):

public static byte[] imageToBytes(BufferedImage image, String format) {
    try {
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      BufferedImage imageToWrite = image;
      if(format.toLowerCase().endsWith("jpg") || format.toLowerCase().endsWith("jpeg")) {
        if(image.getType() != BufferedImage.TYPE_INT_RGB) {
          // most incoming BufferedImage that went through some ImageTools operation are ARGB
          // saving ARGB to jpeg will not fail, but e.g. browser will interpret the 4 channel images as CMYK color or something
          // need to convert to RGB 3-channel before saving as JPG
          // https://stackoverflow.com/a/46460009/1124509
          // https://stackoverflow.com/questions/9340569/jpeg-image-with-wrong-colors

          // if the reading already produces wrong colors, also try installing twelvemonkeys image plugin (for better jpeg reading support)
          // https://github.com/haraldk/TwelveMonkeys
          // ImageIO.scanForPlugins();
          // GT.toList(ImageIO.getImageReadersByFormatName("jpeg")).forEach(i -> System.out.println(i));
          int w = image.getWidth();
          int h = image.getHeight();
          imageToWrite = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
          int[] rgb = image.getRGB(0, 0, w, h, null, 0, w);
          imageToWrite.setRGB(0, 0, w, h, rgb, 0, w);
        }
      }
      ImageIO.write(imageToWrite, format, byteArrayOutputStream);
      byte[] bytes = byteArrayOutputStream.toByteArray();
      return bytes;
    }
    catch(Exception e) {
      throw new RuntimeException(e);
    }
  }

2voto

Mark Jeronimus Punkte 8750

Hier ist ein Algorithmus, der das "schlechte" Bild in ein gutes umwandelt. Allerdings habe ich keine Möglichkeit gefunden, automatisch zu erkennen, ob ein Bild schlecht gerendert wird, so dass er immer noch nutzlos ist.

Wenn jemand einen Weg findet, um zu erkennen, ob ein Bild schlecht gerendert wird (abgesehen von der Betrachtung mit dem Auge), sagen Sie es uns bitte. (wie, wo bekomme ich diese so genannte colorSpaceCode Wert?!)

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

0voto

Andrew Thompson Punkte 166383

Hier scheint alles in Ordnung zu sein:

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

0voto

LiNKeR Punkte 797
public static void write_jpg_image(BufferedImage bad_image,String image_name) throws IOException {
        BufferedImage good_image=new BufferedImage(bad_image.getWidth(),bad_image.getHeight(),BufferedImage.TYPE_INT_RGB);
        Graphics2D B2G=good_image.createGraphics();
        B2G.drawImage(bad_image,0,0,null);
        B2G.dispose();
        ImageIO.write(good_image, "jpg", new File(image_name));
    }

-1voto

Wenske Punkte 1

Das Problem tritt auf, wenn das ursprüngliche jpg in CMYK-Farbe gespeichert wurde. Färben Sie es als RGB-Farbbild in z. B. Paint um.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X