¿Cómo cambiar un color en particular en una imagen?

Mi pregunta es si tengo una imagen de León, solo quiero cambiar el color del león solo, no el color de fondo. Para eso he referido esta pregunta SO, pero cambia el color de la imagen completa. Además, la imagen no se ve muy bien. Necesito el cambio de color como photoshop. si es posible hacer esto en coregraphics o tengo que usar cualquier otra biblioteca.

EDITAR: Necesito que el cambio de color sea como la aplicación iQuikColor

enter image description here

Ver las respuestas a continuación en su lugar. El mío no proporciona una solución completa.


Aquí está el boceto de una posible solución usando OpenCV:

  • Convierta la imagen de RGB a HSV usando cvCvtColor (solo queremos cambiar el matiz).
  • Aísle un color con cvThreshold especificando una cierta tolerancia (desea un rango de colores, no un color plano).
  • Deseche las áreas de color por debajo de un tamaño mínimo utilizando una biblioteca de detección de blobs como cvBlobsLib . Esto eliminará los puntos del mismo color en la escena.
  • Enmascare el color con cvInRangeS y use la máscara resultante para aplicar el nuevo matiz.
  • cvMerge la nueva imagen con el nuevo matiz con una imagen compuesta por los canales de saturación y brillo que guardó en el paso uno.

Hay varios puertos OpenCV para iOS en la red, por ejemplo: http://www.eosgarden.com/en/opensource/opencv-ios/overview/ No lo he intentado yo mismo, pero parece una buena dirección de investigación.

Esto tomó bastante tiempo para descifrarlo, principalmente porque quería ponerlo en funcionamiento en Swift usando Core Image y CIColorCube.

La explicación de @Miguel es acertada sobre la forma en que necesita reemplazar un “rango de ángulo de Hue” con otro “rango de ángulo de Hue”. Puede leer su publicación anterior para obtener detalles sobre qué es un rango de Ángulo de Hue.

Hice una aplicación rápida que reemplaza una camioneta azul predeterminada a continuación, con lo que elija en el control deslizante Hue.

enter image description here

Puede deslizar el control deslizante para indicarle a la aplicación el color de tono con el que desea reemplazar el azul.

Estoy codificando el rango Hue para que sea de 60 grados, lo que típicamente parece abarcar la mayoría de un color en particular, pero puede editar eso si lo necesita.

enter image description here

enter image description here

Tenga en cuenta que no colorea las llantas o las luces traseras porque está fuera del rango de 60 grados del tono azul predeterminado del camión, pero sí maneja el sombreado de manera apropiada.

Primero necesitas código para convertir RGB a HSV (valor de tono):

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

Entonces necesitas convertir HSV a RGB. Desea utilizar esto cuando descubra un matiz que en el rango de matiz deseado (es decir, un color que sea del mismo tono azul del camión predeterminado) para ahorrar cualquier ajuste que realice.

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

Ahora simplemente recorre un cubo de color RGBA completo y "ajusta" cualquier color en el rango de tono "azul predeterminado" con los del tono que acaba de desear. Luego use Core Image y el filtro CIColorCube para aplicar su cubo de color ajustado a la imagen.

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

Puede descargar el proyecto de muestra aquí . Está usando Xcode 7 y Swift 2.0.

Voy a hacer la suposición de que sabes cómo realizar estas operaciones básicas, por lo que no se incluirán en mi solución:

  • cargar una imagen
  • obtener el valor RGB de un píxel dado de la imagen cargada
  • establecer el valor RGB de un pixel dado
  • mostrar una imagen cargada, y / o guardarla nuevamente en el disco.

Antes que nada, consideremos cómo puede describir los colores de origen y destino. Es evidente que no puede especificar estos valores exactos RGB, ya que una foto tendrá ligeras variaciones en el color. Por ejemplo, los píxeles verdes en la imagen del camión que publicó no son exactamente del mismo color verde. El modelo de color RGB no es muy bueno para express características de color básicas, por lo que obtendrá resultados mucho mejores si convierte los píxeles a HSL. Aquí están las funciones C para convertir RGB a HSL y viceversa.

El modelo de color HSL describe tres aspectos de un color:

  1. Tono – el color percibido principal – es decir, rojo, verde, naranja, etc.
  2. Saturación: cuán “completo” es el color, es decir, desde el color completo hasta el color nulo
  3. Ligereza: qué tan shiny es el color

Entonces, por ejemplo, si quiere encontrar todos los píxeles verdes en una imagen, convertirá cada píxel de RGB a HSL, luego busque valores H que correspondan a verde, con cierta tolerancia para los colores “casi verdes”. A continuación se muestra un gráfico Hue, de Wikipedia:

