wake-up-neo.com

Wie ändere ich eine bestimmte Farbe in einem Bild?

Meine Frage ist, wenn ich ein Löwebild habe, möchte ich nur die Farbe des Löwen ändern, nicht die Hintergrundfarbe. Dafür habe ich diese SO Frage angesprochen aber es ändert die Farbe des gesamten Bildes. Außerdem sieht das Bild nicht gut aus. Ich brauche den Farbwechsel wie Photoshop. ob das in coregraphics möglich ist oder ich irgendeine andere bibliothek benutzen muss.

EDIT: Ich brauche den Farbwechsel, um wie iQuikColor App zu sein

enter image description here

39
Aravindhan

Siehe stattdessen die Antworten. Meine bietet keine vollständige Lösung.


Hier ist die Skizze einer möglichen Lösung mit OpenCV:

  • Konvertieren Sie das Bild von RGB in HSV mit cvCvtColor (wir möchten nur den Farbton ändern).
  • Isolieren Sie eine Farbe, indem Sie mit cvThreshold eine bestimmte Toleranz angeben (Sie möchten einen Farbbereich und nicht eine flache Farbe).
  • Verwerfen Sie Farbbereiche unterhalb einer Mindestgröße mit einer Blob-Erkennungsbibliothek wie cvBlobsLib . Dadurch werden Punkte mit ähnlicher Farbe in der Szene entfernt.
  • Maskieren Sie die Farbe mit cvInRangeS und verwenden Sie die resultierende Maske, um den neuen Farbton anzuwenden.
  • cvMerge das neue Bild mit dem neuen Farbton mit einem Bild, das aus den Sättigungs- und Helligkeitskanälen besteht, die Sie in Schritt 1 gespeichert haben.

Im Netz gibt es mehrere OpenCV-iOS-Ports, z. B .: http://www.eosgarden.com/de/opensource/opencv-ios/overview/ Ich habe das selbst nicht probiert, scheint aber eine gute Forschungsrichtung zu sein .

21
Jano

Das hat eine Weile gedauert, vor allem, weil ich es mit Core Image und CIColorCube in Swift zum Laufen bringen wollte.

Die Erklärung von Miguel ist genau auf die Art und Weise, wie Sie einen "Farbtonwinkelbereich" durch einen anderen "Farbtonwinkelbereich" ersetzen müssen. Sie können seinen Beitrag oben lesen, um zu erfahren, was ein Farbtonwinkelbereich ist.

Ich habe eine schnelle App erstellt, die einen blauen Standard-Lkw unten ersetzt. 

 enter image description here

Sie können den Schieberegler verschieben, um der App mitzuteilen, mit welcher Farbe der Farbton den Blauen ersetzen soll. 

Ich kann den Farbtonbereich mit 60 Grad hart codieren, was normalerweise den Großteil einer bestimmten Farbe zu umfassen scheint, aber Sie können das bearbeiten, wenn Sie dies benötigen. 

 enter image description here

 enter image description here

Beachten Sie, dass die Reifen oder Rückleuchten nicht gefärbt werden, da dies außerhalb des 60-Grad-Bereichs des Standard-Blautons des LKWs liegt, die Schattierung jedoch ordnungsgemäß erfolgt.

Zuerst benötigen Sie Code, um RGB in HSV (Farbtonwert) zu konvertieren:

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

Dann müssen Sie HSV in RGB konvertieren. Sie möchten dies verwenden, wenn Sie einen Farbton entdecken, der sich in Ihrem gewünschten Farbtonbereich befindet (dh, eine Farbe, die dem blauen Farbton des Standard-Trucks entspricht), um alle vorgenommenen Einstellungen zu speichern.

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

Jetzt durchlaufen Sie einfach einen vollständigen RGBA-Farbwürfel und "justieren" alle Farben im "Standard-Blau" -Farbtonbereich mit denen des neu gewünschten Farbtons. Verwenden Sie dann Core Image und den CIColorCube-Filter, um den angepassten Farbwürfel auf das Bild anzuwenden.

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

Das Beispielprojekt können Sie hier herunterladen

