Раскрасить UIImage в Swift

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

Вот что у меня так далеко:

extension UIImage {

    func applyColorMask(color: UIColor, context: CIContext) -> UIImage {

        guard let cgImageInput = self.cgImage else {
            print("applyColorMask: \(self) has no cgImage attribute.")
            return self
        }

        // Throw away existing colors, and fill the non transparent pixels with the input color
        // s.r = dot(s, redVector), s.g = dot(s, greenVector), s.b = dot(s, blueVector), s.a = dot(s, alphaVector)
        // s = s + bias
        let colorFilter = CIFilter(name: "CIColorMatrix")!
        let ciColorInput = CIColor(cgColor: color.cgColor)
        colorFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputRVector")
        colorFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputGVector")
        colorFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputBVector")
        colorFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 1), forKey: "inputAVector")
        colorFilter.setValue(CIVector(x: ciColorInput.red, y: ciColorInput.green, z: ciColorInput.blue, w: 0), forKey: "inputBiasVector")
        colorFilter.setValue(CIImage(cgImage: cgImageInput), forKey: kCIInputImageKey)

        if let cgImageOutput = context.createCGImage(colorFilter.outputImage!, from: colorFilter.outputImage!.extent) {
            return UIImage(cgImage: cgImageOutput)
        } else {
            print("applyColorMask: failed to apply filter to \(self)")
            return self
        }
    }
}

Код работает хорошо для черного и белого, но не так, как я ожидал, применяя смешные цвета. Смотрите оригинальное изображение и скриншоты ниже: один и тот же цвет используется для рамки и для изображения. Хотя они разные. Моя функция работает неправильно. Я что-то упустил в матрице фильтра?

Исходное изображение (в центре белая точка):

Оригинальные картинки

Сверху вниз: изображение отфильтровано UIColor(1.0, 1.0, 1.0, 1.0) вставлен в UIImageView, который имеет границы того же цвета. Тогда то же самое с UIColor(0.6, 0.5, 0.4, 1.0), И наконец с UIColor(0.2, 0.5, 1.0, 1.0)

Скриншот


РЕДАКТИРОВАТЬ

Запуск Filterpedia дает мне тот же результат. Мое понимание фильтра CIColorMatrix может быть неправильным. В документации сказано:

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

  • sr = точка (s, redVector)
  • sg = точка (s, greenVector)
  • sb = точка (s, blueVector)
  • sa = точка (s, alphaVector)
  • s = s + смещение

Затем, допустим, я выбрасываю все данные RGB с (0,0,0,0) вектором, а затем передаю (0,5, 0, 0, 0) середину красного в качестве вектора смещения; Я ожидаю, что мое изображение будет иметь все свои полностью непрозрачные пиксели (127, 0, 0). Снимки экрана ниже показывают, что он немного светлее (красный =186):

Скриншот Fliterpedia

Вот некоторый псевдокод, который я хочу сделать:

// image "im" is a vector of pixels
// pixel "p" is struct of rgba values
// color "col" is the input color or a struct of rgba values
for (p in im) {
    p.r = col.r
    p.g = col.g
    p.b = col.b
    // Nothing to do with the alpha channel
}

2 ответа

Решение

Я наконец написал CIColorKernel, как предложил @dfd, и он отлично работает:

class ColorFilter: CIFilter {

    var inputImage: CIImage?
    var inputColor: CIColor?

    let kernel: CIColorKernel = {
        let kernelString = "kernel vec4 colorize(__sample pixel, vec4 color)\n"
            + "{\n"
            + "    pixel.rgb = color.rgb;\n"
            + "    return pixel;\n"
            + "}\n"

        return CIColorKernel(source: kernelString)!
    }()

    override var outputImage: CIImage? {

        guard let inputImage = inputImage else {
            print("\(self) cannot produce output because no input image provided.")
            return nil
        }
        guard let inputColor = inputColor else {
            print("\(self) cannot produce output because no input color provided.")
            return nil
        }

        let inputs = [inputImage, inputColor] as [Any]
        return kernel.apply(extent: inputImage.extent, arguments: inputs)
    }
}

Подводя итог, можно сказать, что использованная мной CIColorMatrix вначале не является линейной (при использовании вектора смещения). При задании красного значения 0,5 (с плавающей запятой) изображение с красным цветом 127 не выводилось в интервале [0-255].

Написание собственного фильтра было моим решением.

Рад, что помог. Если можно, одна вещь. Вы можете отправить цвет в ядро ​​- это vec4с четвертым значением, являющимся альфа-каналом. Просто помните, что CoreImage использует 0-1, а не 0-254.

Вот код ядра:

kernel vec4 colorize(__sample pixel, vec4 color) {
    pixel.rgb = color.rgb;
    return pixel;
}

Это почти так же, как у вас. Но теперь все, что вам нужно сделать, это создать CIColor вместо изображения. Если у вас уже есть UIColor под названием inputColorПросто сделайте это:

let ciColor = CIColor(color: inputColor)
var inputs = [inputImage, ciColor] as [Any]

Пара FYI.

  • Там также есть vec3 это можно использовать, но так как у вас уже есть UIColor `vec4 выглядит самым простым способом.
  • __sample значение обрабатываемого пикселя, так что его "базовый тип" действительно vec4,
Другие вопросы по тегам