【问题标题】:Colorize a UIImage in Swift在 Swift 中为 UIImage 着色
【发布时间】:2018-05-11 21:24:18
【问题描述】:

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

这是我目前所拥有的:

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 过滤器的理解可能是错误的。 documentation 说:

此过滤器执行矩阵乘法,如下所示,以变换 颜色向量:

  • s.r = dot(s, redVector)
  • s.g = dot(s, greenVector)
  • s.b = dot(s, blueVector)
  • s.a = 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
}

【问题讨论】:

  • 如果将向量 (x:y:z:w:) 设置为:1,0,0,0 为红色,0,1,0,0 为绿色,0,0,1,0 为蓝色,您将获得正常外观的图片.现在,要将红色通道减半,请将inputRVector 设置为0.5,0,0,0。要删除 all 红色,请将其设置为 0,0,0,0。这基本上就是我使用它的方式。我确信还有其他组合可以获得其他结果,但这应该可以帮助您入门。
  • 这是 Filterpedia 的 Swift 4 版本 - github.com/rhoeper/Filterpedia-Swift4 我还没有尝试过(我有自己的 Swift 4 端口),但它是尝试每个过滤器的绝佳资源。所有输入参数都显示为UISliders,向您显示最小/最大/当前值。如果它像原始版本一样,则有六个不同的测试图像以及几十个使用 Metal 或 CIKernel(使用 GLSL)的自定义过滤器。一个警告:它只能在 iPad 上运行(您可以使用模拟器,但与任何 CoreImage 项目一样,性能很差)。
  • 感谢@dfd 的链接,我首先尝试将 rgb 向量设置为 (theRedlwant,0,0,0) (0,theBluelwant,0,0) 和 (0,0,theGreenIWant ,0) 但正如你所说的 1 用于获得正常外观的图片。我的目标是丢弃所有颜色信息并使其成为单色。只保留alpha通道。它主要用于为按钮图像或徽标重新着色。
  • 我会在我的 iPad 或模拟器上尝试链接。我也觉得肯定还有别的组合。
  • 好的,我想我明白了。您想逐像素拍摄图像并(至少在您的示例中)更改每个 RGB 通道。例如,假设一个像素的 RGB 值为 (1.0, 0.4, 0.4) - 我不知道颜色是什么! - 你传入一个 (0.5, 0.0, 0.0) 的“col”。您希望输出为 R=1.0*0.5,G=0.4*0.0,B=0.4*0.0,产生 (0.5, 0.0, 0.0)。请记住,UIKit 使用 0-254 而 CoreImage 使用 0-1。这是一个非常简单的CIColorKernel。如果你想要的我是正确的,请告诉我,我会编写 Swift 代码来完成它。

标签: swift image-processing cifilter


【解决方案1】:

我终于按照@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 颜色的图像。

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

【讨论】:

    【解决方案2】:

    很高兴能帮上忙。如果可以的话,一件事。您可以将颜色发送到内核 - 它是 vec4,第四个值是 alpha 通道。请记住,CoreImage 使用 0-1,而不是 0-254。

    这是内核代码:

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

    这和你的差不多。但是现在您需要做的就是创建一个CIColor 而不是图像。如果您已经有一个名为 inputColor 的 UIColor,请执行以下操作:

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

    几个仅供参考。

    • 还有一个 vec3 可以使用,但由于您已经有一个 UIColor `vec4 看起来是最简单的方法。
    • __sample 是正在处理的像素的值,因此它的“基本类型”实际上是 vec4

    【讨论】:

    • 谢谢!确实,制作整个图像有点额外,到目前为止,我只编写了与 C++ 绑定的 OpenGL GLSL 着色器,所以我不确定。我会用一个简单的 vec4 更新我的答案!
    猜你喜欢
    • 2015-10-26
    • 1970-01-01
    • 2017-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多