Вопрос относительно UIImage - ›CVPixelBuffer -› Преобразование UIImage

Я работаю над простым шумоподавляющим POC в SwiftUI, где хочу:

  1. Загрузить входное изображение
  2. Примените модель CoreML (шумоподавление) к входному изображению
  3. Отображение выходного изображения

У меня есть что-то работающее на основе десятков исходных кодов, которые я нашел в Интернете. Основываясь на том, что я читал, модель CoreML (по крайней мере, та, которую я использую) принимает CVPixelBuffer и выводит также CVPixelBuffer. Итак, моя идея заключалась в следующем:

  1. Преобразуйте входной UIImage в CVPixelBuffer
  2. Примените модель CoreML к CVPixelBuffer
  3. Преобразуйте вновь созданный CVPixelBuffer в UIImage

(Обратите внимание, что я читал, что, используя структуру Vision, можно вводить CGImage непосредственно в модель. Я попробую этот подход, как только ознакомлюсь с тем, чего я пытаюсь достичь здесь, поскольку я думаю, что это хорошее упражнение.)

Для начала я хотел пропустить шаг (2), чтобы сосредоточиться на проблеме преобразования. В приведенном ниже коде я пытался добиться:

  1. Преобразуйте входной UIImage в CVPixelBuffer
  2. Преобразуйте CVPixelBuffer в UIImage

Я не разработчик Swift или Objective-C, поэтому почти уверен, что сделал по крайней мере несколько ошибок. Я нашел этот код довольно сложным, и мне было интересно, есть ли лучший / простой способ сделать то же самое?

func convert(input: UIImage) -> UIImage? {

    // Input CGImage
    guard let cgInput = input.cgImage else {
        return nil
    }

    // Image size
    let width = cgInput.width
    let height = cgInput.height
    let region = CGRect(x: 0, y: 0, width: width, height: height)

    // Attributes needed to create the CVPixelBuffer
    let attributes = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue,
                      kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue]

    // Create the input CVPixelBuffer
    var pbInput:CVPixelBuffer? = nil
    let status = CVPixelBufferCreate(kCFAllocatorDefault,
                                     width,
                                     height,
                                     kCVPixelFormatType_32ARGB,
                                     attributes as CFDictionary,
                                     &pbInput)

    // Sanity check
    if status != kCVReturnSuccess {
        return nil
    }

    // Fill the input CVPixelBuffer with the content of the input CGImage
    CVPixelBufferLockBaseAddress(pbInput!, CVPixelBufferLockFlags(rawValue: 0))
    guard let context = CGContext(data: CVPixelBufferGetBaseAddress(pbInput!),
                                  width: width,
                                  height: height,
                                  bitsPerComponent: cgInput.bitsPerComponent,
                                  bytesPerRow: cgInput.bytesPerRow,
                                  space: cgInput.colorSpace!,
                                  bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) else {
                                    return nil
    }
    context.draw(cgInput, in: region)
    CVPixelBufferUnlockBaseAddress(pbInput!, CVPixelBufferLockFlags(rawValue: 0))

    // Create the output CGImage
    let ciOutput = CIImage(cvPixelBuffer: pbInput!)
    let temporaryContext = CIContext(options: nil)
    guard let cgOutput = temporaryContext.createCGImage(ciOutput, from: region) else {
        return nil
    }

    // Create and return the output UIImage
    return UIImage(cgImage: cgOutput)
}

Когда я использовал этот код в своем проекте SwiftUI, входные и выходные изображения выглядели одинаково, но не были идентичными. Я думаю, что у входного изображения была связанная с ним палитра (профиль ColorSync), которая была потеряна во время преобразования. Я предполагал, что должен был использовать cgInput.colorSpace во время создания CGContext, но казалось, что использование CGColorSpace(name: CGColorSpace.sRGB)! работает лучше. Может кто-нибудь объяснить мне это?

Спасибо за вашу помощь.


person Vincent Garcia    schedule 19.02.2020    source источник


Ответы (1)


Вы также можете использовать CGImage объекты с Core ML, но вам нужно создать MLFeatureValue объект вручную, а затем поместить его в MLFeatureProvider, чтобы передать его модели. Но это касается только ввода модели, а не вывода.

Другой вариант - использовать код из моего репозитория CoreMLHelpers.

person Matthijs Hollemans    schedule 19.02.2020
comment
Спасибо за Ваш ответ. Я попробую, так как это может упростить мой код, особенно преобразование CGImage в CVPixelBuffer. Но в любом случае мне приходится иметь дело с преобразованием CVPixelBuffer в UIImage. - person Vincent Garcia; 20.02.2020
comment
Это довольно просто: let image = UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer)). - person Matthijs Hollemans; 20.02.2020
comment
По какой-то причине let image = UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer)) не работает. Мне пришлось использовать CIContext, а затем createCGImage, чтобы заставить его работать. Но я действительно не уверен в коде, который у меня есть. Вот почему я создал этот пост. - person Vincent Garcia; 20.02.2020
comment
Приносим извинения за неточность. Не работая, я имел в виду, что мое выходное изображение полностью белое. Наверное, я сделал что-то не так. - person Vincent Garcia; 21.02.2020
comment
Вы имеете в виду, что у вас всегда получается полностью белое изображение? В этом случае я бы попробовал запустить модель из Python, чтобы посмотреть, что у вас получится. - person Matthijs Hollemans; 21.02.2020
comment
Я здесь даже не использую модель. Если я заменю в приведенном выше коде последнюю часть, в которой я конвертирую CVPixelBuffer в UIImage, используя то, что вы предложили, выходное изображение будет белым. Но с исходным кодом это нормально. - person Vincent Garcia; 21.02.2020