상세 컨텐츠

본문 제목

java CMYK RGB 변환 (java CMYK to RGB conversion, convert, ISOcoated_v2_300_eci.icc)

프로그램 언어/Java

by husks 2015. 9. 2. 16:53

본문

반응형

 

 

자바로 이미지 파일을 처리하는 업무를 진행하였습니다.

 

 

 

이번에 작업하면서 알게 된 사항인데 이미지 방식에 CMYK와 RGB방식이 있다는것을 알게 되었습니다.

 

 

 

CMYK 방식은 인쇄물에 주로 쓰이는 방식이라고 합니다. ㅋㅋ

 

 

 

인터넷에 CMYK RGB로 검색하시면 다른 분들이 잘 설명 해주십니다.

 

 

 

뭐 검색을 통해 들어 오셨다면 이미 이 문제로 고생하시다 방문하셨으니 CMYK 방식에 대해 더 설명드릴 필요 없이 바로 소스를 적어 보겠습니다.

 

 

 

일단 CMYK방식 을 읽을수 있는 Class를 작성합니다.

 

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 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);
			} finally {
				stream.close(); //원본에는 close해주는 부분이 없어서 추가해 줬음, 위치상으로 애매한데 리턴 전이라 그냥 닫아줌
			}

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

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

 

에러가 발생 할껍니다.

 

그러면 jar를 추가해서 에러를 안 보이도록 하시죠

 

sanselan-0.97-incubator.jar
다운로드

 

위 jar파일을 받아서 라이브러리에 추가해주세요.

 

그리고 소스에서 ISOcoated_v2_300_eci.icc 파일도 사용(135라인)하고 있으니 해당 파일도 넣어주세요. 저는 JpegReader.java 파일과 같은 위치에 넣어주었습니다.

 

ISOcoated_v2_300_eci.icc
다운로드

 

해당 클래스를 사용해서 이미지를 변경할 main이 있는 Class를 작성합시다.

 

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.apache.sanselan.ImageReadException;
public class ConvertCmyk{
	public static void main(String[] args){
		String filename = "C:/tmp/Channel_digital_image_CMYK_color.jpg";
		JpegReader jpegReader = new JpegReader(); //위에서 만든 Class
		BufferedImage bufferedImage = null;
		try {
			bufferedImage = jpegReader.readImage(new File(filename));
			File outputfile = new File(filename);
			ImageIO.write(bufferedImage, "jpg", outputfile);//동일한 명으로 이미지를 변경한다. 복사본을 남기려면 outputfile의 파일명을 변경 			 		
		} catch (ImageReadException e) {
			e.printStackTrace(); 		
		} catch (IOException e) {
			e.printStackTrace(); 		
		}     	             
	}      
}

 

해당 ConvertCmyk 을 실행하면 동일한 명으로 RGB형식의 이미지로 변경 된 것을 확인 하실 수 있습니다.

 

소스 작성하면서 테스트한 이미지도 첨부합니다.

 

[CMYK]

 

[RGB]

 

 

해당 소스의 출처는 http://stackoverflow.com/questions/3123574/how-to-convert-from-cmyk-to-rgb-in-java-correctly 이며 일부 수정한 부분이 있습니다.

 

출처에는 jar 파일도 없고 ISOcoated_v2_300_eci.icc 파일도 없어서 구하느라 검색 좀 했네요 ㅋㅋ

 

※ 변환시 약간의 이미지 색상의 변화가 있습니다. 서로 다른 방식으로 인한 차이 입니다.

 

 

반응형

관련글 더보기

댓글 영역