如何在iOS 9中使用Swift从CMSampleBuffer提取像素数据进行处理?

如何在iOS 9中使用Swift从CMSampleBuffer提取像素数据进行处理?,ios,objective-c,swift,image,core-media,Ios,Objective C,Swift,Image,Core Media,我正在用Swift编写一个应用程序,它使用Scandit条形码扫描SDK。SDK允许您直接访问相机帧,并将帧作为CMSampleBuffer提供。他们用Objective-C提供文档,我在Swift中很难使用它。我不知道问题是否出在移植代码上,或者样本缓冲区本身是否有问题,可能是由于核心介质在生成文档后发生了变化 他们的API将框架公开如下(Objective-C): 但是,这在以下方面失败: Jan 29 09:01:30 Scandit[1308] <Error>: CGBit

我正在用Swift编写一个应用程序,它使用Scandit条形码扫描SDK。SDK允许您直接访问相机帧,并将帧作为CMSampleBuffer提供。他们用Objective-C提供文档,我在Swift中很难使用它。我不知道问题是否出在移植代码上,或者样本缓冲区本身是否有问题,可能是由于核心介质在生成文档后发生了变化

他们的API将框架公开如下(Objective-C):

但是,这在以下方面失败:

Jan 29 09:01:30  Scandit[1308] <Error>: CGBitmapContextCreate: invalid data bytes/row: should be at least 7680 for 8 integer bits/component, 3 components, kCGImageAlphaNoneSkipFirst.
Jan 29 09:01:30  Scandit[1308] <Error>: CGBitmapContextCreateImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
fatal error: unexpectedly found nil while unwrapping an Optional value
根据
CMSampleBufferGetFormatDescription(frame)
,从委托传递的缓冲区包含以下内容:

给出:

Pixel buffer bytes per row (Plane 0): 1920
Pixel buffer bytes per row (Plane 1): 1920
我不理解这种差异

我还尝试单独处理每个像素,因为很明显缓冲区包含某种形式的YCbCr,但我尝试过的每种方法都失败了。Scandit API建议(目标C):

但是,我找不到允许使用CVPlanarPixelBufferInfo访问缓冲区信息的快速实现。。。我尝试过的一切都失败了,因此我无法确定“Y”、“Cr”等的偏移量


如何访问缓冲区中的像素数据?这是SDK传递的CMSampleBuffer的问题,还是iOS9的问题,还是两者都有问题?

这不是一个完整的答案,只是一些提示:

Scandit使用YCBCRBI平面格式。每个像素有一个Y字节,每组2x2像素有一个Cb和Cr字节。Y值在第一个平面上,Cb和Cr值在第二个平面上

如果图像是w x h像素大,那么第一个平面包含h行w字节(可能每行都有一些填充)

第二个平面包含w/2对字节的h/2行。每对由Cb和Cr值组成。同样,每一行的末尾可能都有一些填充

因此,位置(x,Y)处像素的Y值可以在地址处找到:

Width: 1920
Height: 1080
Bytes per row: 2904
Y:baseAddressPlane1+Y*bytesPerRowPlane1+x

位置(x,y)处像素的值Cb和Cr可在地址处找到:

Width: 1920
Height: 1080
Bytes per row: 2904
Cb:baseAddressPlane2+(y/2)*BytesPerrowPlane2+(x/2)*2

Cr:baseAddressPlane2+(y/2)*BytesPerrowPlane2+(x/2)*2+1

除以2的除法是整数除法,舍弃了小数部分。

根据Codo的“提示”,结合Scandit文档中的Objective-C代码,我在Swift中找到了一个解决方案。虽然我接受了Codo的答案,因为它帮了我很大的忙,但我也在回答我自己的问题,希望一个完整的解决方案能在将来帮助到某人:

let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
CVPixelBufferLockBaseAddress(pixelBuffer, 0)
let lumaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
let chromaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)

let width = CVPixelBufferGetWidth(pixelBuffer)
let height = CVPixelBufferGetHeight(pixelBuffer)

let lumaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
let chromaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let lumaBuffer = UnsafeMutablePointer<UInt8>(lumaBaseAddress)
let chromaBuffer = UnsafeMutablePointer<UInt8>(chromaBaseAddress)

var rgbaImage = [UInt8](count: 4*width*height, repeatedValue: 0)
for var x = 0; x < width; x++ {
    for var y = 0; y < height; y++ {
        let lumaIndex = x+y*lumaBytesPerRow
        let chromaIndex = (y/2)*chromaBytesPerRow+(x/2)*2
        let yp = lumaBuffer[lumaIndex]
        let cb = chromaBuffer[chromaIndex]
        let cr = chromaBuffer[chromaIndex+1]

        let ri = Double(yp)                                + 1.402   * (Double(cr) - 128)
        let gi = Double(yp) - 0.34414 * (Double(cb) - 128) - 0.71414 * (Double(cr) - 128)
        let bi = Double(yp) + 1.772   * (Double(cb) - 128)

        let r = UInt8(min(max(ri,0), 255))
        let g = UInt8(min(max(gi,0), 255))
        let b = UInt8(min(max(bi,0), 255))

        rgbaImage[(x + y * width) * 4] = b
        rgbaImage[(x + y * width) * 4 + 1] = g
        rgbaImage[(x + y * width) * 4 + 2] = r
        rgbaImage[(x + y * width) * 4 + 3] = 255
    }
}

