2012-02-18 101 views
31

我有一个读取图像,转换它们(大小,格式)并将它们写回的方法。这一直运行得很好,但现在我遇到了一些显然包含一些元数据(IPTC)的JPEG图像(来自新闻社)。转换这些图像时,颜色都是错误的。我的第一个猜测是,那些是CMYK图像,但它们不是。错误颜色的JPEG图像

问题必须来自阅读,因为无论我将图像转换为较小的JPEG还是PNG,都无关紧要,它看起来总是相同的。

起初,我用ImageIO.read()来读取图像。我现在通过ImageIO.getImageReadersByMIMEType()得到实际的ImageReader,并试图告诉读者通过设置ignoreMetadata参数ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata)忽略元数据,但没有成功。

然后我创建了一个没有元数据的图像版本(使用Fireworks)。该图像被正确转换。

我能找到,唯一的区别是,与不工作的形象价值读者的变量colorSpaceCode是,whilest与工作形象,价值。还有一个outColorSpaceCode这两个图像是。

由于source comment of the reader只说由setImageData本地代码回调设置。修改后的IJG + NIFTY颜色空间代码现在我真的被卡住了。所以任何帮助将不胜感激。

您可以通过here并点击下载获取原始图像(〜3 MB)。下面的左图显示了我从原始图像中获得的内容,右图显示了它应该是什么样子。

wrong colors correct colors (after removing metadata)

+0

我已经有这个问题,只要我记得。它发生在我遇到的大约0.1%的jpg文件中。例如:http://chan.sankakustatic.com/data/cd/81/cd81a9fa1305b9c1887ab1ac4904d166.jpg我还没有找到一个解决方案,但在面板中正确显示它们。我的猜测是这是Java的JPEG解析器中的一个错误。 – 2012-02-22 09:57:24

+0

可能的重叠:[用Java保存图片颜色改变](http://stackoverflow.com/questions/20789043/image-changes-color-when-saved-with-java) – 2013-12-26 18:25:46

回答

8

我找到了一个解决方案,现在,这样的作品,至少如果我得到的图像也是一个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。不幸的是,如果我写一个PNG图像,我仍然会得到错误的颜色(即使传递了元数据),但我可以忍受这一点。

0

似乎这里罚款:

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

那么,图片是我的结果转换,正确的是我得到的,如果我删除元数据。我应该发布原始的非工作图像,但它非常大(约3mb)。我会在某处上传它并添加一个问题链接。尽管感谢你的代码,我会用原始图像来尝试。 – Ridcully 2012-02-18 12:23:53

+0

*“但它非常大(约3mb)。”*请找到一个较小的休息时间。我无法承受使用需要3兆下载(一次或多次)代码的实验带宽。 – 2012-02-18 12:37:16

+0

我尝试将原始图像上传到imgur.com,但显然他们删除了元标签,因为在那里上传图像时,图像看起来没问题。我现在已经把图片上传到google-docs,从那里你可以下载原始图片和你的代码,我用错误的颜色获取它: - /(必须改变代码才能从本地文件读取而不是URL )。 – Ridcully 2012-02-18 12:43:52

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

我相信你如何检测不良JPEG的问题的答案可以在这里找到[http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4712797)和[here](http:// stackoverflow.com/questions/7676701/java-jpeg-converter-for-odd-image-types)。你有什么是没有JFIF标记的JPEG。所有其他图像加载器都假设在这种情况下数据是YCbCr,除了ImageIO,当通道1和2未被二次采样时,ImageIO假定它是RGB。因此,检查前4个字节是否为FF D8 FF E1,如果是,则检查通道1和2是否进行二次采样。这是你需要转换的情况。 – uckelman 2013-02-24 19:14:53

+0

您可以在[本答案](http://stackoverflow.com/questions/672916/how-to-get-image-height-and-width-using-java#2911772)中调整部分JPEG解码器以检测转换条件。 – uckelman 2013-02-24 20:33:45

4

我也有类似的疑难问题不得不使用:

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

代替

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

我有类似的问题,BufferedImage返回基于的演绎,如果有透明像素,对于大多数png/gif类型的文件,该像素将设置为true。但是,转换为JPEG时,此标志应设置为false。您可能需要编写一个方法,在该方法中正确处理转换。即:

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

否则,“marunish”overtone成为保存的结果。 :)


+13

如果没有代码显示如何处理转换,此答案是无用的。 – 2013-10-29 13:21:22

3

我遇到了这个问题,实际上我发现了一个第三方库为我处理这个问题。 https://github.com/haraldk/TwelveMonkeys

从字面上看,我所要做的就是将其包含在我的maven依赖项中,并且以怪异颜色出现的jpeg开始正常读取。我甚至不必更改一行代码。