Раскрасить UIImage в Swift - PullRequest
0 голосов
/ 07 мая 2018

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

Вот что у меня есть:

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
        }
    }
}

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

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

Original pictures

Сверху вниз: изображение, отфильтрованное с помощью 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)

Screenshot


EDIT

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

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

  • s.r = точка (s, redVector)
  • s.g = точка (s, greenVector)
  • s.b = точка (с, blueVector)
  • s.a = точка (с, alphaVector)
  • s = s + смещение

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

Fliterpedia screenshot

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

// 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 ]

0 голосов
/ 11 мая 2018

Рад, что помог.Если можно, одно.Вы можете отправить цвет в ядро ​​- это 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.
0 голосов
/ 10 мая 2018

Я наконец-то написал 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].

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

...