web-dev-qa-db-fra.com

Comment changer une couleur particulière dans une image?

Ma question est la suivante: si j’ai une image de lion, je veux seulement changer la couleur du lion et non la couleur de l’arrière-plan. Pour cela, je me suis référé à cette SO question mais cela change la couleur de l’image entière. De plus, l'image n'est pas très belle. J'ai besoin du changement de couleur comme dans Photoshop. s'il est possible de faire cela en coregraphics ou si je dois utiliser une autre bibliothèque.

EDIT: J'ai besoin que le changement de couleur ressemble à iQuikColor app

enter image description here

39
Aravindhan

Voir les réponses ci-dessous à la place. Le mien ne fournit pas une solution complète.


Voici l’esquisse d’une solution possible utilisant OpenCV:

  • Convertissez l'image de RVB en HSV en utilisant cvCvtColor (nous voulons seulement changer la teinte).
  • Isolez une couleur avec cvThreshold en spécifiant une certaine tolérance (vous voulez une plage de couleurs, pas une seule couleur).
  • Éliminez les zones de couleur inférieures à une taille minimale en utilisant une bibliothèque de détection de blob telle que cvBlobsLib . Cela éliminera les points de couleur similaire dans la scène.
  • Masquez la couleur avec cvInRangeS et utilisez le masque obtenu pour appliquer la nouvelle teinte.
  • cvMerge la nouvelle image avec la nouvelle teinte avec une image composée des canaux de saturation et de luminosité enregistrés à la première étape.

Il existe plusieurs ports OpenCV iOS sur le réseau, par exemple: http://www.eosgarden.com/en/opensource/opencv-ios/overview/ Je n'ai pas essayé cela moi-même, mais cela semble être une bonne direction de recherche .

21
Jano

Cela a pris un certain temps à comprendre, principalement parce que je voulais le rendre opérationnel dans Swift avec Core Image et CIColorCube.

L'explication de Miguel est claire sur la manière dont vous devez remplacer une "plage d'angle de teinte" par une autre "plage d'angle de teinte". Vous pouvez lire son article ci-dessus pour plus de détails sur ce qu'est une plage d'angle de teinte.

J'ai créé une application rapide qui remplace un camion bleu par défaut ci-dessous, avec ce que vous choisissez sur le curseur Teinte. 

 enter image description here

Vous pouvez faire glisser le curseur pour indiquer à l'application la couleur avec laquelle vous souhaitez remplacer le bleu. 

Je suis en train de coder en dur la plage de teintes sur 60 degrés, ce qui semble généralement englober la plupart des couleurs, mais vous pouvez le modifier si vous en avez besoin. 

 enter image description here

 enter image description here

Notez que cela ne colore pas les pneus ni les feux arrière, car il se situe en dehors de la plage de 60 degrés de la teinte bleue par défaut du camion, mais il gère l’ombrage de manière appropriée.

D'abord, vous avez besoin d'un code pour convertir RVB en HSV (valeur de teinte):

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

Ensuite, vous devez convertir HSV en RVB. Vous souhaitez utiliser cette option lorsque vous découvrez une teinte qui correspond à la plage de teintes souhaitée (c'est-à-dire, une couleur identique à celle du camion par défaut) pour éviter tout réglage.

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

Maintenant, il vous suffit de parcourir un cube de couleur RGBA complet et "d'ajuster" toutes les couleurs de la plage de teintes "bleu par défaut" avec celles de votre nouvelle teinte souhaitée. Ensuite, utilisez Core Image et le filtre CIColorCube pour appliquer le cube de couleur ajusté à l'image.

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

Vous pouvez télécharger le exemple de projet ici

34
William T.

Je vais supposer que vous savez comment effectuer ces opérations de base; elles ne seront donc pas incluses dans ma solution:

  • charger une image
  • obtenir la valeur RVB d'un pixel donné de l'image chargée
  • définir la valeur RVB d'un pixel donné
  • afficher une image chargée et/ou la sauvegarder sur le disque.

Tout d’abord, examinons comment vous pouvez décrire les couleurs source et cible. Il est clair que vous ne pouvez pas spécifier ces valeurs en tant que valeurs RVB exactes, car une photo aura de légères variations de couleur. Par exemple, les pixels verts de la photo de camion que vous avez publiée ne sont pas tous exactement de la même nuance de vert. Le modèle de couleur RVB ne permet pas d'exprimer les caractéristiques de couleur de base. Vous obtiendrez ainsi de bien meilleurs résultats si vous convertissez les pixels en HSL. Ici sont des fonctions C pour convertir RVB en HSL et inversement.

Le modèle de couleur HSL décrit trois aspects d’une couleur:

  1. Teinte - la couleur principale perçue - à savoir rouge, vert, orange, etc.
  2. Saturation - à quel point la couleur est "pleine" - c’est-à-dire de couleur pleine à aucune couleur du tout
  3. Légèreté - la luminosité de la couleur

Ainsi, par exemple, si vous souhaitez rechercher tous les pixels verts dans une image, vous devez convertir chaque pixel de RVB en HSL, puis rechercher les valeurs H qui correspondent au vert, avec une certaine tolérance pour les couleurs "presque vertes". Ci-dessous, un tableau de Hue, de Wikipedia:

Donc, dans votre cas, vous allez regarder les pixels qui ont une teinte de 120 degrés plus ou moins. Plus la plage est large, plus le nombre de couleurs sélectionnées est élevé. Si vous élargissez votre plage, vous constaterez que les pixels jaunes et cyan sont sélectionnés. Vous devez donc trouver la plage adéquate et vous pouvez même offrir à l'utilisateur de votre application des contrôles pour sélectionner cette plage.

En plus de la sélection par teinte, vous pouvez autoriser des plages pour la saturation et la luminosité, afin de pouvoir éventuellement limiter davantage les pixels que vous souhaitez sélectionner pour la colorisation.

Enfin, vous voudrez peut-être offrir à l'utilisateur la possibilité de dessiner une "sélection au lasso" afin que des parties spécifiques de l'image puissent être omises de la colorisation. Voici comment vous pouvez dire à l'application que vous voulez le corps du camion vert, mais pas la roue verte.

Une fois que vous savez quels pixels vous souhaitez modifier, il est temps de modifier leur couleur.

Le moyen le plus simple de coloriser les pixels est simplement de changer la teinte, en laissant la saturation et la luminosité du pixel d'origine. Ainsi, par exemple, si vous souhaitez créer des pixels verts Magenta, vous ajouterez 180 degrés à toutes les valeurs de teinte des pixels sélectionnés (en vous assurant d'utiliser modulo 360 math).

