Java讀取圖片 cmyk轉rgb

背景

使用java處理圖片的時候,比如用ImageIo.read(圖片)時,是不能處理CMYK的圖片的,會報錯,因此,我們需要將CMYK轉化爲RGB模式,並且排除轉換時的色差問題。

代碼

package com.e369.elc.framework.image;

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.File;
import java.io.IOException;
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.sanselan.ImageReadException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceFile;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
import org.apache.sanselan.formats.jpeg.segments.UnknownSegment;

public class Cmyk2RgbUtil {

  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 BufferedImage readImage(String fileName) throws IOException, ImageReadException {
    int colorType = COLOR_TYPE_RGB;
    boolean hasAdobeMarker = false;
    File file = new File(fileName);
    ImageInputStream stream = ImageIO.createImageInputStream(file);
    Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
    if (iter == null || !iter.hasNext()) {
      throw new RuntimeException("CMYK 轉 RGB報錯,未找到image reader");
    }
    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;
      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;
        }
      }
      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;
  }

  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(Cmyk2RgbUtil.class.getResourceAsStream("/ISOcoated_v2_300_eci.icc"));
    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 {
    File outputfile = new File("C:\\Users\\emp\\Desktop\\fa1.jpg");
    ImageIO.write(readImage("C:\\Users\\emp\\Desktop\\1.jpg"), "jpg", outputfile);
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章