CoreImage:CIImage写JPG正在改变颜色[macOS]

时间:2019-12-13 21:40:41

标签: swift macos colors jpeg core-image

使用CoreImage过滤照片,我发现保存为JPG文件将导致图像具有微妙但可见的蓝色调。在使用黑白图像的此示例中,直方图显示了已保存文件中颜色的移动方式。

---输入 Input Histogram

输出 [Output Histogram]直方图显示颜色层偏移

-MacOS“预览”应用显示的问题

我只能使用预览应用程序显示类似的结果。

  1. 在此处测试图像:https://i.stack.imgur.com/Y3f03.jpg
  2. 使用“预览”打开JPG图像。
  3. 以默认值(85%?)以外的其他任何“质量”格式导出为JPEG
  4. 打开导出的文件并查看直方图,可以看到我在应用程序中遇到的相同颜色偏移。

enter image description here

-自定义MacOS App中显示的问题

这里的代码尽可能简单,从照片创建CIImage并立即保存它而不执行任何过滤器。在此示例中,我选择0.61进行压缩,因为它产生的文件大小与原始文件类似。如果使用较高的压缩率,则失真似乎会更大,但是我找不到能消除它的任何值。

if let img = CIImage(contentsOf: url) {
   let dest = procFolder.url(named: "InOut.jpg")
   img.jpgWrite(url: dest)
}

extension CIImage {
    func jpgWrite(url: URL) {

        let prop: [NSBitmapImageRep.PropertyKey: Any] = [
            .compressionFactor: 0.61
        ]

        let bitmap = NSBitmapImageRep(ciImage: self)
        let data = bitmap.representation(using: NSBitmapImageRep.FileType.jpeg, properties: prop)

        do {
            try data?.write(to: url, options: .atomic)
        } catch {
            log.error(error)
        }
    }
}

更新1:使用@Frank Schlegel的答案保存JPG文件

JPG现在带有“颜色同步配置文件”,我可以(不科学地)跟踪肖像图像的性能提升约10%(对于风景图像则不然),这是很好的改进。但是,不幸的是,生成的文件仍然按照上面的直方图中所示的相同方式扭曲颜色。

extension CIImage {
   static let writeContext = CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!, options: [
        // using an extended working color space allows you to retain wide gamut information, e.g., if the input is in DisplayP3
        .workingColorSpace: CGColorSpace(name: CGColorSpace.extendedSRGB)!,
        .workingFormat: CIFormat.RGBAh // 16 bit color depth, needed in extended space
    ])

    func jpgWrite(url: URL) {
       // write the output in the same color space as the input; fallback to sRGB if it can't be determined
        let outputColorSpace = colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
        do {
            try CIImage.writeContext.writeJPEGRepresentation(of: self, to: url, colorSpace: outputColorSpace, options: [:])
        } catch {
        }
    }
}

问题:

如何打开B&W JPG作为CIImage,并重新保存JPG文件以避免颜色偏移?

2 个答案:

答案 0 :(得分:0)

这看起来像是色彩同步问题(正如Leo指出的那样)–更具体地说,是输入,处理和输出之间的色彩空间不匹配/误解。

致电NSBitmapImageRep(ciImage:)时,实际上发生了很多事情。系统实际上需要渲染您提供的CIImage以获得结果的位图数据。通过创建具有默认设置(特定于设备的设置)的CIContext,使用它来处理图像(应用了所有滤镜和转换),然后为结果提供原始位图数据,可以完成此操作。在此过程中,使用此API时,您无法控制发生多种颜色空间转换(而且看起来并没有达到您想要的结果)。由于这个原因,我不喜欢这些用于呈现CIImage的“便捷” API,我在SO上看到很多与它们相关的问题。

我建议您改用CIContextCIImage渲染为JPEG文件。这使您可以直接控制色彩空间以及更多:

let input = CIImage(contentsOf: url)

// ideally you create this context once and re-use it because it's an expensive object
let context = CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!, options: [
    // using an extended working color space allows you to retain wide gamut information, e.g., if the input is in DisplayP3
    .workingColorSpace: CGColorSpace(name: CGColorSpace.extendedSRGB)!,
    .workingFormat: CIFormat.RGBAh // 16 bit color depth, needed in extended space
])

// write the output in the same color space as the input; fallback to sRGB if it can't be determined
let outputColorSpace = input.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!

context.writeJPEGRepresentation(of: input, to: dest, colorSpace: outputColorSpace, options: [kCGImageDestinationLossyCompressionQuality: 0.61])

在使用此API时是否仍然出现差异,请告诉我。

答案 1 :(得分:0)

我从来没有发现这个问题的根本原因,因此没有我所寻求的“真正”解决方案。在与@Frank Schlegel进行讨论时,人们认为它是Apple的jpeg转换器的产物。当使用显示为单色但实际上包含少量颜色信息的测试文件时,问题肯定会更加明显。

对于我的应用程序,最简单的解决方法是确保源图像中没有颜色,因此在保存文件之前,我将饱和度降至0。

    let params = [
        "inputBrightness": brightness,  // -1...1, This filter calculates brightness by adding a bias value: color.rgb + vec3(brightness)
        "inputContrast": contrast,      // 0...2, this filter uses the following formula: (color.rgb - vec3(0.5)) * contrast + vec3(0.5)
        "inputSaturation": saturation   // 0...2
    ]
    image.applyingFilter("CIColorControls", parameters: params)