34
William T.

Ich gehe davon aus, dass Sie wissen, wie diese grundlegenden Operationen ausgeführt werden, sodass diese nicht in meine Lösung aufgenommen werden:

  • ein Bild laden
  • ruft den RGB-Wert eines bestimmten Pixels des geladenen Bildes ab
  • legen Sie den RGB-Wert eines bestimmten Pixels fest
  • ein geladenes Bild anzeigen und/oder auf der Festplatte speichern.

Lassen Sie uns zunächst überlegen, wie Sie die Quell- und Zielfarben beschreiben können. Natürlich können Sie diese nicht als exakte RGB-Werte angeben, da ein Foto geringfügige Farbabweichungen aufweisen kann. Beispielsweise haben die grünen Pixel in dem von Ihnen geposteten LKW-Bild nicht alle exakt den gleichen Grünton. Das RGB-Farbmodell kann grundlegende Farbmerkmale nicht sehr gut ausdrücken. Sie erhalten also bessere Ergebnisse, wenn Sie die Pixel in HSL konvertieren. Hier sind C-Funktionen zum Konvertieren von RGB in HSL und zurück.

Das HSL-Farbmodell beschreibt drei Aspekte einer Farbe:

  1. Farbton - die hauptsächlich wahrgenommene Farbe - d. H. Rot, Grün, Orange usw.
  2. Sättigung - wie "voll" die Farbe ist - d. H. Von voller Farbe zu keiner Farbe
  3. Helligkeit - wie hell die Farbe ist

Wenn Sie beispielsweise alle grünen Pixel in einem Bild suchen möchten, konvertieren Sie jedes Pixel von RGB in HSL. Suchen Sie dann nach H-Werten, die Grün entsprechen, mit einer gewissen Toleranz für Farben, die nahe an Grün liegen. Unten ist ein Farbton-Diagramm von Wikipedia:

In Ihrem Fall werden Sie also Pixel mit einem Farbton von 120 Grad +/- betrachten. Je größer der Bereich, desto mehr Farben werden ausgewählt. Wenn Sie Ihren Bereich zu groß machen, werden gelbe und cyanfarbene Pixel ausgewählt, so dass Sie den richtigen Bereich finden müssen. Möglicherweise möchten Sie auch dem Benutzer Ihrer App-Steuerelemente diesen Bereich auswählen.

Zusätzlich zur Auswahl nach Farbton möchten Sie möglicherweise Bereiche für Sättigung und Helligkeit zulassen, sodass Sie optional den Pixeln mehr Grenzen setzen können, die Sie für die Einfärbung auswählen möchten.

Schließlich möchten Sie möglicherweise dem Benutzer die Möglichkeit bieten, eine "Lasso-Auswahl" zu zeichnen, so dass bestimmte Teile des Bildes von der Einfärbung ausgeschlossen werden können. So können Sie der App sagen, dass Sie die Karosserie des grünen Trucks haben möchten, nicht jedoch das grüne Rad.

Sobald Sie wissen, welche Pixel Sie ändern möchten, müssen Sie deren Farbe ändern.

Am einfachsten können Sie die Pixel einfärben, indem Sie einfach den Farbton ändern und die Sättigung und Helligkeit vom ursprünglichen Pixel lassen. Wenn Sie beispielsweise grüne Pixel zu Magenta machen möchten, fügen Sie alle Farbtonwerte der ausgewählten Pixel um 180 Grad hinzu (stellen Sie sicher, dass Sie die Modulo 360-Mathematik verwenden).

Wenn Sie anspruchsvoller werden möchten, können Sie auch Änderungen an der Sättigung vornehmen. Dadurch erhalten Sie eine größere Auswahl an Tönen, zu denen Sie gehen können. Ich denke, die Helligkeit ist besser alleine gelassen, Sie können möglicherweise kleine Anpassungen vornehmen und das Bild wird immer noch gut aussehen. Wenn Sie sich jedoch zu weit vom Original entfernen, werden möglicherweise harte Kanten sichtbar, an denen die Prozesspixel an die Hintergrundpixel angrenzen.