let colorSpace = CGColorSpaceCreateDeviceRGB()
let dataProvider: CGDataProviderRef = CGDataProviderCreateWithData(nil, rgbaImage, 4 * width * height, nil)!
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.NoneSkipFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue)
let cgImage: CGImageRef = CGImageCreate(width, height, 8, 32, width * 4, colorSpace!, bitmapInfo, dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
let image: UIImage = UIImage(CGImage: cgImage)
CVPixelBufferUnlockBaseAddress(pixelBuffer,0)
让pixelBuffer=CMSampleBufferGetImageBuffer(sampleBuffer)!
CVPixelBufferLockBaseAddress(pixelBuffer,0)
设lumaBaseAddress=CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0)
让chromaBaseAddress=CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1)
let width=CVPixelBufferGetWidth(pixelBuffer)
let height=CVPixelBufferGetHeight(pixelBuffer)
设lumaBytesPerRow=CVPixelBufferGetBytesPerrowOffPlane(pixelBuffer,0)
设chromaBytesPerRow=CVPixelBufferGetBytesPerrowOffPlane(pixelBuffer,1)
设lumaBuffer=UnsafeMutablePointer(lumaBaseAddress)
let chromaBuffer=非女性化指针(chromaBaseAddress)
变量rgbaImage=[UInt8](计数:4*宽度*高度,重复值:0)
对于var x=0;x<宽度;x++{
对于变量y=0;y

尽管迭代了整个8.3MP图像,但代码执行速度非常快。我坦率地承认,我对核心媒体框架没有深入的了解,但我相信这意味着代码是在GPU上执行的。但是,我非常感谢对代码的任何评论,以提高它的效率,或者提高“快速性”,因为我完全是一个业余爱好者

看看我对Cb和Cr的公式做了一个小的更新。好的,这非常有帮助。我能够访问luma数据。迭代每个像素需要相当长的时间。虽然我最终希望在像素级的基础上处理图像,但现在我很高兴从缓冲区中取出一个UIImage。参考我原始问题中的代码,我现在可以使用CGBitmapContextCreate成功地获得图像上下文,但生成的图像是亮度和色度分量的混合,重复了4次。。。我确信这与不正确地跨过数组有关,而且事实上ARGB每像素有4个字节……为了加快代码速度,我看到了两个主要方面:(1)检查“Float”而不是“Double”是否会带来改进。(2) 去掉所有的整数乘法。就像你
let pixelBufferBytesPerRow0 = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0)
let pixelBufferBytesPerRow1 = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1)
Pixel buffer bytes per row (Plane 0): 1920
Pixel buffer bytes per row (Plane 1): 1920
// Get the buffer info for the YCbCrBiPlanar format.
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
CVPixelBufferLockBaseAddress(pixelBuffer, 0)
let lumaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
let chromaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)

let width = CVPixelBufferGetWidth(pixelBuffer)
let height = CVPixelBufferGetHeight(pixelBuffer)

let lumaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
let chromaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let lumaBuffer = UnsafeMutablePointer<UInt8>(lumaBaseAddress)
let chromaBuffer = UnsafeMutablePointer<UInt8>(chromaBaseAddress)

var rgbaImage = [UInt8](count: 4*width*height, repeatedValue: 0)
for var x = 0; x < width; x++ {
    for var y = 0; y < height; y++ {
        let lumaIndex = x+y*lumaBytesPerRow
        let chromaIndex = (y/2)*chromaBytesPerRow+(x/2)*2
        let yp = lumaBuffer[lumaIndex]
        let cb = chromaBuffer[chromaIndex]
        let cr = chromaBuffer[chromaIndex+1]

        let ri = Double(yp)                                + 1.402   * (Double(cr) - 128)
        let gi = Double(yp) - 0.34414 * (Double(cb) - 128) - 0.71414 * (Double(cr) - 128)
        let bi = Double(yp) + 1.772   * (Double(cb) - 128)

        let r = UInt8(min(max(ri,0), 255))
        let g = UInt8(min(max(gi,0), 255))
        let b = UInt8(min(max(bi,0), 255))

        rgbaImage[(x + y * width) * 4] = b
        rgbaImage[(x + y * width) * 4 + 1] = g
        rgbaImage[(x + y * width) * 4 + 2] = r
        rgbaImage[(x + y * width) * 4 + 3] = 255
    }
}

let colorSpace = CGColorSpaceCreateDeviceRGB()
let dataProvider: CGDataProviderRef = CGDataProviderCreateWithData(nil, rgbaImage, 4 * width * height, nil)!
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.NoneSkipFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue)
let cgImage: CGImageRef = CGImageCreate(width, height, 8, 32, width * 4, colorSpace!, bitmapInfo, dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
let image: UIImage = UIImage(CGImage: cgImage)
CVPixelBufferUnlockBaseAddress(pixelBuffer,0)