MTKTextureLoader使图像饱和 [英] MTKTextureLoader saturates image

查看:261
本文介绍了MTKTextureLoader使图像饱和的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用MTKTextureLoader加载CGImage作为纹理.这是原始图片

I am trying to use a MTKTextureLoader to load a CGImage as a texture. Here is the original image

但是,在将CGImage转换为MTLTexture并将纹理转换回CGImage之后,它看起来很恐怖,像这样:

However after I convert that CGImage into a MTLTexture and that texture back to a CGImage it looks horrible, like this:

这是代码中正在发生的事情.

Here is sorta what is going on in code.

图片已作为CGImage加载(我已经检查过了,该图片的确具有完整的视觉质量)

The image is loaded in as a CGImage (I have checked and that image does appear to have the full visual quality)

我有一个功能view(),可以通过在CALayer中使用它来查看NSImage,如下所示:

I have a function view() that allows me to view a NSImage by using it in a CALayer like so:

 func view() {
    .....
    imageView!.layer = CALayer()
    imageView!.layer!.contentsGravity = kCAGravityResizeAspectFill
    imageView!.layer!.contents = img
    imageView!.wantsLayer = true

所以我做了以下

let cg = CoolImage()
let ns = NSImage(cgImage: cg, size: Size(width: cg.width, height: cg.height))
view(image: ns)

并检查它是否具有完整的视觉保真度.

And checked sure enough it had the full visual fidelity.

然后我像这样将cg图像加载到MTLTexture中

So then I loaded the cg image into a MTLTexture like so

    let textureLoader = MTKTextureLoader(device: metalState.sharedDevice!)

    let options = [
        MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue | MTLTextureUsage.shaderWrite.rawValue | MTLTextureUsage.renderTarget.rawValue),
        MTKTextureLoader.Option.SRGB: false
    ]

    return ensure(try textureLoader.newTexture(cgImage: cg, options: options))

然后我将MTLTexture转换回UIImage,如下所示:

I then converted the MTLTexture back to a UIImage like so:

    let texture = self
    let width = texture.width
    let height = texture.height
    let bytesPerRow = width * 4

    let data = UnsafeMutableRawPointer.allocate(bytes: bytesPerRow * height, alignedTo: 4)
    defer {
        data.deallocate(bytes: bytesPerRow * height, alignedTo: 4)
    }

    let region = MTLRegionMake2D(0, 0, width, height)
    texture.getBytes(data, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
    var buffer = vImage_Buffer(data: data, height: UInt(height), width: UInt(width), rowBytes: bytesPerRow)

    var map: [UInt8] = [0, 1, 2, 3]
    if (pixelFormat == .bgra8Unorm) {
        map = [2, 1, 0, 3]
    }
    vImagePermuteChannels_ARGB8888(&buffer, &buffer, map, 0)

    guard let colorSpace = CGColorSpace(name: CGColorSpace.genericRGBLinear) else { return nil }
    guard let context = CGContext(data: data, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) else { return nil }
    guard let cgImage = context.makeImage() else { return nil }

    return NSImage(cgImage: cgImage, size: Size(width: width, height: height))

然后查看.

生成的图像相当饱和,我相信这是由于CGImage到MTLTexture的转换,这在我过去相当成功.

The resulting image was quite saturated and I believe it was because of the CGImage to MTLTexture conversion which I have been fairly successful with in the past.

请注意,此纹理从未被渲染过.

Please note that this texture was never rendered only converted.

您可能想知道为什么我要使用所有这些转换,所以这很重要.我的实际管道无法像这样工作,但是它确实要求每个转换组件都能够正常工作.这不是我的实际用例,只是用来显示问题的.

You are probably wondering why I am using all of these conversions and that is a great point. My actual pipeline does not work anything like this HOWEVER it does require each of these conversion components to be working smoothly. This is not my actual use case just something to show the problem.

推荐答案

这里的问题不是从CGImageMTLTexture的转换.问题是您假设源图像的色彩空间是线性的.图像数据实际上很有可能是sRGB编码的,因此,通过创建具有通用线性色彩空间的位图上下文,您会错误地告诉CG它应在显示之前对图像数据进行伽马编码,从而导致去饱和您正在看到.

The problem here isn't the conversion from CGImage to MTLTexture. The problem is that you're assuming that the color space of the source image is linear. More likely than not, the image data is actually sRGB-encoded, so by creating a bitmap context with a generic linear color space, you're incorrectly telling CG that it should gamma-encode the image data before display, which leads to the desaturation you're seeing.

您可以通过使用原始CGImage的本机色彩空间或通过其他方式考虑到您的图像数据是sRGB编码的事实来解决此问题.

You can fix this by using the native color space of the original CGImage, or by otherwise accounting for the fact that your image data is sRGB-encoded.

这篇关于MTKTextureLoader使图像饱和的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