2010-03-09 341 views
37

我在使用ImageIO.read(文件文件)读取这一个JPEG文件时遇到问题 - 它会通过消息“Unsupported Image Type”引发异常。无法使用ImageIO.read(文件文件)读取JPEG图像

我已经尝试过其他JPEG图像,并且它们似乎工作正常。

我已经能够发现的唯一区别是这个文件似乎包括一个缩略图 - 是否已知会导致ImageIO.read()问题?

Troublesome image

编辑:

加入所得到的图像:

Strange colors

+0

从异常中查看堆栈跟踪会很有用。 – simonlord 2010-03-09 11:36:44

+8

请恢复图像! – math 2013-11-22 08:26:01

回答

34

您的图片 “色彩模式” 为CMYK,JPEGImageReader(内部类读取文件)只读取RGB颜色模型。

如果你坚持阅读CMYK图像,那么你将需要转换它们,试试这个代码。

UPDATE

阅读CMYK图像转换为RGB的BufferedImage。

File f = new File("/path/imagefile.jpg"); 

    //Find a suitable ImageReader 
    Iterator readers = ImageIO.getImageReadersByFormatName("JPEG"); 
    ImageReader reader = null; 
    while(readers.hasNext()) { 
     reader = (ImageReader)readers.next(); 
     if(reader.canReadRaster()) { 
      break; 
     } 
    } 

    //Stream the image file (the original CMYK image) 
    ImageInputStream input = ImageIO.createImageInputStream(f); 
    reader.setInput(input); 

    //Read the image raster 
    Raster raster = reader.readRaster(0, null); 

    //Create a new RGB image 
    BufferedImage bi = new BufferedImage(raster.getWidth(), raster.getHeight(), 
    BufferedImage.TYPE_4BYTE_ABGR); 

    //Fill the new image with the old raster 
    bi.getRaster().setRect(raster); 

更新 - 2015年3月 - 增加模拟图像

原始图像进行从OP的投寄箱中删除。因此,我添加了模拟正在发生的问题的新图像(不是原件)。

第一张图像是正常的RGB图像的样子。

Image RGB

2图像是相同的图像将如何看起来像在CMYK色彩模式。

你实际上看不到它在网络上的外观,因为它将被主机转换为RGB。要查看它的外观,请使用RGB图像并通过RGB到CMYK转换器运行。

第三张图片是CMYK图像在读取时如何看起来像使用Java ImageIO写入时的样子。

Image CMYK read through Java RGB

,将其与OP发生的问题是,他们有这样的事情图像2,当您尝试阅读它会抛出异常。

+0

非常好,我会试试看。它是否也适用于RGB图像,或者我需要以某种方式检测类型? – Malakim 2010-03-09 12:40:44

+0

你会发现许多方法来检测颜色模型,我的首选是使用JPEGImageReader,如果它抛出'不支持的图像类型'异常,那么它最可能的CMYK。 – medopal 2010-03-09 12:47:18

+1

这个效果不错,但是,颜色变得混乱起来。查看我附加到问题的新图像。你有什么建议吗? 谢谢! – Malakim 2010-03-09 18:30:25

5

我发现 https://stackoverflow.com/questions/22409...在这里为好,这其中做了伟大的颜色转换

并结合既要得到这样的:

private BufferedImage convertCMYK2RGB(BufferedImage image) throws IOException{ 
    log.info("Converting a CYMK image to RGB"); 
    //Create a new RGB image 
    BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), 
    BufferedImage.TYPE_3BYTE_BGR); 
    // then do a funky color convert 
    ColorConvertOp op = new ColorConvertOp(null); 
    op.filter(image, rgbImage); 
    return rgbImage; 
} 
+1

这是我发现的唯一答案,它解决了JPEG上带有几个问题的绿色色调问题。 – Phil 2016-08-08 19:12:47

6

ImageIO.read() - >

File filePath = new File("C:\\Users\\chang\\Desktop\\05036877.jpg"); 
com.sun.image.codec.jpeg.JPEGImageDecoder jpegDecoder = JPEGCodec.createJPEGDecoder (new FileInputStream(filePath)); 

BufferedImage image = jpegDecoder.decodeAsBufferedImage(); 
+4

