JPEG изображение с неправильными цветами - PullRequest
31 голосов
/ 18 февраля 2012

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

Поскольку исходный комментарий читателя говорит только Устанавливается обратным вызовом собственного кода setImageData.Модифицированный код цветового пространства IJG + NIFTY Я действительно застрял сейчас.Поэтому любая помощь будет принята с благодарностью.

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

wrong colors correct colors (after removing metadata)

Ответы [ 7 ]

9 голосов
/ 18 февраля 2012

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

5 голосов
/ 18 сентября 2012

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

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

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


4 голосов
/ 18 июня 2012

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

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

вместо

Image image = javax.imageio.ImageIO.read(new File(path));
3 голосов
/ 18 февраля 2016

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

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

2 голосов
/ 22 февраля 2012

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

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

У меня была похожая проблема при попытке преобразовать изображение из байтового массива в Base64.Похоже, что проблема вызвана изображениями с альфа-каналом.При сохранении изображения с альфа-каналом также сохраняется альфа-канал, и некоторые внешние программы, используемые для чтения изображения, интерпретируют 4 канала как CMYK.BufferedImage.Это может быть глупо, но это сработало для меня.

//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 голосов
/ 18 февраля 2012

Здесь все в порядке:

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