0

我正在尝试创建一个实时视频处理应用程序,其中我需要获取每个帧的所有像素的 RGBA 值,并使用外部库处理它们,并显示它们。我正在尝试获取每个像素的 RGBA 值,但是我这样做的方式太慢了,我想知道是否有一种方法可以使用VImage更快地做到这一点。这是我当前的代码,也是我获取所有像素的方式,因为我得到了当前帧:

        guard let cgImage = context.makeImage() else {
        return nil
    }
    guard let data = cgImage.dataProvider?.data,
    let bytes = CFDataGetBytePtr(data) else {
    fatalError("Couldn't access image data")
    }
    assert(cgImage.colorSpace?.model == .rgb)
    let bytesPerPixel = cgImage.bitsPerPixel / cgImage.bitsPerComponent
    gp.async {
        for y in 0 ..< cgImage.height {
            for x in 0 ..< cgImage.width {
                let offset = (y * cgImage.bytesPerRow) + (x * bytesPerPixel)
                let components = (r: bytes[offset], g: bytes[offset + 1], b: bytes[offset + 2])
                print("[x:\(x), y:\(y)] \(components)")
            }
            print("---")
        }

    }

这是使用 VImage 的版本,但我有一些内存泄漏,我无法访问像素

        guard
        let format = vImage_CGImageFormat(cgImage: cgImage),
        var buffer = try? vImage_Buffer(cgImage: cgImage,
                                        format: format) else {
            exit(-1)
        }

    let rowStride = buffer.rowBytes / MemoryLayout<Pixel_8>.stride / format.componentCount
    do {
        
        let componentCount = format.componentCount
        var argbSourcePlanarBuffers: [vImage_Buffer] = (0 ..< componentCount).map { _ in
            guard let buffer1 = try? vImage_Buffer(width: Int(buffer.width),
                                                   height: Int(buffer.height),
                                                  bitsPerPixel: format.bitsPerComponent) else {
                                                    fatalError("Error creating source buffers.")
            }
            return buffer1
        }
        vImageConvert_ARGB8888toPlanar8(&buffer,
                                        &argbSourcePlanarBuffers[0],
                                        &argbSourcePlanarBuffers[1],
                                        &argbSourcePlanarBuffers[2],
                                        &argbSourcePlanarBuffers[3],
                                        vImage_Flags(kvImageNoFlags))

        let n = rowStride * Int(argbSourcePlanarBuffers[1].height) * format.componentCount
        let start = buffer.data.assumingMemoryBound(to: Pixel_8.self)
        var ptr = UnsafeBufferPointer(start: start, count: n)

        print(Array(argbSourcePlanarBuffers)[1]) // prints the first 15 interleaved values
        buffer.free()
    }
4

2 回答 2

1

您可以访问 vImage 缓冲区中的底层像素来执行此操作。

例如,给定一个名为 的图像cgImage,使用以下代码填充 vImage 缓冲区:

guard
    let format = vImage_CGImageFormat(cgImage: cgImage),
    let buffer = try? vImage_Buffer(cgImage: cgImage,
                                    format: format) else {
        exit(-1)
    }

let rowStride = buffer.rowBytes / MemoryLayout<Pixel_8>.stride / format.componentCount

请注意,vImage 缓冲区data可能比图像宽(请参阅:https ://developer.apple.com/documentation/accelerate/finding_the_sharpest_image_in_a_sequence_of_captured_images ),这就是我添加rowStride.

要将像素作为交错值的单个缓冲区访问,请使用:

do {
    let n = rowStride * Int(buffer.height) * format.componentCount
    let start = buffer.data.assumingMemoryBound(to: Pixel_8.self)
    let ptr = UnsafeBufferPointer(start: start, count: n)
    
    print(Array(ptr)[ 0 ... 15]) // prints the first 15 interleaved values
}

要将像素作为Pixel_8888值的缓冲区访问,请使用(确保format.componentCount4

do {
    let n = rowStride * Int(buffer.height)
    let start = buffer.data.assumingMemoryBound(to: Pixel_8888.self)
    let ptr = UnsafeBufferPointer(start: start, count: n)
    
    print(Array(ptr)[ 0 ... 3]) // prints the first 4 pixels
}
于 2021-11-18T10:09:51.917 回答
0

这是最慢的方法。更快的方法是使用自定义 CoreImage 过滤器

比这更快的是编写自己的 OpenGL 着色器(或者更确切地说,它在当前设备的 Metal 中等效)

我已经编写了 OpenGL 着色器,但还没有使用过 Metal。

两者都允许您编写直接在 GPU 上运行的图形代码。

于 2021-11-17T19:31:50.740 回答