解决java imageio.read 图片 cmyk格式报错

报错情况

ImageIO.read(File file)报异常"Unsupported Image Type",是因为其内部读取文件的类JPEGImageReader只能读取RGB模式的图片,报这个错的时候可能因为图片格式是CMYK格式的。

解决方案

将CMYK模式的文件转换成RGB模式
转换中需要注意的是:CMYK属于四个通道数的模式,而RGB只有三个模式,也就是转换时会出现一定程度的色差,另外CMYK JPEG图像还有CMYK, Adobe CMYK模式的不同,所以最好用到合理的CMYK颜色配置文件(.icc文件)

转换代码如下:

/**
 * 
 */
package com.test;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
import org.apache.sanselan.formats.jpeg.segments.UnknownSegment;
import com.jeeplus.common.utils.StringUtils;

/**
 * @author emp
 *
 */
public class JpegReaderCopy {

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

  public static InputStream readImage(byte[] bytes) throws IOException, ImageReadException {
    int colorType = COLOR_TYPE_RGB;
    boolean hasAdobeMarker = false;
    ImageInputStream stream = ImageIO.createImageInputStream(new ByteArrayInputStream(bytes));
    Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
    while (iter.hasNext()) {
      ImageReader reader = iter.next();
      reader.setInput(stream);
      String format = reader.getFormatName();
      BufferedImage image;
      ICC_Profile profile = null;
      try {
        image = reader.read(0);
      } catch (IIOException e) {
        colorType = COLOR_TYPE_CMYK;
        JpegImageParser parser = new JpegImageParser();
        ByteSource byteSource = new ByteSourceArray(bytes);
        // 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;
          }
        }
        // checkAdobeMarker(bytes);
        profile = Sanselan.getICCProfile(bytes);
        WritableRaster raster = (WritableRaster) reader.readRaster(0, null);
        if (colorType == COLOR_TYPE_YCCK)
          convertYcckToCmyk(raster);
        if (hasAdobeMarker)
          convertInvertedColors(raster);
        image = convertCmykToRgb(raster, profile);
      }
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      if (StringUtils.isNotEmpty(format)) {
        ImageIO.write(image, format, os);
      } else {
        ImageIO.write(image, "jpg", os);
      }
      InputStream is = new ByteArrayInputStream(os.toByteArray());
      return is;
    }

    return new ByteArrayInputStream(bytes);
  }

  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 {
    System.out.println(cmykProfile);
    if (cmykProfile == null) {
      cmykProfile = ICC_Profile.getInstance(JpegReaderCopy.class.getResourceAsStream("/icc/ISOcoated_v2_300_eci.icc"));
    }
    System.out.println(cmykProfile);

    if (cmykProfile.getProfileClass() != ICC_Profile.CLASS_DISPLAY) {
      byte[] profileData = cmykProfile.getData();

      if (profileData[ICC_Profile.icHdrRenderingIntent] == ICC_Profile.icPerceptual) {
        intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first

        cmykProfile = ICC_Profile.getInstance(profileData);
      }
    }

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


static void intToBigEndian(int value, byte[] array, int index) {
  array[index]   = (byte) (value >> 24);
  array[index+1] = (byte) (value >> 16);
  array[index+2] = (byte) (value >>  8);
  array[index+3] = (byte) (value);
}

  public static void main(String[] args) throws ImageReadException, IOException {
    // BufferedImage image =
    // ;
    // ByteArrayOutputStream os = new ByteArrayOutputStream();
    // ImageIO.write(image, "png", os);
    InputStream is =
        new JpegReaderCopy().readImage(IOUtils.toByteArray(new FileInputStream(new File("C:\\Users\\emp\\Desktop\\test\\test123.jpg"))));
    // new ByteArrayInputStream(os.toByteArray());
    // InputStream is = new FileInputStream(new File("C:\\Users\\emp\\Desktop\\test\\test3.jpg"));
    byte[] buffer = new byte[is.available()];
    is.read(buffer);

    File targetFile = new File("C:\\Users\\emp\\Desktop\\convert123.jpg");
    OutputStream outStream = new FileOutputStream(targetFile);
    outStream.write(buffer);

  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值