Раскрасить 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):
Вот некоторый псевдокод, который я хочу сделать:
// 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
,