iOS中如何将CVPixelBuffer格式的灰度深度图转换为JPEG/PNG并保存?
解决iOS中CVPixelBuffer格式的深度图保存为JPEG/PNG的问题
我之前也碰到过一模一样的问题!能把深度图渲染到屏幕却存不了本地文件,核心原因是得把CVPixelBuffer里的灰度深度数据转换成可编码的图像格式,下面给你两种亲测有效的解决方法:
方法一:转成UIImage再保存(简单易上手)
先把CVPixelBuffer转换成UIImage,再利用UIImage自带的方法保存成JPEG/PNG,代码逻辑很直观:
import UIKit import CoreImage // 将CVPixelBuffer转为UIImage func pixelBufferToUIImage(_ pixelBuffer: CVPixelBuffer) -> UIImage? { let ciImage = CIImage(cvPixelBuffer: pixelBuffer) // 用硬件加速的CIContext提升转换效率 let context = CIContext(options: [.useSoftwareRenderer: false]) guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else { return nil } return UIImage(cgImage: cgImage) } // 保存UIImage到本地 if let depthImage = pixelBufferToUIImage(yourDepthPixelBuffer) { // 保存为JPEG(压缩质量0.8,可调整) if let jpegData = depthImage.jpegData(compressionQuality: 0.8) { let saveURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("depth_map.jpg") try? jpegData.write(to: saveURL) } // 保存为PNG(无损,适合保留精确深度值) if let pngData = depthImage.pngData() { let saveURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("depth_map.png") try? pngData.write(to: saveURL) } }
如果你的CVPixelBuffer是16位灰度格式(比如kCVPixelFormatType_OneComponent16),记得在转换时指定对应的颜色空间和位图信息,避免出现颜色失真。
方法二:用ImageIO直接编码(高效适合大尺寸)
如果深度图尺寸较大,直接用ImageIO编码CVPixelBuffer会更高效,跳过UIImage的中间转换步骤:
import UIKit import ImageIO import MobileCoreServices // 直接将CVPixelBuffer保存为图像文件 func saveDepthPixelBuffer(_ pixelBuffer: CVPixelBuffer, to url: URL, isPNG: Bool) throws { // 锁定像素缓冲区的内存地址 CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly) } let width = CVPixelBufferGetWidth(pixelBuffer) let height = CVPixelBufferGetHeight(pixelBuffer) let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer) let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) // 创建灰度颜色空间 let colorSpace = CGColorSpaceCreateDeviceGray() // 根据像素格式设置位图信息(这里以8位灰度为例) let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue) // 创建CGContext guard let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { throw NSError(domain: "DepthSaveError", code: 0, userInfo: [NSLocalizedDescriptionKey: "无法创建CGContext"]) } // 生成CGImage guard let cgImage = context.makeImage() else { throw NSError(domain: "DepthSaveError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无法生成CGImage"]) } // 创建图像编码目标 let imageType = isPNG ? kUTTypePNG : kUTTypeJPEG guard let destination = CGImageDestinationCreateWithURL(url as CFURL, imageType, 1, nil) else { throw NSError(domain: "DepthSaveError", code: 2, userInfo: [NSLocalizedDescriptionKey: "无法创建编码目标"]) } // 如果是JPEG,可以添加压缩质量参数 let properties = isPNG ? nil : [kCGImageDestinationLossyCompressionQuality as String: 0.8] as CFDictionary CGImageDestinationAddImage(destination, cgImage, properties) // 完成编码 guard CGImageDestinationFinalize(destination) else { throw NSError(domain: "DepthSaveError", code: 3, userInfo: [NSLocalizedDescriptionKey: "编码图像失败"]) } } // 调用示例 let saveURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("depth_map.png") try? saveDepthPixelBuffer(yourDepthPixelBuffer, to: saveURL, isPNG: true)
额外注意事项
- 确保你的CVPixelBuffer格式是标准灰度格式(比如
kCVPixelFormatType_OneComponent8或kCVPixelFormatType_OneComponent16),否则需要先做格式转换。 - 若要保留精确的深度数据,优先选择PNG格式保存;如果追求文件体积小,再选JPEG。
内容的提问来源于stack exchange,提问作者NoOneHere
相关产品推荐
相关产品推荐

