使用CoreImage过滤照片,我发现保存为JPG文件将导致图像具有微妙但可见的蓝色调。在使用黑白图像的此示例中,直方图显示了已保存文件中颜色的移动方式。
输出 []直方图显示颜色层偏移
-MacOS“预览”应用显示的问题
我只能使用预览应用程序显示类似的结果。
-自定义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文件以避免颜色偏移?
答案 0 :(得分:0)
这看起来像是色彩同步问题(正如Leo指出的那样)–更具体地说,是输入,处理和输出之间的色彩空间不匹配/误解。
致电NSBitmapImageRep(ciImage:)
时,实际上发生了很多事情。系统实际上需要渲染您提供的CIImage
以获得结果的位图数据。通过创建具有默认设置(特定于设备的设置)的CIContext
,使用它来处理图像(应用了所有滤镜和转换),然后为结果提供原始位图数据,可以完成此操作。在此过程中,使用此API时,您无法控制发生多种颜色空间转换(而且看起来并没有达到您想要的结果)。由于这个原因,我不喜欢这些用于呈现CIImage
的“便捷” API,我在SO上看到很多与它们相关的问题。
我建议您改用CIContext
将CIImage
渲染为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)