Iphone 如何更改图像中的特定颜色?

Iphone 如何更改图像中的特定颜色?,iphone,objective-c,image-processing,core-graphics,pixel,Iphone,Objective C,Image Processing,Core Graphics,Pixel,我的问题是,如果我有一个狮子图像,我只想改变狮子的颜色,而不是背景色。为此,我引用了这个,但它改变了整个图像的颜色。而且形象也不太好。我需要像photoshop那样改变颜色。是否可以在coregraphics中实现这一点,或者我必须使用任何其他库 编辑:我需要像应用程序一样更改颜色 我不知道是否有适用于此的CoreGraphics操作,我也没有看到适用于此的CoreImage过滤器。如果这是正确的,那么这里有一个正确方向的推力: 假设您有一个CGImage(或uiImage.CGImage):

我的问题是,如果我有一个狮子图像,我只想改变狮子的颜色,而不是背景色。为此,我引用了这个,但它改变了整个图像的颜色。而且形象也不太好。我需要像photoshop那样改变颜色。是否可以在coregraphics中实现这一点,或者我必须使用任何其他库

编辑:我需要像应用程序一样更改颜色


我不知道是否有适用于此的CoreGraphics操作,我也没有看到适用于此的CoreImage过滤器。如果这是正确的,那么这里有一个正确方向的推力:

假设您有一个
CGImage
(或
uiImage.CGImage
):

  • 首先创建一个新的
    CGBitmapContext
  • 将源图像绘制到位图上下文
  • 获取位图像素数据的句柄
了解缓冲区的结构,以便正确填充具有以下形式的二维像素值数组:

typedef struct t_pixel {
  uint8_t r, g, b, a;
} t_pixel;
然后创建要定位的颜色:

const t_pixel ColorToLocate = { 0,0,0,255 }; // << black, opaque

const t_pixel ColorToLocate={0,0,0255};// 我可以建议你考虑使用吗?这是一个开源的图像处理库,它还有一个iOS端口。有很多关于如何使用和设置它的博客文章

它有一整堆函数,可以帮助您很好地完成所尝试的工作。您可以使用CoreGraphics来实现这一点,但最终结果看起来并不像OpenCV那样好


它是由麻省理工学院的一些人开发的,所以正如你所料,它在边缘检测和目标跟踪方面做得相当好。我记得读过一篇关于如何使用OpenCV从图片中分离出某种颜色的博客——示例显示了一个非常好的结果。有关示例,请参见。从那以后,我无法想象将分离的颜色改成其他颜色将是一项艰巨的工作。

请参见下面的答案。我的解决方案不完整。


以下是使用OpenCV的可能解决方案的示意图:

  • 使用
    cvtcolor
    将图像从RGB转换为HSV(我们只想更改色调)
  • 使用指定特定公差的
    cvThreshold
    隔离颜色(您需要的是一系列颜色,而不是一种单色)
  • 使用blob检测库丢弃低于最小大小的颜色区域,如。这将去除场景中颜色相似的点
  • 使用
    cvInRangeS
    遮罩颜色,并使用生成的遮罩应用新色调
  • cvMerge
    具有新色调的新图像,该图像由步骤1中保存的饱和度和亮度通道组成

网络中有几个OpenCV iOS端口,例如:我自己没有尝试过,但这似乎是一个很好的研究方向。

我假设您知道如何执行这些基本操作,因此我的解决方案中不会包括这些:

  • 加载图像
  • 获取加载图像的给定像素的RGB值
  • 设置给定像素的RGB值
  • 显示加载的图像,和/或将其保存回磁盘
首先,让我们考虑如何描述源和目标颜色。很明显,您不能将它们指定为精确的RGB值,因为照片的颜色会略有变化。例如,您发布的卡车图片中的绿色像素并非都是完全相同的绿色。RGB颜色模型不太擅长表达基本的颜色特征,因此如果将像素转换为HSL,将获得更好的结果。是将RGB转换为HSL并返回的C函数

