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

我的问题是,如果我有一个狮子形象,只是我想改变狮子的颜色,而不是背景颜色。 为此,我提到了这个问题,但是却把整个图像的颜色变成了一个整体。 而且图像看起来不怎么样。 我需要像Photoshop一样的颜色变化。 是否可以在coregraphics中做到这一点,或者我必须使用任何其他库。

编辑:我需要颜色变化像iQuikColor应用程序

在这里输入图像说明

请参阅下面的答案。 我不提供完整的解决scheme。


以下是使用OpenCV的可能解决scheme的草图:

  • 使用cvCvtColor将图像从RGB转换为HSV(我们只想改变色调)。
  • cvThreshold隔离一个颜色,指定一定的容差(您需要一个颜色范围,而不是一个平面颜色)。
  • 使用像cvBlobsLib这样的斑点检测库来丢弃低于最小大小的颜色区域。 这将消除场景中类似颜色的点。
  • 使用cvInRangeS遮罩颜色并使用生成的遮罩来应用新的色调。
  • cvMerge用新的色调将新图像与由第一步中保存的饱和度和亮度通道组成的图像组合在一起。

在networking上有几个OpenCV的iOS端口,例如: http : //www.eosgarden.com/en/opensource/opencv-ios/overview/我自己没有尝试过,但似乎是一个很好的研究方向。

这花了相当长的一段时间才发现,主要是因为我想使用核心图像和CIColorCube在Swift中运行。

@米格尔的解释是关于你需要用另一个“色调angular度范围”replace“色调angular度范围”的方法。 你可以阅读他的文章上面有关色调angular度范围的细节。

我做了一个快速的应用程序,replace下面的默认蓝色卡车,无论你在色相滑块上select什么。

在这里输入图像说明

你可以滑动滑块告诉应用什么颜色的色调,你想用蓝色replace。

我将Hue范围硬编码为60度,这通常似乎包含大部分特定的颜色,但是如果需要的话,您可以编辑它。

在这里输入图像说明

在这里输入图像说明

请注意,它不会为轮胎或尾灯着色,因为这超出了卡车默认蓝色色调的60度范围,但它确实适当地处理着色。

首先,您需要将RGB转换为HSV(色调值)的代码:

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

那么你需要将HSV转换为RGB。 当你发现在你想要的色调范围(也就是与默认卡车的蓝色相同的颜色)中的色调来保存你所做的任何调整时,你想要使用它。

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

现在,您只需循环一个完整的RGBA彩色立方体,然后将“默认蓝色”色调范围内的任何颜色与您最近所需的色调进行“调整”。 然后使用核心图像和CIColorCubefilter将调整后的颜色立方体应用于图像。

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

您可以在这里下载示例项目 。 它使用Xcode 7和Swift 2.0。

我将假设你知道如何执行这些基本操作,所以这些不会被包含在我的解决scheme中:

  • 加载图像
  • 获取加载图像的给定像素的RGB值
  • 设置给定像素的RGB值
  • 显示加载的图像,和/或将其保存回磁盘。

首先,让我们考虑如何描述源和目标颜色。 显然你不能指定这些作为确切的RGB值,因为照片将有轻微的颜色变化。 例如,您张贴的卡车图片中的绿色像素并不完全相同。 RGB颜色模型不太擅长expression基本的颜色特性,所以如果将像素转换为HSL,会得到更好的结果。 这里有C函数将RGB转换为HSL并返回。

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

  1. 色调 – 主要感知的颜色 – 即红色,绿色,橙色等
  2. 饱和度 – 颜色是多么“饱满” – 即从全色到无色
  3. 亮度 – 颜色多亮

因此,例如,如果要查找图片中的所有绿色像素,则需要将每个像素从RGB转换为HSL,然后查找与绿色对应的H值,并对“接近绿色”的颜色有一定的容差。 以下是来自维基百科的色调图表:

所以在你的情况下,你会看到像素的色调120度+/-某些数量。 范围越大,select的颜色越多。 如果你的范围太宽,你会看到黄色和青色像素被选中,所以你必须find正确的范围,你甚至可能想要提供你的应用程序控制的用户来select这个范围。

除了通过色调select以外,您可能需要允许“饱和度”和“亮度”的范围,以便您可以对要为彩色化select的像素select更多的限制。

最后,您可能希望为用户提供绘制“套索select”的function,以使图片的特定部分不会被着色。 这就是你如何告诉应用程序,你想要的绿色卡车的身体,但不是绿色的车轮。

一旦你知道你想修改哪个像素是时候改变它们的颜色了。

对像素进行着色最简单的方法就是改变色调,从原始像素中保留饱和度和亮度。 因此,例如,如果您想使绿色像素品红色,则将为所选像素的所有色相值(确保使用模数360math)添加180度。

如果你想变得更复杂,你也可以应用Saturation的变化,这将给你一个更广泛的色调,你可以去。 我认为Lightness最好是独立存在,你可以做一些小的调整,图像看起来还是不错的,但是如果你离开原始图像太远,你可能会开始看到处理像素与背景像素边界的硬边缘。

一旦你有彩色HSL像素,你只要将其转换回RGB,并写回到图像。

我希望这有帮助。 我应该做的最后一点是,代码中的Hue值通常logging在0-255范围内,但许多应用程序将它们显示为范围为0到360度的色轮。 记住这一点!

我可以build议你看看使用OpenCV ? 它是一个开源的image processing库,它也有一个iOS端口。 有很多关于如何使用和设置它的博客文章。

它有一大堆function,将帮助你做好你正在尝试的东西。 你可以只使用CoreGraphics,但最终的结果不会像OpenCV那么好。

它是由麻省理工学院的一些人开发的,所以你可能认为它在边缘检测和对象跟踪等方面做的很好。 我记得读过一篇关于如何使用OpenCV从图片中分离出某种颜色的博客 – 这些例子显示了一个相当不错的结果。 看这里的例子。 从那里我无法想象将实际改变的颜色变成别的东西是一项巨大的工作。

我不知道这个CoreGraphics操作,我没有看到一个合适的CoreImagefilter。 如果这是正确的,那么这是一个正确的方向:

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

  • 首先创build一个新的CGBitmapContext
  • 将源图像绘制到位图上下文中
  • 获取位图像素数据的句柄

了解如何构build缓冲区,以便可以正确填充具有以下forms的像素值的二维数组:

 typedef struct t_pixel { uint8_t r, g, b, a; } t_pixel; 

然后创build颜色来查找:

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

其替代价值:

 const t_pixel SubstitutionColor = { 255,255,255,255 }; // << white, opaque 
  • 遍历位图上下文的像素缓冲区,创buildt_pixel
  • 当find与ColorToLocate匹配的像素时,请使用SubstitutionColor的值replace源值。

  • CGBitmapContext创build一个新的CGBitmapContext

这是容易的部分! 所有这一切都需要一个CGImage ,取代精确的颜色匹配,并产生一个新的CGImage

你想要的更复杂。 对于这个任务,你需要一个好的边缘检测algorithm。

我没有使用这个应用程序你已经链接。 如果仅限于几种颜色,那么它们可能只是交换通道值,与边缘检测配对(请记住,缓冲区也可以用多种颜色模型表示,而不仅仅是RGBA)。

如果(在你链接的应用程序中)用户可以select任意的颜色,值和边界阈值,那么你将不得不使用真正的混合和边缘检测。 如果你需要看看这是如何完成的,你可能需要检查一下像Gimp这样的软件包(它是一个开放的图像编辑器) – 他们有algorithm来检测边缘并通过颜色来select。