Черно-белое изображение CIFilter становится синим после смешивания

Я получаю неожиданные результаты при применении фильтров CIFilters к изображению. Ожидаемый результат - черно-белое изображение с переходом оранжевого цвета по краям. Вместо этого я получаю синее изображение с красным переходом по краям. Моя функция:

func sketch(with ciImage: CIImage) -> CIImage {

    var sourceCore = ciImage

    var convolutionValue_A:CGFloat = -0.0925937220454216
    var convolutionValue_B:CGFloat = -0.4166666567325592
    var convolutionValue_C:CGFloat = -1.8518532514572144
    var convolutionValue_D:CGFloat = 0.23148006200790405
    var convolutionValue_E:CGFloat = 4.5833334922790527
    var convolutionValue_F:CGFloat = 14.166666984558105

    var brightnessVal:CGFloat = 1.1041666269302368
    var contrastVal:CGFloat = 3.0555555820465088

    var weightsArr: [CGFloat] = [
        convolutionValue_A, convolutionValue_A, convolutionValue_B, convolutionValue_B, convolutionValue_B, convolutionValue_A, convolutionValue_A,
        convolutionValue_A, convolutionValue_B, convolutionValue_C, convolutionValue_C, convolutionValue_C, convolutionValue_B, convolutionValue_A,
        convolutionValue_B, convolutionValue_C, convolutionValue_D, convolutionValue_E, convolutionValue_D, convolutionValue_C, convolutionValue_B,
        convolutionValue_B, convolutionValue_C, convolutionValue_E, convolutionValue_F, convolutionValue_E, convolutionValue_C, convolutionValue_B,
        convolutionValue_B, convolutionValue_C, convolutionValue_D, convolutionValue_E, convolutionValue_D, convolutionValue_C, convolutionValue_B,
        convolutionValue_A, convolutionValue_B, convolutionValue_C, convolutionValue_C, convolutionValue_C, convolutionValue_B, convolutionValue_A,
        convolutionValue_A, convolutionValue_A, convolutionValue_B, convolutionValue_B, convolutionValue_B, convolutionValue_A, convolutionValue_A
    ]

    let inputWeights:CIVector = CIVector(values: weightsArr, count: weightsArr.count)

    sourceCore = sourceCore
        .applyingFilter("CIColorControls", parameters: [kCIInputImageKey: sourceCore,
                                                        kCIInputSaturationKey: 0.0,
                                                        kCIInputBrightnessKey: brightnessVal,
                                                        kCIInputContrastKey: contrastVal])

    // transforms image to only show edges in black and white
    sourceCore = sourceCore
        .applyingFilter("CIConvolution7X7", parameters: [kCIInputImageKey: sourceCore,
                                                         kCIInputWeightsKey: inputWeights]).cropped(to: sourceCore.extent)

    // give camera image a black and white Noir effect
    var ciImage = ciImage
        .applyingFilter("CIPhotoEffectNoir", parameters: [kCIInputImageKey: ciImage])


    // make solid color
    let color = CIColor(red: 0.819, green: 0.309, blue: 0.309)
    let colFilter = CIFilter(name: "CIConstantColorGenerator")!
    colFilter.setValue(color, forKey: kCIInputColorKey)
    var solidColor = colFilter.value(forKey: "outputImage") as! CIImage
    solidColor = solidColor.cropped(to: ciImage.extent)

    // color should only be shown through outlines 
    // for some reason the input image is blue-ish
    sourceCore = sourceCore
        .applyingFilter("CIBlendWithMask", parameters: [
            kCIInputImageKey: ciImage, // black and white image
            kCIInputBackgroundImageKey: solidColor, // solid color
            kCIInputMaskImageKey:sourceCore]) // edge work image

    ciImage = sourceCore

    return ciImage

}

Вот изображения на каждом этапе работы функции:

Мое исходное полноцветное изображение  Мой источник, полноцветное изображение

Вот что получается, когда я применяю CIColorControls с 0 насыщенностью и сверткой 7x7. Я использую это для своей маски, чтобы попытаться показать оранжевый цвет через черные области контура. результирующее изображение маски

Это черно-белое изображение после использования CIFilter «CIPhotoEffectNoir», которое используется в качестве входного изображения при смешивании его с маской и сплошным цветом. черно-белое изображение

