UIImage, созданный из CMSampleBuffer, не сохранен и вызывает EXC_BAD_ACCESS
Я строю UIImage из CMSampleBuffer. Из основного потока я вызываю функцию для доступа к данным пикселей в CMSampleBuffer и преобразую плоскости YCbCr в растровое изображение ABGR, которое я обертываю в UIImage. Я вызываю функцию из основного потока с помощью:
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0), {() -> Void in
let image = self.imageFromSampleBuffer(frame)
dispatch_async(dispatch_get_main_queue(), {() -> Void in
self.testView.image = image
self.testView.hidden = false
})
})
Это поддерживает отзывчивость пользовательского интерфейса и основного потока, как я надеюсь. Функция, обрабатывающая буфер:
func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage {
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 = CGImageCreate(width, height, 8, 32, width * 4, colorSpace!, bitmapInfo, dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
let image = UIImage(CGImage: cgImage)
CVPixelBufferUnlockBaseAddress(pixelBuffer,0)
return image
}
Если я поставлю точку останова непосредственно перед возвратом функции, я смогу использовать "Быстрый просмотр" и увидеть изображение (и это то, что я ожидал). Однако, как только функция возвращается, я не могу использовать image
где-нибудь еще и Quick Look всегда не удается. Если я попытаюсь установить UIImageView для возвращенного изображения, ничего в пользовательском интерфейсе не изменится:
testView.image = image \\The UIImageView does not update.
Если я пытаюсь получить доступ к изображению любым другим способом (например, попытаться сохранить его в Parse), код вылетает с EXC_BAD_ACCESS. Опять же, если я сохраню изображение в Parse в вышеупомянутой функции, оно появится в базе данных бэкэнда, как и ожидалось.
Я также попытался вызвать функцию обработки без отправки в глобальные и основные очереди, вызвав функцию напрямую. Результаты всегда одинаковы.
Я считаю, что это потому, что изображение не сохраняется. Я попытался определить как изображение, так и контекст CGImage на уровне класса и файла, но ни один из них не изменил результат. Я думал, что это будет поддерживать ссылку, но это, очевидно, нет. Я достаточно новичок в Swift, так что я явно не понимаю, как работает ARC в этом случае.
Я также считаю, что было несколько раз при отладке с использованием Quick Look из функции, что при первом нажатии Quick Look было "недоступно"... но ожидание несколько секунд и повторное нажатие приводит к появлению изображения. Возможно ли, что данные становятся доступными дольше? Возможно, GPU->CPU? Если это так, как я могу проверить / задержать, чтобы избежать сбоя?
Как мне сохранить ссылку? Есть ли лучший способ обработки изображения, созданного из CMSampleBuffer?
1 ответ
Проблема в том, как создается CGImage. С помощью dataProvider
а также CGImageCreate
это конкретный вопрос:
let dataProvider = CGDataProviderCreateWithData(nil, rgbaImage, 4 * width * height, nil)!
let cgImage = CGImageCreate(width, height, 8, 32, width * 4, colorSpace!, bitmapInfo, dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
Рабочий раствор с использованием CGBitmapContextGetData
а также CGBitmapContextCreateImage
следующим образом:
func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage? {
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)
let contextBytesPerRow = Int(width) * 4
let contextByteCount = contextBytesPerRow * Int(height)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapData = malloc(contextByteCount)
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue)
let context = CGBitmapContextCreate(bitmapData, width, height, 8, contextBytesPerRow, colorSpace, bitmapInfo.rawValue)
let data = CGBitmapContextGetData(context)
let rgbaImage = UnsafeMutablePointer<UInt8>(data)
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 quartzImage = CGBitmapContextCreateImage(context)
CVPixelBufferUnlockBaseAddress(pixelBuffer,0)
let image = UIImage(CGImage: quartzImage!, scale: CGFloat(1.0), orientation: UIImageOrientation.Right)
return (image)
// frontCameraImageOrientation = UIImageOrientation.LeftMirrored
// backCameraImageOrientation = UIImageOrientation.Right
}