Кадр CMSampleBuffer, преобразованный в vImage, имеет неправильные цвета

Я пытаюсь преобразовать CMSampleBuffer из вывода камеры в vImage, а затем применить некоторую обработку. К сожалению, даже без дальнейшего редактирования кадр, который я получаю из буфера, имеет неправильные цвета:

Пример изображения текущей реализации

Реализация (управление памятью и ошибки не рассматриваются):

Настройка устройства вывода видео:

    videoDataOutput = AVCaptureVideoDataOutput()
    videoDataOutput.videoSettings = [String(kCVPixelBufferPixelFormatTypeKey): kCVPixelFormatType_32BGRA]
    videoDataOutput.alwaysDiscardsLateVideoFrames = true
    videoDataOutput.setSampleBufferDelegate(self, queue: captureQueue)
    videoConnection = videoDataOutput.connection(withMediaType:  AVMediaTypeVideo)

    captureSession.sessionPreset = AVCaptureSessionPreset1280x720

    let videoDevice = AVCaptureDevice.defaultDevice(withMediaType:  AVMediaTypeVideo)
    guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else {
        return
    }

Создание vImage из CASampleBuffer полученных с камеры:

   // Convert `CASampleBuffer` to `CVImageBuffer`
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

    var buffer: vImage_Buffer = vImage_Buffer()
    buffer.data = CVPixelBufferGetBaseAddress(pixelBuffer)
    buffer.rowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer)
    buffer.width = vImagePixelCount(CVPixelBufferGetWidth(pixelBuffer))
    buffer.height = vImagePixelCount(CVPixelBufferGetHeight(pixelBuffer))

    let vformat = vImageCVImageFormat_CreateWithCVPixelBuffer(pixelBuffer)
    let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)

    var cgFormat = vImage_CGImageFormat(bitsPerComponent: 8,
                                        bitsPerPixel: 32,
                                        colorSpace: nil,
                                        bitmapInfo: bitmapInfo,
                                        version: 0,
                                        decode: nil,
                                        renderingIntent: .defaultIntent)

     // Create vImage
     vImageBuffer_InitWithCVPixelBuffer(&buffer, &cgFormat, pixelBuffer, vformat!.takeRetainedValue(), cgColor, vImage_Flags(kvImageNoFlags))

Преобразование буфера в UIImage:

Для тестов CVPixelBuffer экспортируется в UIImage, но добавление его в видеобуфер дает тот же результат.

var dstPixelBuffer: CVPixelBuffer?

    let status = CVPixelBufferCreateWithBytes(nil, Int(buffer.width), Int(buffer.height),
                                              kCVPixelFormatType_32BGRA, buffer.data,
                                              Int(buffer.rowBytes), releaseCallback,
                                              nil, nil, &dstPixelBuffer)

    let destCGImage = vImageCreateCGImageFromBuffer(&buffer, &cgFormat, nil, nil, numericCast(kvImageNoFlags), nil)?.takeRetainedValue()

    // create a UIImage
    let exportedImage = destCGImage.flatMap { UIImage(cgImage: $0, scale: 0.0, orientation: UIImageOrientation.right) }

    DispatchQueue.main.async {
        self.previewView.image = exportedImage
    }

person Dawid    schedule 10.09.2017    source источник
comment
Помогает ли удаление | CGBitmapInfo.byteOrder32Little.rawValue? Ваша желтая утка изменилась с FFFF00 на 00FF00, так что может быть проблемой подкачки байтов, если вы включите нулевую альфу.   -  person Rhythmic Fistman    schedule 10.09.2017
comment
@RhythmicFistman, это интересно. Я попытался удалить byteOrder32Little, а также изменить на CGBitmapInfo(rawValue: CGImageByteOrderInfo.orderMask.rawValue | CGImageAlphaInfo.last.rawValue), чтобы сохранить порядок по умолчанию, но результат во всех случаях одинаков.   -  person Dawid    schedule 10.09.2017
comment
Можете ли вы дать ссылку на работающий образец?   -  person Rhythmic Fistman    schedule 10.09.2017
comment
Конечно - github.com/cieslakdawid/vImageExample   -  person Dawid    schedule 10.09.2017


Ответы (2)


Попробуйте установить цветовое пространство в формате изображения CV:

    let vformat = vImageCVImageFormat_CreateWithCVPixelBuffer(pixelBuffer).takeRetainedValue()

    vImageCVImageFormat_SetColorSpace(vformat,
                                      CGColorSpaceCreateDeviceRGB())

... и обновите свой вызов на vImageBuffer_InitWithCVPixelBuffer, чтобы отразить тот факт, что vformat теперь является управляемой ссылкой:

    let error = vImageBuffer_InitWithCVPixelBuffer(&buffer, &cgFormat, pixelBuffer, vformat, nil, vImage_Flags(kvImageNoFlags))

Наконец, вы можете удалить следующие строки, vImageBuffer_InitWithCVPixelBuffer сделает эту работу за вас:

//        buffer.data = CVPixelBufferGetBaseAddress(pixelBuffer)
//        buffer.rowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer)
//        buffer.width = vImagePixelCount(CVPixelBufferGetWidth(pixelBuffer))
//        buffer.height = vImagePixelCount(CVPixelBufferGetHeight(pixelBuffer))

Обратите внимание, что вам не нужно блокировать пиксельный буфер Core Video, если вы проверите заголовок, в нем говорится: «Нет необходимости блокировать CVPixelBuffer перед вызовом этой функции».

person Simon Gladman    schedule 11.01.2018
comment
Я вернулся к этой проблеме и только что нашел ваш совет. Настройка цветового пространства, которое было недостающей головоломкой! Большое спасибо за все советы!!! - person Dawid; 24.05.2018

Вызов vImageBuffer_InitWithCVPixelBuffer выполняет изменение содержимого ваших vImage_Buffer и CVPixelBuffer, что немного неприлично, потому что в вашем (связанном) коде вы обещаете не изменять пиксели, когда говорите

CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)

Правильный способ инициализации CGBitmapInfo для BGRA8888 — это альфа-первый 32-битный прямой порядок байтов, что неочевидно, но описано в заголовочном файле для vImage_CGImageFormat в vImage_Utilities.h:

let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue | CGImageByteOrderInfo.order32Little.rawValue)

Чего я не понимаю, так это почему vImageBuffer_InitWithCVPixelBuffer изменяет ваш буфер, поскольку cgFormat (desiredFormat) должно соответствовать vformat, хотя задокументировано изменение буфера, поэтому, возможно, вам следует сначала скопировать данные.

person Rhythmic Fistman    schedule 11.09.2017