Сплошной оранжевый цвет, который я использую в качестве фона при смешивании с маской. Сплошной оранжевый цвет

Изображение, полученное после выполнения CIBlendWithMask, выглядит некорректно. Изображение результата

Прежде всего, я бы ожидал, что цвет фона появится только там, где контуры находятся на изображении маски. Там, где установлен цвет фона, он выглядит оранжевым, а не красным, а входное изображение синее, а не строго черно-белое.


person Chewie The Chorkie    schedule 19.12.2019    source источник


Ответы (2)


Думаю, проблема в финальном этапе смешивания. Когда вы вызываете sourceCore.applyingFilter(...), он автоматически устанавливает sourceCore как inputImage в этом фильтре. И я не уверен, что произойдет, если вы переопределите это в parameters.

Вместо этого вы можете создать такой фильтр:

let blendFilter = CIFilter(name: "CIBlendWithMask", parameters: [
    kCIInputImageKey: ciImage,
    kCIInputBackgroundImageKey: solidColor,
    kCIInputMaskImageKey: sourceCore
])
return blendFilter.outputImage

Кстати, создать сплошное цветное изображение намного проще:

let solidColor = CIImage(color: color).cropped(to: ciImage.extent)
person Frank Schlegel    schedule 19.12.2019
comment
Спасибо за подсказку о сплошном цвете. К сожалению, результирующее изображение будет таким же, если использовать ваш код blendFilter или изменить код, чтобы он автоматически не устанавливал sourceCore в качестве inputImage. Как вы думаете, может ли это быть связано с установкой значений за пределами ожидаемых значений? - person Chewie The Chorkie; 19.12.2019
comment
Интересно ... Не могли бы вы изменить фильтр наложения на CIBlendWithRedMask и посмотреть, как будет выглядеть результат? - person Frank Schlegel; 19.12.2019
comment
Конечно. Это точно так же, как полученное изображение, показанное в моем вопросе. - person Chewie The Chorkie; 19.12.2019
comment
Не могли бы вы попробовать переписать части, в которых используется .applyingFilter, во что-то подобное выше? Я все еще спотыкаюсь об этом замечании в документации: этот метод, хотя и удобен, неэффективен, если использовать его несколько раз подряд. Добейтесь лучшей производительности, объединяя фильтры в цепочку, не запрашивая выходы отдельных фильтров. Также установка параметра kCIInputImageKey при использовании этого метода должна быть ненужной и может вызвать побочные эффекты? - person Frank Schlegel; 19.12.2019
comment
Фильтр отображается правильно, если я заменяю фильтр CIConvolution7X7 фильтром CIEdgeWork. (На черно-белом изображении есть оранжевые края) Проблема с CIEdgeWork заключается в том, что он больше не работает одинаково для разных устройств и версий iOS. Фильтры CIConvolution7X7 и CIEdgeWork дают черно-белое изображение. У меня есть ощущение, что что-то не так с информацией, которая составляет CIImage, созданную с помощью фильтра CIConvolution7X7, который неправильно работает с маской. - person Chewie The Chorkie; 19.12.2019
comment
За эти годы я сообщил Apple о нескольких проблемах, но они так и не решили заняться их исправлением. Я думаю, что на данном этапе фильтры CIFilters следует пометить как устаревшие. - person Chewie The Chorkie; 20.12.2019
comment
Спасибо за помощь, но я понял, как заставить его работать. Мне нужно было смешать результат свертки с белым фоном с помощью CIColorDodgeBlendMode. - person Chewie The Chorkie; 20.12.2019
comment
Интересно, может ли свертка также влиять на альфа-канал и тем самым создавать те странные результаты, когда альфа предварительно умножается. Может быть, вы можете попробовать вызвать .settingAlphaOne(in: sourceCore.extent) для результата свертки. - person Frank Schlegel; 21.12.2019
comment
Фрэнк, это действительно классный сеттинг, о котором я никогда не слышал. Я попытался применить его к фильтру свертки, который я там установил. Похоже, никакого эффекта нет. Любые идеи? - person Chewie The Chorkie; 23.12.2019
comment
Честно говоря, я довольно невежественен. Мне нужно было бы самому поэкспериментировать с кодом. - person Frank Schlegel; 23.12.2019

