Конвертировать CMSampleBuffer в UIImage

Я пытаюсь преобразовать sampleBuffer в UIImage и отобразить его в виде изображения с colorspaceGray. Но это отображается как следующее изображение. Что не так с этим вопросом? Как я могу конвертировать CMSampleBuffer? Это серая версия красного цвета изображения

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
    print("buffered")
    let imageBuffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
    CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
    let width: Int = CVPixelBufferGetWidth(imageBuffer)
    let height: Int = CVPixelBufferGetHeight(imageBuffer)
    let bytesPerRow: Int = CVPixelBufferGetBytesPerRow(imageBuffer)
    let lumaBuffer = CVPixelBufferGetBaseAddress(imageBuffer)

    //let planeCount : Int = CVPixelBufferGetPlaneCount(imageBuffer)
    let grayColorSpace: CGColorSpace = CGColorSpaceCreateDeviceGray()
    let context: CGContext = CGContext(data: lumaBuffer, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow , space: grayColorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)!
    let dstImageFilter: CGImage = context.makeImage()!
    let imageRect : CGRect = CGRect(x: 0, y: 0, width: width, height: height)
    context.draw(dstImageFilter, in: imageRect)
    let image = UIImage(cgImage: dstImageFilter)
    DispatchQueue.main.sync(execute: {() -> Void in
        self.imageTest.image = image
    })
}

2 ответа

Решение

Преобразование просто:

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {

        let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!

        let ciimage : CIImage = CIImage(cvPixelBuffer: imageBuffer)

        let image : UIImage = self.convert(cmage: ciimage)

    }

// Convert CIImage to CGImage
    func convert(cmage:CIImage) -> UIImage
    {
        let context:CIContext = CIContext.init(options: nil)
        let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
        let image:UIImage = UIImage.init(cgImage: cgImage)
        return image
    }

Более современное решение с исправлением ориентации и без CGImage преобразование для производительности:

func orientation() -> UIImage.Orientation {
    let curDeviceOrientation = UIDevice.current.orientation
    var exifOrientation: UIImage.Orientation
    switch curDeviceOrientation {
        case UIDeviceOrientation.portraitUpsideDown:  // Device oriented vertically, Home button on the top
            exifOrientation = .left
        case UIDeviceOrientation.landscapeLeft:       // Device oriented horizontally, Home button on the right
            exifOrientation = .upMirrored
        case UIDeviceOrientation.landscapeRight:      // Device oriented horizontally, Home button on the left
            exifOrientation = .down
        case UIDeviceOrientation.portrait:            // Device oriented vertically, Home button on the bottom
            exifOrientation = .up
        default:
            exifOrientation = .up
    }
    return exifOrientation
}

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
    guard let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
    let ciimage : CIImage = CIImage(cvPixelBuffer: imageBuffer)
    let image = UIImage.init(ciImage: ciimage, scale: 1.0, orientation: orientation())
}

Выглядит как CMSampleBuffer дает вам данные RGBA, из которых вы затем непосредственно создаете изображение в градациях серого. Вам нужно будет либо создать новый буфер, где для каждого пикселя вы делаете что-то вроде gray = (pixel.red+pixel.green+pixel.blue)/3, Или вам нужно создать нормальное изображение RGBA из полученных вами данных, а затем преобразовать его в оттенки серого.

Но в вашем коде вы вообще не переходите. Вы взяли необработанный указатель на буфер, используя CVPixelBufferGetBaseAddress независимо от того, какие данные там. И затем вы просто передаете тот же указатель при создании изображения, который предполагает, что полученные данные являются оттенками серого.

Другие вопросы по тегам