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
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:
cvCvtColor
(nous voulons seulement changer la teinte).cvThreshold
en spécifiant une certaine tolérance (vous voulez une plage de couleurs, pas une seule couleur).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 .
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.
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.
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 .
Je vais supposer que vous savez comment effectuer ces opérations de base; elles ne seront donc pas incluses dans ma solution:
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:
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!
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.
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
):
CGBitmapContext
name__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
t_pixel
s.Lorsque vous trouvez un pixel qui correspond à ColorToLocate
name__, remplacez les valeurs source par celles de SubstitutionColor
name__.
Créez un nouveau CGImage
à partir de CGBitmapContext
name__.
C'est la partie facile! Tout ce qu'il fait, c'est une CGImage
name__, un remplacement de exact couleurs et un nouveau CGImage
name__.
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.