从API文档:请注意,com.sun.image.codec.jpeg包中的类不是核心Java API的一部分。它们是Sun JDK和JRE发行版的一部分。虽然其他许可证持有者可能选择分发这些类,但开发人员不能依赖于非Sun实施中的可用性。我们期望等效功能最终将在核心API或标准扩展中可用。 – Omertron 2012-09-17 12:41:17

18

我晚会有点晚了。但可能仍然值得我发布我的答案,因为没有任何答案真正解决了问题。

该解决方案需要Sanselan(或现在称为Apache Commons Imaging),它需要合理的CMYK颜色配置文件(.icc文件)。你可以从Adobe或eci.org获得更新的。

基本问题是,Java - 开箱即用 - 只能读取RGB中的JPEG文件。如果您有CMYK文件,则需要区分常规CMYK,Adobe CMYK(具有反转值,即255表示无墨,0表示最大墨)和Adobe CYYK(某些反转色的变体)。

public class JpegReader { 

    public static final int COLOR_TYPE_RGB = 1; 
    public static final int COLOR_TYPE_CMYK = 2; 
    public static final int COLOR_TYPE_YCCK = 3; 

    private int colorType = COLOR_TYPE_RGB; 
    private boolean hasAdobeMarker = false; 

    public BufferedImage readImage(File file) throws IOException, ImageReadException { 
     colorType = COLOR_TYPE_RGB; 
     hasAdobeMarker = false; 

     ImageInputStream stream = ImageIO.createImageInputStream(file); 
     Iterator<ImageReader> iter = ImageIO.getImageReaders(stream); 
     while (iter.hasNext()) { 
      ImageReader reader = iter.next(); 
      reader.setInput(stream); 

      BufferedImage image; 
      ICC_Profile profile = null; 
      try { 
       image = reader.read(0); 
      } catch (IIOException e) { 
       colorType = COLOR_TYPE_CMYK; 
       checkAdobeMarker(file); 
       profile = Sanselan.getICCProfile(file); 
       WritableRaster raster = (WritableRaster) reader.readRaster(0, null); 
       if (colorType == COLOR_TYPE_YCCK) 
        convertYcckToCmyk(raster); 
       if (hasAdobeMarker) 
        convertInvertedColors(raster); 
       image = convertCmykToRgb(raster, profile); 
      } 

      return image; 
     } 

     return null; 
    } 

    public void checkAdobeMarker(File file) throws IOException, ImageReadException { 
     JpegImageParser parser = new JpegImageParser(); 
     ByteSource byteSource = new ByteSourceFile(file); 
     @SuppressWarnings("rawtypes") 
     ArrayList segments = parser.readSegments(byteSource, new int[] { 0xffee }, true); 
     if (segments != null && segments.size() >= 1) { 
      UnknownSegment app14Segment = (UnknownSegment) segments.get(0); 
      byte[] data = app14Segment.bytes; 
      if (data.length >= 12 && data[0] == 'A' && data[1] == 'd' && data[2] == 'o' && data[3] == 'b' && data[4] == 'e') 
      { 
       hasAdobeMarker = true; 
       int transform = app14Segment.bytes[11] & 0xff; 
       if (transform == 2) 
        colorType = COLOR_TYPE_YCCK; 
      } 
     } 
    } 

    public static void convertYcckToCmyk(WritableRaster raster) { 
     int height = raster.getHeight(); 
     int width = raster.getWidth(); 
     int stride = width * 4; 
     int[] pixelRow = new int[stride]; 
     for (int h = 0; h < height; h++) { 
      raster.getPixels(0, h, width, 1, pixelRow); 

      for (int x = 0; x < stride; x += 4) { 
       int y = pixelRow[x]; 
       int cb = pixelRow[x + 1]; 
       int cr = pixelRow[x + 2]; 

       int c = (int) (y + 1.402 * cr - 178.956); 
       int m = (int) (y - 0.34414 * cb - 0.71414 * cr + 135.95984); 
       y = (int) (y + 1.772 * cb - 226.316); 

       if (c < 0) c = 0; else if (c > 255) c = 255; 
       if (m < 0) m = 0; else if (m > 255) m = 255; 
       if (y < 0) y = 0; else if (y > 255) y = 255; 

       pixelRow[x] = 255 - c; 
       pixelRow[x + 1] = 255 - m; 
       pixelRow[x + 2] = 255 - y; 
      } 

      raster.setPixels(0, h, width, 1, pixelRow); 
     } 
    } 

