You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中读取RGGB/未去马赛克/Bayer格式的RAW图像像素

解决方案

1. 无预处理读取RAW文件的Bayer矩阵

不要使用CIFilter默认的RAW加载逻辑,改用ImageIO框架配置参数跳过自动demosaic:

import ImageIO
import CoreGraphics

guard let rawFileURL = Bundle.main.url(forResource: "test", withExtension: "dng") else {
    // 处理文件不存在错误
    fatalError("RAW文件不存在")
}

let readOptions: [CFString: Any] = [
    kCGImageSourceShouldAllowRAW: true,
    kCGImageSourceDisableRAWDecode: true, // 核心参数:禁用自动demosaic等RAW预处理
    kCGImageSourceShouldCacheImmediately: false
]

guard let imageSource = CGImageSourceCreateWithURL(rawFileURL as CFURL, readOptions as CFDictionary),
      let rawCGImage = CGImageSourceCreateImageAtIndex(imageSource, 0, readOptions as CFDictionary) else {
    fatalError("RAW文件读取失败")
}

// 读取Bayer排列模式
let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, readOptions as CFDictionary) as? [CFString: Any]
let bayerPattern = properties?[kCGImagePropertyRAWInfoBayerPattern] as? Int ?? 0 // 0对应RGGB,1对应GRBG等,按需匹配

获取到rawCGImage后即可通过CGImageGetDataProvider读取原始单通道Bayer像素数据。

2. 转换为MTLTexture

根据Bayer数据的位深创建对应格式的MTLTexture,将原始像素数据拷贝进纹理:

import Metal

let device = MTLCreateSystemDefaultDevice()!
let bayerWidth = rawCGImage.width
let bayerHeight = rawCGImage.height
let bitsPerComponent = rawCGImage.bitsPerComponent

// 10/12/14位RAW一般存储在16位容器中,用r16Unorm格式;8位RAW用r8Unorm
let pixelFormat: MTLPixelFormat = bitsPerComponent > 8 ? .r16Unorm : .r8Unorm

let textureDesc = MTLTextureDescriptor.texture2DDescriptor(
    pixelFormat: pixelFormat,
    width: bayerWidth,
    height: bayerHeight,
    mipmapped: false
)
textureDesc.usage = [.shaderRead, .shaderWrite]
guard let bayerTexture = device.makeTexture(descriptor: textureDesc) else {
    fatalError("纹理创建失败")
}

// 拷贝Bayer数据到纹理
let rawData = CGImageGetDataProvider(rawCGImage).flatMap { CGDataProviderCopyData($0) } as Data?
rawData?.withUnsafeBytes { buffer in
    let bytesPerRow = rawCGImage.bytesPerRow
    bayerTexture.replace(
        region: MTLRegionMake2D(0, 0, bayerWidth, bayerHeight),
        mipmapLevel: 0,
        withBytes: buffer.baseAddress!,
        bytesPerRow: bytesPerRow
    )
}

3. 修改Bayer矩阵

编写Metal计算着色器对bayerTexture的单通道值做自定义修改,示例kernel函数结构如下:

kernel void modifyBayer(
    texture2d<half, access::read> inputTexture [[texture(0)]],
    texture2d<half, access::write> outputTexture [[texture(1)]],
    uint2 gid [[thread_position_in_grid]]
) {
    half rawValue = inputTexture.read(gid).r;
    // 自定义修改逻辑,比如调整增益、消除坏点等
    half modifiedValue = rawValue * 1.2h;
    outputTexture.write(half4(modifiedValue, 0.0h, 0.0h, 1.0h), gid);
}

4. 执行Demosaic处理

有两种常用实现方案:

  • 自定义Metal计算着色器实现Demosaic算法,比如双线性插值、Malvar算法等,直接读取修改后的Bayer纹理输出RGB三通道纹理
  • 将修改后的Bayer纹理转换为CIImage,调用系统CIDemosaic滤镜处理:
import CoreImage

let modifiedCIImage = CIImage(mtlTexture: modifiedBayerTexture, options: [.colorSpace: CGColorSpaceCreateDeviceGray()])
let demosaicFilter = CIFilter(name: "CIDemosaic")!
demosaicFilter.setValue(modifiedCIImage, forKey: kCIInputImageKey)
demosaicFilter.setValue(bayerPattern, forKey: "inputBayerPattern") // 传入之前读取的Bayer排列值
guard let demosaicedImage = demosaicFilter.outputImage else {
    fatalError("去马赛克处理失败")
}

注意事项

  • 优先适配DNG格式RAW文件,不同厂商的私有RAW格式(CR2、NEF等)元数据兼容性存在差异
  • 不要硬编码Bayer排列顺序,必须从RAW文件的元数据中动态读取
  • 处理16位RAW数据时注意字节序问题,部分设备输出的RAW数据为小端序,需根据元数据标识做对应转换

内容的提问来源于stack exchange,提问作者Martin Marek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 00:24:06