Entonces, en su caso, verá píxeles que tienen un Hue de 120 grados +/- alguna cantidad. Cuanto más grande sea el rango, más colores se seleccionarán. Si haces que tu rango sea demasiado amplio, comenzarás a ver píxeles amarillos y cian seleccionados, así que tendrás que encontrar el rango correcto, e incluso puedes ofrecerle al usuario los controles de tu aplicación para seleccionar este rango.

Además de seleccionar por Matiz, es posible que desee permitir rangos de Saturación y Luminosidad, de modo que, opcionalmente, puede poner más límites a los píxeles que desea seleccionar para la coloración.

Finalmente, es posible que desee ofrecerle al usuario la posibilidad de dibujar una “selección de lazo” para que las partes específicas de la imagen queden fuera de la coloración. Así es como puedes decirle a la aplicación que quieres el cuerpo del camión verde, pero no la rueda verde.

Una vez que sepa qué píxeles desea modificar, es hora de modificar su color.

La forma más sencilla de colorear los píxeles es simplemente cambiar el tono, dejando la saturación y la luminosidad del píxel original. Así que, por ejemplo, si desea hacer que los píxeles verdes sean magenta, agregará 180 grados a todos los valores de Tono de los píxeles seleccionados (asegurándose de usar el módulo 360 matemáticos).

Si quieres ser más sofisticado, también puedes aplicar cambios a Saturación y eso te dará una gama más amplia de tonos a los que podrás acceder. Creo que es mejor dejar solo la Luminosidad, es posible que pueda hacer pequeños ajustes y la imagen se verá bien, pero si se aleja demasiado del original, puede comenzar a ver bordes duros donde los píxeles del proceso bordean con píxeles de fondo.

Una vez que tiene el píxel HSL coloreado, simplemente lo convierte de nuevo a RGB y lo vuelve a escribir en la imagen.

Espero que esto ayude. Un último comentario que debo hacer es que los valores de tono en el código generalmente se registran en el rango 0-255, pero muchas aplicaciones los muestran como una rueda de color con un rango de 0 a 360 grados. ¡Mantenlo en mente!

¿Puedo sugerirle que estudie el uso de OpenCV ? Es una biblioteca de manipulación de imágenes de código abierto, y también tiene un puerto iOS. Hay muchas publicaciones en el blog sobre cómo usarlo y configurarlo.

Tiene un montón de funciones que te ayudarán a hacer un buen trabajo de lo que estás intentando. Podrías hacerlo usando CoreGraphics, pero el resultado final no será tan bueno como lo haría OpenCV.

Fue desarrollado por algunas personas en MIT, por lo que es de esperar que haga un trabajo bastante bueno en cosas como la detección de bordes y el seguimiento de objetos. Recuerdo haber leído un blog sobre cómo separar un determinado color de una imagen con OpenCV: los ejemplos arrojaron un resultado bastante bueno. Vea aquí para un ejemplo. A partir de ahí, no puedo imaginarme que sería un trabajo enorme cambiar realmente el color separado por otra cosa.

No sé de una operación CoreGraphics para esto, y no veo un filtro CoreImage adecuado para esto. Si eso es correcto, aquí hay un empujón en la dirección correcta:

Suponiendo que tiene una CGImage (o una uiImage.CGImage ):

  • Comience creando un nuevo CGBitmapContext
  • Dibuja la imagen de origen en el contexto del bitmap
  • Accede a los datos de píxeles del bitmap

Aprenda cómo está estructurado el búfer para que pueda llenar correctamente una matriz 2D de valores de píxeles que tengan la forma:

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

Luego crea el color para ubicar:

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

Y su valor de sustitución:

 const t_pixel SubstitutionColor = { 255,255,255,255 }; // < < white, opaque 
  • Itere el búfer de píxeles del contexto del bitmap, creando t_pixel s.
  • Cuando encuentre un píxel que coincida con ColorToLocate , reemplace los valores de origen con los valores en ColorToLocate SubstitutionColor .

  • Cree una nueva CGImage desde CGBitmapContext .

¡Esa es la parte fácil! Todo lo que hace es tomar una imagen CGImage , reemplazar coincidencias de color exactas , y produce una nueva imagen CGImage .

Lo que quieres es más sofisticado. Para esta tarea, querrás un buen algoritmo de detección de bordes.

No he usado esta aplicación que has vinculado. Si está limitado a unos pocos colores, es posible que simplemente intercambien valores de canal, junto con la detección de bordes (tenga en cuenta que los búferes también pueden estar representados en varios modelos de color, no solo en RGBA).

Si (en la aplicación que vinculó) el usuario puede elegir colores, valores y umbrales de bordes arbitrarios, entonces deberá usar mezclas reales y detección de bordes. Si necesita ver cómo se logra esto, es posible que desee verificar un paquete como Gimp (es un editor de imágenes abierto): tienen los algos para detectar bordes y elegir por color.