MTKView отображает цветовое пространство Wide Gamut P3

Я создаю редактор фотографий в реальном времени на основе CIFilters и MetalKit. Но у меня возникла проблема с отображением изображений с широкой гаммой в MTKView.

Стандартные изображения sRGB отображаются нормально, но изображения Display P3 размыты.

Я попытался установить цветовое пространство CIContext.render в качестве цветового пространства изображения, но проблема все еще возникает.

Вот фрагменты кода:

 guard let inputImage = CIImage(mtlTexture: sourceTexture!) else { return }
                let outputImage = imageEditor.processImage(inputImage)
                print(colorSpace)
                context.render(outputImage,
                               to: currentDrawable.texture,
                               commandBuffer: commandBuffer,
                               bounds: inputImage.extent,
                               colorSpace: colorSpace)
                commandBuffer?.present(currentDrawable)

let pickedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
        print(pickedImage.cgImage?.colorSpace)
        if let cspace = pickedImage.cgImage?.colorSpace {
            colorSpace = cspace
        }

Я нашел похожую проблему на форумах разработчиков Apple, но без ответов: https://forums.developer.apple.com/thread/66166


person Kevin    schedule 25.07.2017    source источник
comment
Я не уверен, что это возможно в iOS... В macOS у MTKView есть свойство colorSpace, которое вы можете установить, но, по-видимому, оно не поддерживается в iOS :( developer.apple.com/documentation/metalkit/mtkview/   -  person endavid    schedule 29.03.2018


Ответы (1)


Для поддержки широкой цветовой гаммы вам необходимо установить colorPixelFormat вашего MTKView на BGRA10_XR или bgra10_XR_sRGB. Я подозреваю, что свойство colorSpace macOS MTKViews не будет поддерживаться на iOS, потому что управление цветом в iOS не активно, а целевое (читайте Рекомендации по управлению цветом).

Не видя ваших изображений и их фактических значений, трудно поставить диагноз, но я объясню свои выводы и эксперименты. Я предлагаю вам начать, как я, с отладки одного цвета.

Например, какая самая красная точка в цветовом пространстве P3? Его можно определить через UIColor следующим образом:

UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1)

Добавьте UIButton в свое представление с фоном, установленным на этот цвет, для целей отладки. Вы можете либо получить компоненты в коде, чтобы увидеть, какими будут эти значения в sRGB,

    var fRed : CGFloat = 0
    var fGreen : CGFloat = 0
    var fBlue : CGFloat = 0
    var fAlpha : CGFloat = 0
    let c = UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1)
    c.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha)

или вы можете использовать калькулятор в macOS Color Sync Utility,

Утилита синхронизации цвета

Убедитесь, что вы выбрали Расширенный диапазон, иначе значения будут ограничены 0 и 1.

Итак, как видите, ваш P3(1, 0, 0) соответствует (1,0930, -0,2267, -0,1501) в расширенном sRGB.

Теперь вернемся к вашему MTKView,

  • Если вы установите для colorPixelFormat вашего MTKView значение .BGRA10_XR, вы получите самый яркий красный цвет, если вывод вашего шейдера,

    (1.0930, -0.2267, -0.1501)

  • Если вы установите colorPixelFormat вашего MTKView на .bgra10_XR_sRGB, вы получите самый яркий красный цвет, если вывод вашего шейдера,

    (1.22486, -0.0420312, -0.0196301)

    потому что вам нужно написать линейное значение RGB, так как этот формат текстуры применит для вас гамма-коррекцию. Будьте осторожны при применении инверсной гаммы, так как есть отрицательные значения. Я использую эту функцию,

    let f = {(c: Float) -> Float in
        if fabs(c) <= 0.04045 {
            return c / 12.92
        }
        return sign(c) * powf((fabs(c) + 0.055) / 1.055, 2.4)
    }
    

Последняя недостающая деталь — это создание широкой гаммы UIImage. Установите цветовое пространство на CGColorSpace.displayP3 и скопируйте данные. Но какие данные, верно? Самый яркий красный цвет на этом изображении будет

