Как построить матрицу 5x5 для применения в качестве цветовой матрицы в Swift?

Мне снова нужна помощь в преобразовании моего приложения Android в iOS...

Как я упоминал в другом посте, приложение предназначено для обработки изображений и основано на ColorMatrix. Проблема в том, что Swift поддерживает (насколько мне известно...) только матрицу 4x4, а для яркости или контрастности я использую матрицу 5x5 в Android.

В Android это моя матрица:

Яркость:

float[] brightMatrix = {
                1f, 0f, 0f, 0f, 32f,
                0f, 1f, 0f, 0f, 32f,
                0f, 0f, 1f, 0f, 32f,
                0f, 0f, 0f, 1f,  0f,
                0f, 0f, 0f, 0f,  1f};

Контраст:

float[] contMatrix = {
                1.190f,     0f,     0f, 0f, -12.065f,
                    0f, 1.190f,     0f, 0f, -12.065f,
                    0f,     0f, 1.190f, 0f, -12.065f,
                    0f,     0f,     0f, 1f,       0f,
                    0f,     0f,     0f, 0f,       1f};

Но я действительно не знаю, как получить такие же результаты в Swift...

В случае яркости я попытался использовать этот код, разделив 32/255, чтобы получить значение 0,12549:

func zoeFilter() -> UIImage? {
   let inImage = CIImage (image: self)
   dynamic let brightFactor = CIFilter (name: "CIColorControls")
   brightFactor?.setValue(inImage, forKey: kCIInputImageKey)
   brightFactor?.setValue(0.12549, forKey: kCIInputBrightnessKey)
   let brightImage = brightFactor?.outputImage
   let cgImage = CIContext().createCGImage(brightImage!, from: brightImage!.extent)
   return UIImage (cgImage: cgImage!)
}

Но результат совсем не тот... в iOS это как белый туман...

И говоря о контрасте, я нашел следующий код, но я не могу преобразовать матрицу Android только в значение здесь contFactor?.setValue(1, forKey: kCIInputContrastKey) просто зная, что 1 равно нейтральному:

func chiaFilter() -> UIImage? {
   let inImage = CIImage (image: self)
   dynamic let contFactor = CIFilter (name: "CIColorControls")
   contFactor?.setValue(inImage, forKey: kCIInputImageKey)
   contFactor?.setValue(1, forKey: kCIInputContrastKey)
   let contImage = contFactor?.outputImage
   let cgImage = CIContext().createCGImage(contImage!, from: contImage!.extent)
   return UIImage (cgImage: cgImage!)
}

С другой стороны, изменить экспозицию легко, потому что матрица в Android выглядит так:

float[] expMatrix = {
                1.3f,   0f,   0f, 0f, 0f,
                  0f, 1.3f,   0f, 0f, 0f,
                  0f,   0f, 1.3f, 0f, 0f,
                  0f,   0f,   0f, 1f, 0f,
                  0f,   0f,   0f, 0f, 1f};

Итак, в Swift я могу использовать следующий код, получая точно такой же результат:

func liaFilter() -> UIImage? {
   let inImage = CIImage (image: self)
   dynamic let expoMatrix = CIFilter (name: "CIColorMatrix")
   expoMatrix?.setDefaults()
   expoMatrix?.setValue(inImage, forKey: kCIInputImageKey)
   expoMatrix?.setValue(CIVector (x: 1.3, y: 0, z: 0, w: 0), forKey: "inputRVector")
   expoMatrix?.setValue(CIVector (x: 0, y: 1.3, z: 0, w: 0), forKey: "inputGVector")
   expoMatrix?.setValue(CIVector (x: 0, y: 0, z: 1.3, w: 0), forKey: "inputBVector")
   expoMatrix?.setValue(CIVector (x: 0, y: 0, z: 0, w: 1), forKey: "inputAVector")
   let expoImage = expoMatrix?.outputImage
   let cgImage = CIContext().createCGImage(expoImage!, from: expoImage!.extent)
   return UIImage (cgImage: cgImage!)
}

Это так запутанно... Я знаю... но было бы здорово, если бы у кого-нибудь была идея, как решить эти проблемы между Android и iOS.

Заранее спасибо!

ИЗДАНИЕ ПОСЛЕ ОТВЕТА ФРАНКА

Вот исходное изображение: введите здесь описание изображения

Вот после применения фильтра с Android с помощью:

float[] brightMatrix = {
                1f, 0f, 0f, 0f, 32f,
                0f, 1f, 0f, 0f, 32f,
                0f, 0f, 1f, 0f, 32f,
                0f, 0f, 0f, 1f,  0f,
                0f, 0f, 0f, 0f,  1f};

введите здесь описание изображения

Вот то же изображение, применяющее фильтр с iOS, используя:

