Изображение JPEG с неправильными цветами

У меня есть метод, который читает изображения, преобразует их (размер, формат) и записывает их обратно. Это всегда срабатывало очень хорошо, но теперь я сталкивался с некоторыми изображениями в формате JPEG (из агентства печати), которые, очевидно, содержат некоторые метаданные (IPTC). При преобразовании этих изображений цвета не соответствуют действительности. Мое первое предположение заключалось в том, что это изображения CMYK, но это не так.

Проблема должна исходить из чтения, потому что неважно, преобразую ли я изображение в меньший JPEG или PNG, он всегда выглядит одинаково.

Сначала я использовал ImageIO.read() для чтения изображения. Теперь я получаю фактический ImageReader через ImageIO.getImageReadersByMIMEType() и пытаюсь сказать читателю игнорировать метаданные, установив параметр ignoreMetadata ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata), но не получив успеха.

Затем я создал версию изображения без метаданных (используя Fireworks). Это изображение преобразуется правильно.

Единственное различие, которое я смог выяснить, заключается в том, что с неработающим изображением значение переменной читателя colorSpaceCode составляет 2, в то время как рабочий образ имеет значение 3. Там также outColorSpaceCode, который 2 для обоих изображений.

Как сообщает , говорит только Set by setImageData. Измененный код цвета IJG + NIFTY Я действительно застрял сейчас. Поэтому любая помощь была бы высоко оценена.

Вы можете получить исходное изображение (~ 3 МБ), перейдя здесь и нажав кнопку "скачать". Левое изображение ниже показывает, что я получаю от исходного изображения, справа показывает, как оно должно выглядеть.

wrong colorscorrect colors (after removing metadata)

Ответ 1

Теперь я нашел решение, которое работает, по крайней мере, если итоговое изображение также является JPEG: Сначала я прочитал изображение (из байтового массива imageData), и самое главное, я также прочитал метаданные.

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

Теперь я бы сделал некоторые преобразования (т.е. уменьшить размер)... и, наконец, я бы записал результат обратно как изображение в формате JPEG. Здесь наиболее важно передать метаданные, полученные нами из исходного изображения, в новый 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();

К сожалению, если бы я написал PNG-образ, я все равно получаю неправильные цвета (даже если передаю метаданные), но я могу жить с этим.

Ответ 2

У меня были аналогичные проблемы, возвращаемый BufferedImage - это преобразование, основанное на прозрачном пикселе, который будет установлен для большинства файлов png/gif. Но при преобразовании в jpeg этот флаг должен быть установлен на false. Возможно, вам нужно написать метод, в котором преобразование должным образом обрабатывается. то есть:.

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

В противном случае "marunish" overtone станет сохраненным результатом.:)


Ответ 3

У меня была аналогичная проблема. Мне пришлось использовать:

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

вместо

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

Ответ 4

Я столкнулся с этой проблемой, и на самом деле я нашел стороннюю библиотеку, которая обрабатывала это для меня. https://github.com/haraldk/TwelveMonkeys

Буквально все, что мне нужно было сделать, это включить это в мои зависимости maven, и jpegs, которые выходили в странных цветах, начали нормально читать. Мне даже не пришлось менять строку кода.

Ответ 5

Здесь алгоритм преобразования "плохого" изображения в хороший, однако, я не нашел способа автоматического определения того, будет ли изображение плохо отображаться, поэтому оно все еще бесполезно.

Если кто-нибудь найдет способ определить, будет ли изображение плохо отображаться (кроме глазного яблока), сообщите нам. (например, где я могу получить это так называемое значение 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());
    }

Ответ 6

Кажется, здесь хорошо:

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