(1, 0, 0)

или (65535, 0, 0) в 16-битных целых числах.

Что я делаю в своем коде, так это использую текстуры .rgba16Unorm для управления изображениями в цветовом пространстве displayP3, где (1, 0, 0) будет самым ярким красным цветом в P3. Таким образом, я могу напрямую скопировать его содержимое в файл UIImage. Затем для отображения я передаю преобразование цвета в шейдер для преобразования из P3 в расширенный sRGB (то есть без насыщения цветов) перед отображением. Я использую линейный цвет, поэтому мое преобразование представляет собой просто матрицу 3x3. Я установил свой вид на .bgra10_XR_sRGB, поэтому гамма будет применяться автоматически для меня.

Эта (столбцовая) матрица,

 1.2249  -0.2247  0
-0.0420   1.0419  0
-0.0197  -0.0786  1.0979

Вы можете прочитать о том, как я его сгенерировал, здесь: Изучение цветового пространства display-P3

Вот пример, который я создал с помощью UIButtons и MTKView, сделанный на iPhoneX.

Красный MTKView

Кнопка слева имеет самый яркий красный цвет в sRGB, а кнопка справа использует цвет displayP3. В центре я разместил MTKView, который выводит преобразованный линейный цвет, как описано выше.

Тот же эксперимент для зеленого, Зеленый MTKView

Теперь, если вы видите это на последнем iPhone или iPad, вы должны увидеть, что и квадрат в центре, и кнопка справа имеют одинаковые яркие цвета. Если вы видите это на Mac, который не может их отображать, левая кнопка будет иметь тот же цвет. Если вы видите это на компьютере с Windows или в браузере без надлежащего управления цветом, левая кнопка также может иметь другой цвет, но это только потому, что все изображение интерпретируется как sRGB, и, очевидно, эти пиксели имеют разные значения... Но внешний вид не будет правильным.

Если вам нужны дополнительные ссылки, проверьте модульный тест testP3UIColor, который я добавил здесь: ColorTests.swift,

мои функции для инициализации UIImage: Image.swift< / а>,

и пример приложения для опробования преобразований: SampleColorPalette.

Я не экспериментировал с CIImages, но, думаю, применимы те же принципы.

Я надеюсь, что эта информация чем-то поможет. Мне также потребовалось много времени, чтобы понять, как правильно отображать цвета, потому что я не смог найти явную ссылку на поддержку displayP3 в документации Metal SDK.

person endavid    schedule 30.03.2018
comment
На самом деле 1.22486, -0.0420312, -0.0196301 не самый яркий красный для формата bgra10_xr_srgb пикселей, 1.358, -0.074, -0.012 есть. Джастин Стойлс сказал, что на сеансе Работа с широким цветом вы можете проверить это можно сделать, поместив UIView с цветом фона 1.0, 0.0, 0.0, 1.0 Display P3 между MTKViews, отображающими как 1.22486, -0.0420312, -0.0196301, так и 1.358, -0.074, -0.012 цвета. - person Vahan Babayan; 28.11.2018
comment
Кажется, что формат bgra10_xr_srgb пикселей использует другое цветовое пространство, чем CGColorSpace.extendedSRGB или CGColorSpace.extendedLinearSRGB. Я был бы признателен за любую помощь в получении матрицы преобразования из дисплея P3 в это цветовое пространство. - person Vahan Babayan; 28.11.2018
comment
Спасибо за ссылку. Не знаю, откуда взялось это 1.358. Если вы создаете UIColor displayP3, let c = UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1); c.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha), красным значением будет 1.0930339097976685. Чтобы он стал 1.358 при преобразовании в линейное пространство, вам понадобится гамма gamma=log(1.358)/log(1.093)=3.44, что смехотворно велико. Могу поспорить, что они использовали обычный расчет гаммы: linear=((c+0.055)/1.055)^2.4 и забыли разделить на 1,055 или что-то в этом роде. Грубое предположение... - person endavid; 09.12.2018