    public static void convertInvertedColors(WritableRaster raster) { 
     int height = raster.getHeight(); 
     int width = raster.getWidth(); 
     int stride = width * 4; 
     int[] pixelRow = new int[stride]; 
     for (int h = 0; h < height; h++) { 
      raster.getPixels(0, h, width, 1, pixelRow); 
      for (int x = 0; x < stride; x++) 
       pixelRow[x] = 255 - pixelRow[x]; 
      raster.setPixels(0, h, width, 1, pixelRow); 
     } 
    } 

    public static BufferedImage convertCmykToRgb(Raster cmykRaster, ICC_Profile cmykProfile) throws IOException { 
     if (cmykProfile == null) 
      cmykProfile = ICC_Profile.getInstance(JpegReader.class.getResourceAsStream("/ISOcoated_v2_300_eci.icc")); 
     ICC_ColorSpace cmykCS = new ICC_ColorSpace(cmykProfile); 
     BufferedImage rgbImage = new BufferedImage(cmykRaster.getWidth(), cmykRaster.getHeight(), BufferedImage.TYPE_INT_RGB); 
     WritableRaster rgbRaster = rgbImage.getRaster(); 
     ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace(); 
     ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null); 
     cmykToRgb.filter(cmykRaster, rgbRaster); 
     return rgbImage; 
    } 
} 

该代码首先尝试使用常规方法读取文件,该方法适用于RGB文件。如果失败,它会读取颜色模型的详细信息(配置文件,Adobe标记,Adobe变体)。然后它读取原始像素数据(光栅)并进行所有必要的转换(YCCK到CMYK,反转色彩,CMYK到RGB)。

我对我的解决方案并不满意。虽然颜色大多是好的,但黑色区域稍微太亮,特别是黑色不完全黑色。如果有人知道我可以改进什么,我很乐意听到它。

+0

这是我在此发现的最佳答案,但是您不想在finally块中关闭ImageInputStream吗? – Amalgovinus 2015-11-13 23:34:55

+0

感谢您的代码片段,工作正常。 (ISOcoated_v2_300_eci.icc可以在这里找到:http://www.humburg.de/?page=4) – user2198875 2017-01-05 16:29:04

40

旧的职位,但以供将来参考:

这个问题,在这里找到的链接的启发,我写了一个ImageIO的插件JPEGImageReader支持CMYK颜色模型(均与原来的颜色模式,或隐式转换为读取RGB)。读者还可以使用嵌入JPEG流中的ICC配置文件进行适当的颜色转换,与此处提到的其他解决方案相反。

这是普通的Java,不需要JAI。源代码和二进制发行版可在github.com/haraldk/TwelveMonkeys免费获得,并且包含BSD样式的许可证。

一旦你安装了它,它可以读取使用ImageIO.read(...)这样的CMYK JPEG文件:

File cmykJPEGFile = new File(/*path*/); 
BufferedImage image = ImageIO.read(cmykJPEGFile); 

即:在大多数情况下,它没有必要修改代码。

+0

谢谢!我希望有这样的事情。你有自述文件/文档吗? :)或者我应该只是检查测试?再次感谢 – 2013-04-26 21:20:31

+4

对不起,目前文档很少。但是,它是一个ImageIO插件,因此如果您只想读取CMYK JPEG,请执行以下操作:使用Maven构建JAR,放在classpath中,ImageIO.read(cmykJPEGFile)应该可以工作。随意问,如果有什么具体的你想要做的。 :-) – haraldK 2013-05-08 14:21:14

+1

很酷,不错的插件,你在那里。 – medopal 2014-04-24 14:03:48

0

我解决了这个问题。 只需要添加此依赖关系。我可以通过ImageIO读取CMYK图像。 TwelveMonkeys

ImageIO.read(new URL("http://img3.tianyancha.com/api/9b80a61183787909e719c77fd0f78103.png"))