【问题标题】:In Java converting an image to sRGB makes the image too bright在 Java 中将图像转换为 sRGB 会使图像太亮
【发布时间】:2019-07-04 14:42:54
【问题描述】:

我有多个图像,其中嵌入了自定义配置文件,并希望将图像转换为 sRGB 以便将其提供给浏览器。我见过如下代码:

BufferedImage image = ImageIO.read(fileIn);
ColorSpace ics = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp cco = new ColorConvertOp(ics, null);
BufferedImage result = cco.filter(image, null);
ImageIO.write(result, "PNG", fileOut);

其中fileInfileOut 是分别代表输入文件和输出文件的文件对象。这在一定程度上有效。问题是生成的图像比原始图像更亮。如果我要在 Photoshop 中转换颜色空间,颜色会显示相同。事实上,如果我用 Photoshop 调出两张图片并截屏并采样颜色,它们是相同的。 Photoshop 做了什么而上面的代码不是,我能做些什么来纠正这个问题?

正在转换的图像有多种类型,包括 JPEG、PNG 和 TIFF。我尝试使用TwelveMonkeys 读取JPEG 和TIFF 图像,但我仍然得到相同的效果,但图像太亮了。当应用于最初没有嵌入配置文件的图像时,转换过程似乎最糟糕。

编辑:

我添加了一些示例图片来帮助解释问题。

  1. 此图像是其中嵌入了颜色配置文件的图像。在某些浏览器上查看,这一个和下一个之间不会有明显的区别,但在 Mac OSX 和 Windows 上的 Chrome 中查看,它目前看起来比它应该的要暗。这就是我的问题首先出现的地方。我需要将图像转换为可以在 Chrome 中正确显示的内容。
  2. 这是使用 ImageMagick 转换为 Adob​​e RGB 1998 颜色配置文件的图像,Chrome 似乎能够正确显示。
  3. 这是我使用上面的代码转换的图像,它看起来比它应该的要亮。

(请注意,上面的图片是在 imgur 上,所以为了使它们更大,只需删除文件名末尾的“t”,在文件扩展名之前。)

【问题讨论】:

  • 这是你必须在 Java 中做的事情吗?可以考虑在命令行中使用 image magick 吗?
  • 不可行。当我说多个时,我的意思是超过 20,000 张图像。这些图像用于印刷和数字产品。问题是打印支持颜色配置文件,但浏览器不可靠。 Chrome 在某种程度上似乎是我们的目标浏览器。自定义配置文件似乎无法正确加载。我知道有 jmagick,但如果可能的话,我不想使用命令行包装器。
  • 您能否提供带有自定义颜色配置文件的示例图像?
  • @MichaelHogenson 如果 Image Magick 一次只能处理一个图像,我想您可以编写一个相当简单的 bash/PowerShell 脚本来遍历目录中的图像。特别是如果这只是需要发生一次然后就会完成的事情。现在,如果您需要在应用程序代码中,那将是在代码中进行此操作的正当理由。
  • 是的,它会在应用程序代码中即时发生,具体取决于图像是用于数字产品还是打印。

标签: java image bufferedimage color-space color-profile


【解决方案1】:

这是我最初的解决方案,但我不喜欢使用 ImageMagick。我根据我最终坚持的解决方案创建了另一个答案。

我放弃并最终使用了im4java,它是图像魔法命令行工具的包装器。当我使用以下代码获取 BufferedImage 时,效果非常好。

IMOperation op = new IMOperation();
op.addImage(fileIn.getAbsolutePath());
op.profile(colorFileIn.getAbsolutePath());
op.addImage("png:-");

ConvertCmd cmd = new ConvertCmd();
Stream2BufferedImage s2b = new Stream2BufferedImage();
cmd.setOutputConsumer(s2b);
cmd.run(op);
BufferedImage image = s2b.getImage();

我还可以在需要时使用该库应用 CMYK 配置文件进行打印。如果 ColorConvertOp 正确地进行了转换,那就太好了,但至少现在,这是我的解决方案。为了与我的问题达到同等效果,在问题中实现相同效果的 im4java 代码是:

ConvertCmd cmd = new ConvertCmd();

IMOperation op = new IMOperation();
op.addImage(fileIn.getAbsolutePath());
op.profile(colorFileIn.getAbsolutePath());
op.addImage(fileOut.getAbsolutePath());

cmd.run(op);

其中colorFileIn.getAboslutePath() 是机器上sRGB 颜色配置文件的位置。由于 im4java 使用的是命令行,因此如何执行操作并不那么直接,但该库已详细解释 here。如问题中所述,我最初遇到图像魔法无法在我的 Mac 上工作的问题。我使用 brew 安装了它,但在 Mac 上你必须像 brew install imagemagick --with-little-cms 一样安装它。在那之后,魔术对我来说效果很好。

【讨论】:

    【解决方案2】:

    我找到了一个不需要 ImageMagick 的解决方案。基本上Java在加载图像时不尊重配置文件,因此如果有一个则需要加载。这是我为实现此目的所做的代码 sn-p:

    private BufferedImage loadBufferedImage(InputStream inputStream) throws IOException, BadElementException {
        byte[] imageBytes = IOUtils.toByteArray(inputStream);
        BufferedImage incorrectImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
    
        if (incorrectImage.getColorModel() instanceof ComponentColorModel) {
    
            // Java does not respect the color profile embedded in a component based image, so if there is a color
            // profile, detected using iText, then create a buffered image with the correct profile.
            Image iTextImage = Image.getInstance(imageBytes);
            com.itextpdf.text.pdf.ICC_Profile iTextProfile = iTextImage.getICCProfile();
    
            if (iTextProfile == null) {
                // If no profile is present than the image should be processed as is.
                return incorrectImage;
            } else {
                // If there is a profile present then create a buffered image with the profile embedded.
                byte[] profileData = iTextProfile.getData();
                ICC_Profile profile = ICC_Profile.getInstance(profileData);
                ICC_ColorSpace ics = new ICC_ColorSpace(profile);
    
                boolean hasAlpha = incorrectImage.getColorModel().hasAlpha();
                boolean isAlphaPremultiplied = incorrectImage.isAlphaPremultiplied();
                int transparency = incorrectImage.getTransparency();
                int transferType = DataBuffer.TYPE_BYTE;
                ComponentColorModel ccm = new ComponentColorModel(ics, hasAlpha, isAlphaPremultiplied, transparency, transferType);
                return new BufferedImage(ccm, incorrectImage.copyData(null), isAlphaPremultiplied, null);
            }
        }
        else if (incorrectImage.getColorModel() instanceof IndexColorModel) {
            return incorrectImage;
        }
        else {
            throw new UnsupportedEncodingException("Unsupported color model type.");
        }
    }
    

    这个答案确实使用了iText,它通常用于 PDF 创建和操作,但它恰好可以正确处理 ICC 配置文件,我已经在我的项目中依赖它,所以它恰好是一个更好的选择比 ImageMagick。

    问题中的代码结果如下:

    BufferedImage image = loadBufferedImage(new FileInputStream(fileIn));
    ColorSpace ics = ColorSpace.getInstance(ColorSpace.CS_sRGB);
    ColorConvertOp cco = new ColorConvertOp(ics, null);
    BufferedImage result = cco.filter(image, null);
    ImageIO.write(result, "PNG", fileOut);
    

    效果很好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      • 2016-03-29
      • 1970-01-01
      • 2011-03-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多