HSL颜色模型描述颜色的三个方面:

  • 色调-主要感知颜色-即红色、绿色、橙色等
  • 饱和度-颜色的“完整”程度-即从完整颜色到完全没有颜色
  • 亮度-颜色有多亮
  • 例如,如果你想找到一张图片中的所有绿色像素,你将把每个像素从RGB转换成HSL,然后寻找对应于绿色的H值,对“近绿色”颜色有一定的容差。以下是来自维基百科的色调图表:

    因此,在您的例子中,您将看到色调为120度+/-一定量的像素。范围越大,选择的颜色就越多。如果你的范围太宽,你会看到黄色和青色像素被选中,因此你必须找到正确的范围,你甚至可能想让你的应用程序控件的用户选择这个范围

    除了按色调选择外,您可能还希望允许饱和度和亮度的范围,以便可以选择对要选择着色的像素施加更多限制

    最后,您可能希望为用户提供绘制“套索选择”的能力,以便在着色时可以忽略图片的特定部分。这就是你如何告诉应用程序你想要的是绿色卡车的车身,而不是绿色车轮

    一旦知道要修改哪些像素,就可以改变它们的颜色了

    给像素着色最简单的方法就是改变色调,保留原始像素的饱和度和亮度。因此,例如,如果要使绿色像素为洋红色,则将向选定像素的所有色调值添加180度(确保使用模360数学)

    如果你想变得更复杂,你也可以改变饱和度,这将给你一个更广泛的音调范围,你可以去。我认为亮度更好,你可以做一些小的调整,图像看起来仍然很好,但是如果你离原始图像太远,你可能会看到处理像素与背景像素交界处的硬边

    一旦你有了彩色的HSL像素,你只需将其转换回RGB并将其写回图像

    func render() {
        let centerHueAngle: Float = 214.0/360.0 //default color of truck body blue
        let destCenterHueAngle: Float = slider.value
        let minHueAngle: Float = (214.0 - 60.0/2.0) / 360 //60 degree range = +30 -30
        let maxHueAngle: Float = (214.0 + 60.0/2.0) / 360
        var hueAdjustment = centerHueAngle - destCenterHueAngle
        let size = 64
        var cubeData = [Float](count: size * size * size * 4, repeatedValue: 0)
        var rgb: [Float] = [0, 0, 0]
        var hsv: (h : Float, s : Float, v : Float)
        var newRGB: (r : Float, g : Float, b : Float)
        var offset = 0
        for var z = 0; z < size; z++ {
            rgb[2] = Float(z) / Float(size) // blue value
            for var y = 0; y < size; y++ {
                rgb[1] = Float(y) / Float(size) // green value
                for var x = 0; x < size; x++ {
                    rgb[0] = Float(x) / Float(size) // red value
                    hsv = RGBtoHSV(rgb[0], g: rgb[1], b: rgb[2])
                    if hsv.h < minHueAngle || hsv.h > maxHueAngle {
                        newRGB.r = rgb[0]
                        newRGB.g = rgb[1]
                        newRGB.b = rgb[2]
                    } else {
                        hsv.h = destCenterHueAngle == 1 ? 0 : hsv.h - hueAdjustment //force red if slider angle is 360
                        newRGB = HSVtoRGB(hsv.h, s:hsv.s, v:hsv.v)
                    }
                    cubeData[offset]   = newRGB.r
                    cubeData[offset+1] = newRGB.g
                    cubeData[offset+2] = newRGB.b
                    cubeData[offset+3] = 1.0
                    offset += 4
                }
            }
        }
        let data = NSData(bytes: cubeData, length: cubeData.count * sizeof(Float))
        let colorCube = CIFilter(name: "CIColorCube")!
        colorCube.setValue(size, forKey: "inputCubeDimension")
        colorCube.setValue(data, forKey: "inputCubeData")
        colorCube.setValue(ciImage, forKey: kCIInputImageKey)
        if let outImage = colorCube.outputImage {
            let context = CIContext(options: nil)
            let outputImageRef = context.createCGImage(outImage, fromRect: outImage.extent)
            imageView.image = UIImage(CGImage: outputImageRef)
        }
    }
    
    我希望如此
    func RGBtoHSV(r : Float, g : Float, b : Float) -> (h : Float, s : Float, v : Float) {
        var h : CGFloat = 0
        var s : CGFloat = 0
        var v : CGFloat = 0
        let col = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0)
        col.getHue(&h, saturation: &s, brightness: &v, alpha: nil)
        return (Float(h), Float(s), Float(v))
    }
    
    func HSVtoRGB(h : Float, s : Float, v : Float) -> (r : Float, g : Float, b : Float) {
        var r : Float = 0
        var g : Float = 0
        var b : Float = 0
        let C = s * v
        let HS = h * 6.0
        let X = C * (1.0 - fabsf(fmodf(HS, 2.0) - 1.0))
        if (HS >= 0 && HS < 1) {
            r = C
            g = X
            b = 0
        } else if (HS >= 1 && HS < 2) {
            r = X
            g = C
            b = 0
        } else if (HS >= 2 && HS < 3) {
            r = 0
            g = C
            b = X
        } else if (HS >= 3 && HS < 4) {
            r = 0
            g = X
            b = C
        } else if (HS >= 4 && HS < 5) {
            r = X
            g = 0
            b = C
        } else if (HS >= 5 && HS < 6) {
            r = C
            g = 0
            b = X
        }
        let m = v - C
        r += m
        g += m
        b += m
        return (r, g, b)
    }
    
    func render() {
        let centerHueAngle: Float = 214.0/360.0 //default color of truck body blue
        let destCenterHueAngle: Float = slider.value
        let minHueAngle: Float = (214.0 - 60.0/2.0) / 360 //60 degree range = +30 -30
        let maxHueAngle: Float = (214.0 + 60.0/2.0) / 360
        var hueAdjustment = centerHueAngle - destCenterHueAngle
        let size = 64
        var cubeData = [Float](count: size * size * size * 4, repeatedValue: 0)
        var rgb: [Float] = [0, 0, 0]
        var hsv: (h : Float, s : Float, v : Float)
        var newRGB: (r : Float, g : Float, b : Float)
        var offset = 0
        for var z = 0; z < size; z++ {
            rgb[2] = Float(z) / Float(size) // blue value
            for var y = 0; y < size; y++ {
                rgb[1] = Float(y) / Float(size) // green value
                for var x = 0; x < size; x++ {
                    rgb[0] = Float(x) / Float(size) // red value
                    hsv = RGBtoHSV(rgb[0], g: rgb[1], b: rgb[2])
                    if hsv.h < minHueAngle || hsv.h > maxHueAngle {
                        newRGB.r = rgb[0]
                        newRGB.g = rgb[1]
                        newRGB.b = rgb[2]
                    } else {
                        hsv.h = destCenterHueAngle == 1 ? 0 : hsv.h - hueAdjustment //force red if slider angle is 360
                        newRGB = HSVtoRGB(hsv.h, s:hsv.s, v:hsv.v)
                    }
                    cubeData[offset]   = newRGB.r
                    cubeData[offset+1] = newRGB.g
                    cubeData[offset+2] = newRGB.b
                    cubeData[offset+3] = 1.0
                    offset += 4
                }
            }
        }
        let data = NSData(bytes: cubeData, length: cubeData.count * sizeof(Float))
        let colorCube = CIFilter(name: "CIColorCube")!
        colorCube.setValue(size, forKey: "inputCubeDimension")
        colorCube.setValue(data, forKey: "inputCubeData")
        colorCube.setValue(ciImage, forKey: kCIInputImageKey)
        if let outImage = colorCube.outputImage {
            let context = CIContext(options: nil)
            let outputImageRef = context.createCGImage(outImage, fromRect: outImage.extent)
            imageView.image = UIImage(CGImage: outputImageRef)
        }
    }