Разобрался, хотя я не уверен, почему это работает. Я предполагаю, что это ошибка техники Apple. После CIConvolution7X7 мне нужно было смешать это черно-белое изображение с белым фоном с помощью CIColorDodgeBlendMode или CILinearDodgeBlendMode.

Окончательный код:

func sketch(with ciImage: CIImage) -> CIImage {

    var sourceCore = ciImage

    var convolutionValue_A:CGFloat = -0.0925937220454216
    var convolutionValue_B:CGFloat = -0.4166666567325592
    var convolutionValue_C:CGFloat = -1.8518532514572144
    var convolutionValue_D:CGFloat = 0.23148006200790405
    var convolutionValue_E:CGFloat = 4.5833334922790527
    var convolutionValue_F:CGFloat = 14.166666984558105

    var brightnessVal:CGFloat = 1.1041666269302368
    var contrastVal:CGFloat = 3.0555555820465088

    var weightsArr: [CGFloat] = [
        convolutionValue_A, convolutionValue_A, convolutionValue_B, convolutionValue_B, convolutionValue_B, convolutionValue_A, convolutionValue_A,
        convolutionValue_A, convolutionValue_B, convolutionValue_C, convolutionValue_C, convolutionValue_C, convolutionValue_B, convolutionValue_A,
        convolutionValue_B, convolutionValue_C, convolutionValue_D, convolutionValue_E, convolutionValue_D, convolutionValue_C, convolutionValue_B,
        convolutionValue_B, convolutionValue_C, convolutionValue_E, convolutionValue_F, convolutionValue_E, convolutionValue_C, convolutionValue_B,
        convolutionValue_B, convolutionValue_C, convolutionValue_D, convolutionValue_E, convolutionValue_D, convolutionValue_C, convolutionValue_B,
        convolutionValue_A, convolutionValue_B, convolutionValue_C, convolutionValue_C, convolutionValue_C, convolutionValue_B, convolutionValue_A,
        convolutionValue_A, convolutionValue_A, convolutionValue_B, convolutionValue_B, convolutionValue_B, convolutionValue_A, convolutionValue_A
    ]

    let inputWeights:CIVector = CIVector(values: weightsArr, count: weightsArr.count)

    sourceCore = sourceCore
        .applyingFilter("CIColorControls", parameters: [kCIInputImageKey: sourceCore,
                                                        kCIInputSaturationKey: 0.0,
                                                        kCIInputBrightnessKey: brightnessVal,
                                                        kCIInputContrastKey: contrastVal])

    // transforms image to only show edges in black and white
    sourceCore = sourceCore
        .applyingFilter("CIConvolution7X7", parameters: [kCIInputImageKey: sourceCore,

    let whiteCIColor = CIColor(red: 1, green: 1, blue: 1)

    let whiteColor = CIImage(color: whiteCIColor).cropped(to: ciImage.extent)

    // CIColorDodgeBlendMode, CILinearDodgeBlendMode

    sourceCore = sourceCore
            .applyingFilter("CIColorDodgeBlendMode", parameters: [kCIInputImageKey: sourceCore,
                                                                    kCIInputBackgroundImageKey: whiteColor])
                                                         kCIInputWeightsKey: inputWeights]).cropped(to: sourceCore.extent)

    // give camera image a black and white Noir effect
    var ciImage = ciImage
        .applyingFilter("CIPhotoEffectNoir", parameters: [kCIInputImageKey: ciImage])


    // make solid color
    let color = CIColor(red: 0.819, green: 0.309, blue: 0.309)
    let colFilter = CIFilter(name: "CIConstantColorGenerator")!
    colFilter.setValue(color, forKey: kCIInputColorKey)
    var solidColor = colFilter.value(forKey: "outputImage") as! CIImage
    solidColor = solidColor.cropped(to: ciImage.extent)

    // color is shown through outlines correctly, 
    // and image is black and white
    sourceCore = sourceCore
        .applyingFilter("CIBlendWithMask", parameters: [
            kCIInputImageKey: ciImage, // black and white image
            kCIInputBackgroundImageKey: solidColor, // solid color
            kCIInputMaskImageKey:sourceCore]) // edge work image

    ciImage = sourceCore

    return ciImage

}
person Chewie The Chorkie    schedule 19.12.2019