Si vous souhaitez être plus sophistiqué, vous pouvez également appliquer des modifications à Saturation, ce qui vous donnera une gamme de tons plus large. Je pense qu'il vaut mieux laisser la luminosité seule; vous pourrez peut-être faire de petits ajustements et l'image restera belle, mais si vous vous éloignez trop de l'original, vous commencerez peut-être à voir des contours nets où les pixels du processus sont délimités par des pixels d'arrière-plan.

Une fois que vous avez le pixel HSL colorisé, il vous suffit de le reconvertir en RVB et de l'écrire dans l'image.

J'espère que ça aide. Un dernier commentaire que je devrais faire est que les valeurs de teinte dans le code sont généralement enregistrées dans la plage 0-255, mais de nombreuses applications les affichent comme une roue chromatique dans une plage de 0 à 360 degrés. Garde cela à l'esprit!

13
Miguel

Puis-je vous suggérer d'utiliser OpenCV ? C'est une bibliothèque de manipulation d'images open source, et elle possède également un port iOS. Il existe de nombreux articles de blog sur la façon de l'utiliser et de le configurer.

Il a tout un tas de fonctions qui vous aideront à bien faire ce que vous tentez. Vous pourriez faites-le simplement en utilisant CoreGraphics, mais le résultat final ne sera pas aussi bon que celui d’OpenCV. 

Il a été développé par des gens du MIT, alors comme on peut s'y attendre, il fait un très bon travail dans des domaines tels que la détection Edge et le suivi d'objets. Je me souviens avoir lu sur un blog comment séparer une certaine couleur d'une image avec OpenCV - les exemples montraient un assez bon résultat. Voir ici pour un exemple. À partir de là, je ne peux pas imaginer que ce serait un travail énorme de changer réellement la couleur séparée en quelque chose d'autre.

1
Jordan Smith

Je ne connais pas d'opération CoreGraphics pour cela, et je ne vois pas de filtre CoreImage approprié pour cela. Si cela est correct, alors voici un Push dans la bonne direction:

En supposant que vous avez un CGImage(ou un uiImage.CGImage):

  • Commencez par créer un nouveau CGBitmapContextname__
  • Dessine l'image source dans le contexte bitmap
  • Obtenir une poignée pour les données de pixel du bitmap

Découvrez comment le tampon est structuré pour pouvoir renseigner correctement un tableau 2D de valeurs de pixels ayant la forme suivante:

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

Puis créez la couleur à localiser:

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

Et sa valeur de substitution:

const t_pixel SubstitutionColor = { 255,255,255,255 }; // << white, opaque
  • Parcourez le tampon de pixels du contexte bitmap en créant t_pixels.
  • Lorsque vous trouvez un pixel qui correspond à ColorToLocatename__, remplacez les valeurs source par celles de SubstitutionColorname__.

  • Créez un nouveau CGImageà partir de CGBitmapContextname__.

C'est la partie facile! Tout ce qu'il fait, c'est une CGImagename__, un remplacement de exact couleurs et un nouveau CGImagename__.

Ce que vous voulez est plus sophistiqué. Pour cette tâche, vous aurez besoin d’un bon algorithme de détection d’Edge.

Je n'ai pas utilisé cette application que vous avez liée. Si elle est limitée à quelques couleurs, il peut alors s'agir simplement de valeurs de canal, associées à la détection de contour (gardez à l'esprit que les tampons peuvent également être représentés dans plusieurs modèles de couleur, pas uniquement RGBA).

Si (dans l'application que vous avez liée), l'utilisateur peut choisir des couleurs, des valeurs et des seuils Edge arbitraires, vous devrez utiliser une véritable fusion et une détection Edge. Si vous avez besoin de voir comment cela est accompli, vous pouvez extraire un paquet tel que Gimp (c'est un éditeur d'image ouvert) - ils ont les algos pour détecter les contours et choisir par couleur.

0
justin