有很多次问过类似的问题.但是我仍然不明白为什么在用ICC_Profile转换图片后输出太暗.我尝试了很多配置文件:来自Adobe网站,以及图片本身.
在图像之前

图像之后

码
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("jpeg");
ImageReader reader = null;
while (readers.hasNext()){
reader = readers.next();
if (reader.canReadRaster()){
break;
}
}
// read
ImageInputStream ios = ImageIO.createImageInputStream(new FileInputStream(new File(myPic.jpg)));
reader.setInput(ios);
Raster r = reader.readRaster(0, null);
BufferedImage result = new BufferedImage(r.getWidth(), r.getHeight(), bufferedImage.TYPE_INT_RGB);
WritableRaster resultRaster = result.getRaster();
ICC_Profile iccProfile = ICC_Profile.getInstance(new File("profile_name.icc");
ColorSpace cs = new ICC_ColorSpace(iccProfile);
ColorConvertOp cmykToRgb = new ColorConvertOp(cs, result.getColorModel().getColorSpace(), null);
cmykToRgb.filter(r, resultRaster);
// write
ImageIo.write(resul, "jpg", new File("myPic.jpg"));
Run Code Online (Sandbox Code Playgroud)
转换图片后,我还需要做些什么吗?
Cod*_*odo 19
这个问题并不是全新的.但是由于我花了很多时间来解决这个问题,并提出了一个有效的解决方案,我想我会在这里发布.该解决方案需要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;
}
}
Run Code Online (Sandbox Code Playgroud)
代码首先尝试使用常规方法读取文件,该方法适用于RGB文件.如果失败,则会读取颜色模型的详细信息(配置文件,Adobe标记,Adobe变体).然后它读取原始像素数据(光栅)并进行所有必要的转换(YCCK到CMYK,反转颜色,CMYK到RGB).
我对我的解决方案不太满意.虽然颜色大多是好的,但是暗区域略微太亮,特别是黑色不是完全黑色.如果有人知道我可以改进什么,我会很高兴听到它.
更新:
我已经想出如何解决亮度问题.或者更确切地说:来自12monkeys-imageio项目的人们(参见这篇文章).它与颜色渲染意图有关.
修复是添加以下几行,这对我很有用.基本上,颜色配置文件被修改,因为似乎没有其他方法可以告诉ColorConvertOp类使用感知颜色渲染意图.
if (cmykProfile.getProfileClass() != ICC_Profile.CLASS_DISPLAY) {
byte[] profileData = cmykProfile.getData(); // Need to clone entire profile, due to a JDK 7 bug
if (profileData[ICC_Profile.icHdrRenderingIntent] == ICC_Profile.icPerceptual) {
intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first
cmykProfile = ICC_Profile.getInstance(profileData);
}
}
Run Code Online (Sandbox Code Playgroud)
...
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);
}
Run Code Online (Sandbox Code Playgroud)
正如我所说,我们的想法是将 CMYK 图片转换为 RGB,并在我的应用程序中使用它们。
但由于某种原因,ConvertOp 不执行任何 CMYK 到 RGB 的转换。它将 numBand 数量减少到 3,仅此而已。我决定尝试 CMYKtoRGB 算法。
即获取图像,识别其色彩空间并读取或转换它。
另一个问题是 Photoshop。这句话是我在网上找到的。
对于 adobe,它在元数据中包含 CMYK 配置文件,但随后将原始图像数据保存为反转的 YCbCrK 颜色。
最后我可以用下面的算法实现我的目标。到目前为止我还没有使用 icc_profiles,输出看起来有点暗。我得到了正确的 RGB 图像,看起来不错。
伪代码
BufferedImage result = null;
Raster r = reader.readRaster()
if (r.getNumBands != 4){
result = reader.read(0);
} else {
if (isPhotoshopYCCK(reader)){
result = YCCKtoCMYKtoRGB(r);
}else{
result = CMYKtoRGB(r);
}
}
private boolean isPhotoshopYCCK(reader){
// read IIOMetadata from reader and according to
// http://download.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html decide which ColorSpace is used
// or maybe there is another way to do it
int transform = ... // 2 or 0 or something else
return transform;
}
Run Code Online (Sandbox Code Playgroud)
我没有任何意义显示 YCCKtoCMYKtoRGB 或 CMYKtoRGB 算法。在互联网上很容易找到。