Изображение 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 colors]()
![correct 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));
}
}