首页 > 解决方案 > 在 Swift 中为 UIImage 着色

问题描述

我正在尝试编写一个将颜色蒙版应用于给定图像的辅助函数。我的功能必须将图像的所有不透明像素设置为相同的颜色。

这是我到目前为止所拥有的:

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 = dot(s, alphaVector)
  • s = s + 偏差

然后,假设我用 (0,0,0,0) 向量抛出所有 RGB 数据,然后传递 (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
}

标签: swiftimage-processingcifilter

解决方案


正如@dfd 建议的那样,我终于写了一个 CIColorKernel,它工作正常:

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(浮点数)值不会在 [0-255] 区间内输出红色 127 颜色的图像。

编写自定义过滤器是我的解决方案。


推荐阅读