使用 Java 把 RGB 转为 CMYK | 酸辣土豆丝 iCS
Excerpt
使用 JAI 把 RGB 转为 CMYK.
00 前言
最近接到了一个需求,要把 RGB 的图片转为 CMYK 的颜色模式,用于印刷。
网上对于 RGB 转为 CMYK 的资料较少,Java 也没有内置的方法。主流方法有两种,一种是手搓强转,另一种则是借助 ICC 文件来转换。
该需求对于分辨率、物理尺寸有一定的要求,且原途径就是通过 PS 处理的,正好用同样的 ICC 文件转换,能达到相对一致的颜色要求。
01 色彩空间转换
1.1 关键概念
RGB 模式是以色光三原色为基础建立的色彩模式,红、绿、蓝,是电脑、手机、投影仪、电视等屏幕显示的最佳颜色模式。
CMYK(青色、品红色、黄色、黑色)是印刷材料的色彩空间。CMY 是 3 种印刷油墨名称的首字母:青色 Cyan、洋红色 Magenta、黄色 Yellow,由于目前制造工艺还不能造出高纯度的油墨,CMY 相加实际得到的是深灰色或深褐色,故加入纯黑色。
ICC Profile 色彩特性文件,是一组用来描述色彩空间的特性的数据集合,因由国际色彩联盟(ICC)主持制定其规范而得名。RGB 转为 CMYK 就是把 RGB 值转为 CMYK 值。
像素(Pixel)是指构成图片的小色点。 分辨率(单位 DPI)是指每英寸(Inch)上的像素数量,++1 英寸 = 2.54 厘米++。 像素相同时,分辨率越高则像素密度越大,实际打印尺寸越小,图像也越细腻。
1.2 转换步骤
首先,引入 JAI(Java Advanced Imaging),最新版 1.1.3,这个库好几年没更新了:
1
| <dependency> <groupId>javax.media.jai</groupId> <artifactId>com.springsource.javax.media.jai.codec</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>com.github.jai-imageio</groupId> <artifactId>jai-imageio-core</artifactId> <version>1.4.0</version> </dependency>
|
然后,加载 ICC 文件,这里选择 JapanColor2001Coated.icc
(按需选择即可),安装 PS 就有,在 C:\Program Files (x86)\Common Files\Adobe\Color\Profiles\Recommended
目录下,路径不一样找不到的搜一下就可以。
1
| private static void readICCProfile() throws IOException { if (cmykColorSpace == null) { synchronized (CMYKUtil.class) { if (cmykColorSpace == null) { try (InputStream inputStream = CMYKUtil.class.getClassLoader().getResourceAsStream("icc/JapanColor2001Coated.icc")) { ICC_Profile cmykProfile = ICC_Profile.getInstance(inputStream);
|
需要注意的是,这里我是从 resources
直接加载的配置文件,需要额外配置 Maven,否则编译时 ICC 文件会被 Maven 修改,报错 Invalid ICC Profile Data
:
1
| <!-- pom.xml --> <build> <resources> <!-- 保留其他文件的处理方式 --> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>***.properties</include> <include>***.icc</include> </includes> </resource> </resources> <!-- ... --> </build>
|
如果不想修改项目配置,只能通过读取外部文件,把 ICC 文件放到指定目录去读取即可。
然后, 准备需要的编码参数,用来调整图片:
1
| private static TIFFEncodeParam prepareEncodeParams(int dpi) { TIFFEncodeParam params = new TIFFEncodeParam();
|
- 由于需求是要打印固定的尺寸的图片,但是图片的分辨率不固定,所以每次都要计算 DPI。
- 其他特殊需求, 需要自己去查去摸索了,参考
BaselineTIFFTagSet
内的代码,内部有些许说明。
到这里,可以封装一个转换方法了:
1
| public static ByteArrayOutputStream rgb2Cmyk(BufferedImage rgbImage, int dpi) throws IOException {
|
最后,可以把转换好的图片写入文件(或者其他具体操作):
1
| public static void writeImage(ByteArrayOutputStream outputStream, String filepath) { try (FileImageOutputStream fo = new FileImageOutputStream(new File(filepath))) { fo.write(outputStream.toByteArray()); } catch (IOException e) { log.error("写入文件失败, {}, {}", filepath, e.getMessage(), e); } }
|
封装好后,使用步骤很简单了:
1
| @Test public void test_rgb2Cmyk() throws IOException {
|
03 总结
要把 RGB 图片转为 CMYK 等其他色彩空间的图片,使用 ICC 是比较方便的。但是 JAI 许久未更新了,有些功能还不支持,好几个参数试了没效果,如果有其他更详细的需求,未必能够用它来实现。
总结一下转换的步骤:
- 引入 JAI 依赖,读取 ICC 配置文件(把 ICC 放到项目 Resources 下的需要注意过滤)。
- 为转换准备相应的参数。
- 初始化源色彩空间(ColorConvertOp op),转换原图为目标格式(op.filter)。
- 使用准备的参数编码转换后的图像。
- 对转换好多图片进行自定义处理。
注意,同时压缩太多图片的话,建议压缩(优先使用 TIFFEncodeParam 配置),否则很容易内存不足。
完整代码
1
| package fun.springx.image.utils; import cn.hutool.core.util.StrUtil; import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.TIFFEncodeParam; import com.sun.media.jai.codec.TIFFField; import lombok.extern.slf4j.Slf4j; import javax.imageio.stream.FileImageOutputStream; 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.io.*; @Slf4j public class CMYKUtil { private static final double INCH_2_CM = 2.54d; static volatile ColorSpace cmykColorSpace = null; private static void readICCProfile() throws IOException { if (cmykColorSpace == null) { synchronized (CMYKUtil.class) { if (cmykColorSpace == null) { try (InputStream inputStream = CMYKUtil.class.getClassLoader().getResourceAsStream("icc/JapanColor2001Coated.icc")) { ICC_Profile cmykProfile = ICC_Profile.getInstance(inputStream); cmykColorSpace = new ICC_ColorSpace(cmykProfile); } } } } } private static TIFFEncodeParam prepareEncodeParams(int dpi) { TIFFEncodeParam params = new TIFFEncodeParam();
|
References
[1] 艺海拾贝Design. RGB 和 CMYK 色彩模式的区别与用途. https://www.shejidaren.com/rgb-cmyk-qu-bie-yu-yong-tu.html .
[2] SMILE嘻嘻. Maven 编译后资源文件发生改变问题. https://blog.csdn.net/zt_16KK/article/details/72459160 , 2017-05-18.
[3] whyMyHelloWorld. java 多张jpg合成tif后避免tif文件过大的方法. https://blog.csdn.net/sinat_29048381/article/details/80006951 , 2018-04-19.