我有一个方法可以读取图像,转换它们(大小,格式)并将它们写回。这总是很好用,但现在我遇到了一些显然包含一些元数据(IPTC)的JPEG图像(来自新闻社)。转换这些图像时,颜色都是错误的。我的第一个猜测是,那些是CMYK图像,但它们不是。
问题必须来自阅读,因为无论我是将图像转换为较小的JPEG还是PNG都无关紧要,它看起来总是一样。
首先,我使用ImageIO.read()
来阅读图片。我现在通过ImageReader
获取实际的ImageIO.getImageReadersByMIMEType()
并尝试通过设置ignoreMetadata
的{{1}}参数来告诉读者忽略元数据,但没有成功。
然后我创建了一个没有元数据的图像版本(使用Fireworks)。该图像转换正确。
我能找到的唯一区别是,对于未工作的图像,读者变量ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata)
的值为 2 ,而对于工作图像,值为 3 即可。两张图片还有colorSpaceCode
2 。
由于source comment of the reader仅表示由setImageData本机代码回调设置。修改后的IJG + NIFTY颜色空间代码我现在真的被困住了。所以任何帮助都会非常感激。
您可以通过here并点击下载来获取原始图像(~3 MB)。下图左侧显示了我从原始图像中获得的内容,右侧显示了它应该是什么样子。
答案 0 :(得分:9)
我现在找到了一个解决方案,至少在我生成的图像也是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图像,我仍然会得到错误的颜色(即使传递元数据),但我可以忍受。
答案 1 :(得分:5)
我有类似的问题,如果有透明像素,返回的BufferedImage
是基于再现的,对于大多数png / gif类型的文件将设置为true。但是当转换为jpeg时,此标志应设置为false。您可能需要编写一个方法,正确处理转换。即:
public static BufferedImage toBufferedImage(Image image) {
...
}
否则“marunish”泛音成为保存的结果。 :)
答案 2 :(得分:4)
我遇到了类似的问题。我不得不使用:
Image image = java.awt.Toolkit.getDefaultToolkit().getImage(path);
而不是
Image image = javax.imageio.ImageIO.read(new File(path));
答案 3 :(得分:3)
我遇到了这个问题,我实际上找到了一个为我处理这个问题的第三方库。 https://github.com/haraldk/TwelveMonkeys
字面上,我所要做的就是在我的maven依赖项中包含这个,并且以奇怪的颜色出现的jpegs开始正常读取。我甚至不必改变一行代码。
答案 4 :(得分:2)
这是一种将“坏”图像转换为好图像的算法,但是,我还没有找到任何方法来自动检测图像是否会被渲染得很糟糕,所以它仍然没用。
如果有人找到方法来检测图像是否会变坏(除了眼球),请告诉我们。 (比如,我在哪里得到这个所谓的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());
}
答案 5 :(得分:0)
这里似乎很好:
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));
}
}
答案 6 :(得分:0)
当尝试将图像从字节数组转换为Base64时,我遇到了类似的问题。看来问题是由带有Alpha通道的图像引起的。当使用Alpha通道保存图像时,也会保存Alpha通道,并且一些用于读取图像的外部程序会将4个通道解释为CMYK。
通过删除BufferedImage的alpha通道找到了一个简单的解决方法。这可能很愚蠢,但对我确实有用。
//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);
答案 7 :(得分:0)
请注意,问题可能会发生在各个阶段:
ImageIO.write()
时)我最近遇到的一个问题是上传Greenshot创建的png
屏幕截图,使用ImageIO
阅读,缩放,然后使用ImageIO
作为jpeg
进行书写(您通常的缩略图过程)。
我在书写方面的解决方案:删除Alpha通道,以避免浏览器将图像解释为YMCK):
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);
}
}
答案 8 :(得分:0)
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));
}