gpt4 book ai didi

ios - 如何从 CVPixelBuffer 中删除 alpha channel 并在 Swift 中获取其数据

转载 作者:行者123 更新时间:2023-12-01 15:42:43 25 4
gpt4 key购买 nike

我的 CVPixelBuffer 以 kCVPixelFormatType_32BGRA 的形式出现,我正在尝试以 BGR 格式获取没有 Alpha channel 的帧数据。这是我尝试做的(作为 扩展 CVPixelBuffer)

func bgrData(byteCount: Int) -> Data? {
CVPixelBufferLockBaseAddress(self, .readOnly)
defer { CVPixelBufferUnlockBaseAddress(self, .readOnly) }
guard let sourceData = CVPixelBufferGetBaseAddress(self) else {
return nil
}

let width = CVPixelBufferGetWidth(self)
let height = CVPixelBufferGetHeight(self)
let sourceBytesPerRow = CVPixelBufferGetBytesPerRow(self)
let destinationBytesPerRow = 3 * width

// Assign input image to `sourceBuffer` to convert it.
var sourceBuffer = vImage_Buffer(
data: sourceData,
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: sourceBytesPerRow
)

// Make `destinationBuffer` and `destinationData` for its data to be assigned.
guard let destinationData = malloc(height * destinationBytesPerRow) else {
os_log("Error: out of memory", type: .error)
return nil
}
defer { free(destinationData) }
var destinationBuffer = vImage_Buffer(
data: destinationData,
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: destinationBytesPerRow)

// Return `Data` with bgr image.
return imageByteData = Data(
bytes: sourceBuffer.data, count: destinationBuffer.rowBytes * height)
}

但是得到的buffer好像不对。实现这一目标的最佳方法是什么?提前致谢

最佳答案

由于您有权访问您的 CVPixelBuffer,您可以直接使用 Accelerate 框架为您进行转换。

我不会检查此代码中的任何错误、try/catch 语句、无守卫等。您需要确保代码已防错。

让我们首先定义我们的 BGRA 颜色格式。因为我们有 4 个 channel ,所以每个像素需要 32 位。我们还定义我们的 alpha channel 是最后一位。

var bgraSourceFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: Unmanaged.passRetained(CGColorSpaceCreateDeviceRGB()),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue),
version: 0,
decode: nil,
renderingIntent: .defaultIntent
)

现在我们可以定义 BGR 格式了。我们需要 3 个 channel ,因此每个像素 24 位就足够了。我们还定义此格式将没有 alpha channel 。

var bgrDestinationFormat = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 24,
colorSpace: nil,
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
version: 0,
decode: nil,
renderingIntent: .defaultIntent
)

并创建转换器...

let bgraToRgbConverter = vImageConverter_CreateWithCGImageFormat(
&bgraSourceFormat,
&bgrDestinationFormat,
nil,
vImage_Flags(kvImagePrintDiagnosticsToConsole),
nil
)

let converter = bgraToRgbConverter!.takeRetainedValue()

现在我们需要从我们现有的像素数据创建一个读取缓冲区,以及一个用于复制我们需要的内容的写入缓冲区。要从 CVPixelBuffer 创建一个读取缓冲区,我们可以这样做:

var bgraBuffer  = vImage_Buffer()
let imageFormat = vImageCVImageFormat_CreateWithCVPixelBuffer(cvPixelBuffer).takeRetainedValue()
vImageCVImageFormat_SetColorSpace(imageFormat, CGColorSpaceCreateDeviceRGB())
vImageBuffer_InitWithCVPixelBuffer(
&bgraBuffer,
&bgraSourceFormat,
cvPixelBuffer,
imageFormat,
nil,
vImage_Flags(kvImageNoFlags)
)

并创建空的写入缓冲区...

var bgrBuffer = vImage_Buffer()
vImageBuffer_Init(
&bgrBuffer,
bgraBuffer.height,
bgraBuffer.width,
bgrDestinationFormat.bitsPerPixel,
vImage_Flags(kvImageNoFlags)
)

我们准备好了......让我们告诉加速框架从一种格式转换为另一种格式

vImageConvert_AnyToAny(
converter,
&bgraBuffer,
&bgrBuffer,
nil,
vImage_Flags(kvImagePrintDiagnosticsToConsole)
)

仅此而已。您的 BGRA 现在已作为 vImage_Buffer 转换为 BGR。我们可以通过直接读取像素数据来检查我们是否完成了我们想要的。首先,我们需要访问数据:

let bgraData = bgraBuffer.data!.assumingMemoryBound(to: UInt8.self)
let bgrData = bgrBuffer.data!.assumingMemoryBound(to: UInt8.self)

现在,我们可以打印第一个和第二个像素

print(bgraData[0], bgraData[1], bgraData[2], bgraData[3])
print(bgrData[0], bgrData[1], bgrData[2])

print(bgraData[4], bgraData[5], bgraData[6], bgraData[7])
print(bgrData[3], bgrData[4], bgrData[5])

这是我在 playgrounds 中用于测试的 png 图像中看到的输出:

249 244 234 255
249 244 234

251 242 233 255
251 242 233

如您所见,像素是在没有 alpha channel 的情况下复制的。如果您要使用任何循环,请小心您的 for 循环,因为我们现在有 3 个 channel 。

如果您正在开发一款游戏并在每一帧都这样做,请尝试让您的对象保持事件状态。加速格式定义,写入缓冲区和转换器不会因相同的图像大小和格式而改变,因此它们可以创建一次并保存在内存中以备将来使用。

看起来您正在返回一个数据对象。您可以将 UnsafeMutablePointer 构造转换为您需要的任何内容。

或者您也可以使用加速器的 vImageBuffer_CopyToCVPixelBuffer 方法将 vImage_Buffer 转换回 CVPixelBuffer(如果需要)。 vImage_Buffer 有很多转换器,总有一款适合你。检查this link有关如何使用复制到像素缓冲区方法的更多信息。 This link有一个很好的用法示例。

编辑:您的 CVPixelBuffer 可能会被填充。

由于硬件要求,您的图像可能有一个填充以确保缓冲区宽度和高度是 16 的倍数。这也会导致在您的 vImage_Buffer 结构中进行填充。如果你需要循环,但只需要访问/更新单个像素,你可以使用 Accelerate 的函数来提高速度。检查这个link了解可能的方法,页面末尾有很好的示例。

要完整读取数据,可以这样写:

var bgrData = bgrBuffer.data!.assumingMemoryBound(to: UInt8.self)
print(bgrBuffer.width, bgrBuffer.height, bgrBuffer.rowBytes)

for _ in 0 ..< Int(bgrBuffer.height) {
for x in 0 ..< Int(bgrBuffer.width) {
let b = (bgrData + x * 3 + 0).pointee
let g = (bgrData + x * 3 + 1).pointee
let r = (bgrData + x * 3 + 2).pointee
print(b, g, r)
}
bgrData = bgrData.advanced(by: bgrBuffer.rowBytes)
}

这将确保您正在读取全宽像素,但在末尾传递填充。

关于ios - 如何从 CVPixelBuffer 中删除 alpha channel 并在 Swift 中获取其数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63243981/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com