Sobald Sie das eingefärbte HSL-Pixel haben, konvertieren Sie es einfach wieder in RGB und schreiben es zurück in das Bild.

Ich hoffe das hilft. Abschließend möchte ich sagen, dass Farbtonwerte im Code normalerweise im Bereich von 0-255 aufgezeichnet werden, aber in vielen Anwendungen werden sie als Farbrad mit einem Bereich von 0 bis 360 Grad angezeigt. Merk dir das!

13
Miguel

Kann ich Ihnen vorschlagen, sich mit OpenCV zu beschäftigen? Es ist eine Open Source-Bibliothek zur Bildbearbeitung und verfügt auch über einen iOS-Port. Es gibt viele Blogbeiträge, wie man sie benutzt und einrichtet.

Es hat eine ganze Reihe von Funktionen, die Ihnen dabei helfen werden, das zu tun, was Sie versuchen. Sie könnten tun dies einfach mit CoreGraphics, aber das Endergebnis wird nicht annähernd so gut aussehen wie OpenCV. 

Es wurde von einigen Leuten am MIT entwickelt, so dass Sie, wie Sie vielleicht erwarten, ziemlich gute Arbeit bei Sachen wie der Kantenerkennung und der Objektverfolgung leistet. Ich erinnere mich, wie ich in einem Blog gelesen habe, wie man mit OpenCV eine bestimmte Farbe von einem Bild trennt - die Beispiele zeigten ein ziemlich gutes Ergebnis. Siehe hier für ein Beispiel. Von dort aus kann ich mir nicht vorstellen, dass es eine gewaltige Aufgabe sein würde, die getrennte Farbe in etwas anderes zu ändern.

1
Jordan Smith

Ich kenne hierfür keine CoreGraphics-Operation, und ich sehe keinen geeigneten CoreImage-Filter dafür. Wenn das richtig ist, dann hier ein Push in die richtige Richtung:

Angenommen, Sie haben ein CGImage (oder einen uiImage.CGImage):

  • Beginnen Sie mit dem Erstellen einer neuen CGBitmapContext.
  • Zeichnen Sie das Quellbild in den Bitmap-Kontext
  • Holen Sie sich ein Handle für die Pixeldaten der Bitmap

Erfahren Sie, wie der Puffer strukturiert ist, damit Sie ein 2D-Array von Pixelwerten mit der folgenden Form richtig füllen können:

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

Dann erstellen Sie die Farbe, die Sie suchen müssen:

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

Und seinen Substitutionswert:

const t_pixel SubstitutionColor = { 255,255,255,255 }; // << white, opaque
  • Durchlaufen Sie den Pixelpuffer des Bitmap-Kontexts und erstellen Sie t_pixels.
  • Wenn Sie ein Pixel finden, das mit ColorToLocate übereinstimmt, ersetzen Sie die Quellwerte durch die Werte in SubstitutionColor.

  • Erstellen Sie eine neue CGImage aus der CGBitmapContext.

Das ist der leichte Teil! Alles was Sie tun müssen, ist CGImage, ersetzt exact color und erzeugt ein neues CGImage.

Was Sie wollen, ist anspruchsvoller. Für diese Aufgabe benötigen Sie einen guten Kantenerkennungsalgorithmus.

Ich habe diese von Ihnen verknüpfte App nicht verwendet. Wenn es auf wenige Farben beschränkt ist, können sie einfach Kanalwerte austauschen und mit der Kantenerkennung kombinieren (beachten Sie, dass Puffer auch in mehreren Farbmodellen dargestellt werden können - nicht nur in RGBA).

Wenn (in der von Ihnen verknüpften App) der Benutzer beliebige Farben, Werte und Kantenschwellenwerte auswählen kann, müssen Sie echtes Mischen und Kantenerkennung verwenden. Wenn Sie wissen möchten, wie dies ausgeführt wird, möchten Sie vielleicht ein Paket wie Gimp (es ist ein geöffneter Bildeditor) auschecken. Sie haben die Möglichkeit, Kanten zu erkennen und nach Farbe zu wählen.

0
justin