brightMatrix?.setValue(CIVector (x: 1, y: 0, z: 0, w: 0), forKey: "inputRVector")
brightMatrix?.setValue(CIVector (x: 0, y: 1, z: 0, w: 0), forKey: "inputGVector")
brightMatrix?.setValue(CIVector (x: 0, y: 0, z: 1, w: 0), forKey: "inputBVector")
brightMatrix?.setValue(CIVector (x: 0, y: 0, z: 0, w: 1), forKey: "inputAVector")
brightMatrix?.setValue(CIVector (x: 32.0/255.0, y: 32.0/255.0, z: 32.0/255.0, w: 0), forKey: "inputBiasVector")

введите здесь описание изображения

ФИНАЛЬНОЕ РЕШЕНИЕ, РАБОТАЮЩЕЕ ПОСЛЕ ПРЕДЛОЖЕНИЯ ФРАНКА

func zoeFilter() -> UIImage? {
        let inImage = CIImage (image: self)

        dynamic let SRGBMatrix = CIFilter (name: "CILinearToSRGBToneCurve")
        dynamic let brightMatrix = CIFilter (name: "CIColorMatrix")
        dynamic let linearMatrix = CIFilter (name: "CISRGBToneCurveToLinear")

        SRGBMatrix?.setValue(inImage, forKey: kCIInputImageKey)
        let SRGBImage = SRGBMatrix?.outputImage

        brightMatrix?.setDefaults()
        brightMatrix?.setValue(SRGBImage, forKey: kCIInputImageKey)
        brightMatrix?.setValue(CIVector (x: 1, y: 0, z: 0, w: 0), forKey: "inputRVector")
        brightMatrix?.setValue(CIVector (x: 0, y: 1, z: 0, w: 0), forKey: "inputGVector")
        brightMatrix?.setValue(CIVector (x: 0, y: 0, z: 1, w: 0), forKey: "inputBVector")
        brightMatrix?.setValue(CIVector (x: 0, y: 0, z: 0, w: 1), forKey: "inputAVector")
        brightMatrix?.setValue(CIVector (x: 32.0/255.0, y: 32.0/255.0, z: 32.0/255.0, w: 0), forKey: "inputBiasVector")
        let brightImage = brightMatrix?.outputImage

        linearMatrix?.setDefaults()
        linearMatrix?.setValue(brightImage, forKey: kCIInputImageKey)
        let linearImage = linearMatrix?.outputImage

        let cgImage = CIContext().createCGImage(linearImage!, from: linearImage!.extent)
        return UIImage (cgImage: cgImage!)
    }

person Tsilaicos    schedule 22.02.2020    source источник
comment
Рад, что это сработало! Два дополнения: (1) Вам не нужно setDefaults на фильтрах, если вы все равно установили все параметры (или у фильтра их нет). (2) Вы можете использовать ярлык let SRGBImage = inImage.applyingFilter("CILinearToSRGBToneCurve"), чтобы сохранить несколько строк в каждой.   -  person Frank Schlegel    schedule 25.02.2020
comment
Я возьму эти советы!   -  person Tsilaicos    schedule 25.02.2020


Ответы (1)


Вы должны иметь возможность использовать свои матрицы яркости и контрастности, также установив параметр inputBiasVector фильтра CIColorMatrix следующим образом:

// the first 4 should actually be the default, so you probably don't need to set them explicitly
brightMatrix?.setValue(CIVector(x: 1, y: 0, z: 0, w: 0), forKey: "inputRVector")
brightMatrix?.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputGVector")
brightMatrix?.setValue(CIVector(x: 0, y: 0, z: 1, w: 0), forKey: "inputBVector")
brightMatrix?.setValue(CIVector(x: 0, y: 0, z: 0, w: 1), forKey: "inputAVector")
brightMatrix?.setValue(CIVector(x: 32.0/255.0, y: 32.0/255.0, z: 32.0/255.0, w: 0), forKey: "inputBiasVector")

Странно, однако, что вы не можете использовать фильтр CIColorControls для достижения того же. Может быть, вы можете разместить примеры изображений.

person Frank Schlegel    schedule 22.02.2020
comment
Спасибо Фрэнк за ответ! Полезно знать о векторе смещения, хотя результат тот же, что и при использовании CIColorControl... Я добавил несколько изображений. - person Tsilaicos; 23.02.2020
comment
Это может быть связано с гамма-коррекцией (или ее отсутствием). Похоже, ваш фильтр Android работает с гамма-кривой sRGB, тогда как Core Image обычно использует линейное распределение. Может быть, вы могли бы попробовать добавить фильтр CILinearToSRGBToneCurve в свой конвейер прямо перед цветовой матрицей, а CISRGBToneCurveToLinear после него (или наоборот, я не уверен...). - person Frank Schlegel; 23.02.2020
comment
Мужик, ты гений! Большое вам спасибо, потому что это был ключ... Завтра я опубликую окончательный код, который работает на основе вашей помощи. Спасибо еще раз! - person Tsilaicos; 24.02.2020
comment
Окончательный код добавлен как решение! Еще раз спасибо, Фрэнк! - person Tsilaicos; 24.02.2020