web-dev-qa-db-ja.com

画像の特定の色を変更するには?

私の質問は、ライオンの画像がある場合、背景色ではなくライオンの色だけを変更したいということです。そのため私はこれを参照しました SO質問 しかし、それは画像全体の色を変えます。さらに、画像は見栄えが良くありません。フォトショップのような色替えが必要です。コアグラフィックスでこれを行うことが可能かどうか、または他のライブラリを使用する必要があるかどうか。

編集:私は iQuikColor appのような色の変更が必要です

enter image description here

39
Aravindhan

代わりに以下の回答をご覧ください。私は完全な解決策を提供していません。


以下は、OpenCVを使用した可能なソリューションのスケッチです。

  • cvCvtColorを使用して画像をRGBからHSVに変換します(色相のみを変更します)。
  • 特定の許容値を指定してcvThresholdで色を分離します(単一の色ではなく、色の範囲が必要です)。
  • cvBlobsLib のようなblob検出ライブラリを使用して、最小サイズを下回る色の領域を破棄します。これにより、シーン内の類似した色のドットが取り除かれます。
  • cvInRangeSで色をマスクし、結果のマスクを使用して新しい色相を適用します。
  • cvMergeステップ1で保存した彩度チャネルと輝度チャネルで構成された画像を含む新しい色相の新しい画像。

ネットにはいくつかのOpenCV iOSポートがあります。例: http://www.eosgarden.com/en/opensource/opencv-ios/overview/ 私はこれを自分で試したことはありませんが、良い研究方向。

21
Jano

Swift Core ImageとCIColorCubeを使用して実行したかったので、これにはかなり時間がかかりました。

@Miguelの説明は、「色相角度範囲」を別の「色相角度範囲」に置き換える必要がある方法についてのスポットです。色相角度範囲の詳細については、上記の彼の投稿をご覧ください。

以下のデフォルトの青いトラックを、Hueスライダーで選択したものに置き換えるクイックアプリを作成しました。

enter image description here

スライダーをスライドして、青を置き換える色相をアプリに指示できます。

色相の範囲を60度にハードコーディングしています。これは通常、特定の色のほとんどを網羅しているようですが、必要に応じて編集できます。

enter image description here

enter image description here

タイヤやテールライトには色を付けないことに注意してください。これは、トラックのデフォルトの青い色相の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カラーキューブをループして、「デフォルトの青」の色相範囲内のすべての色を、新しく希望する色相の色で「調整」します。次に、Core ImageとCIColorCubeフィルターを使用して、調整したカラーキューブを画像に適用します。

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

サンプルプロジェクトはこちら をダウンロードできます。

37
William T.

これらの基本的な操作を実行する方法を知っていると仮定して、これらが私のソリューションに含まれないようにします。

  • 画像を読み込む
  • ロードされた画像の特定のピクセルのRGB値を取得します
  • 指定されたピクセルのRGB値を設定します
  • ロードされた画像を表示するか、ディスクに保存します。

まず、ソースとデスティネーションの色をどのように説明できるかを考えてみましょう。写真の色はわずかに異なるため、これらを正確なRGB値として指定することはできません。たとえば、投稿したトラックの写真の緑のピクセルは、すべて同じ緑の色合いではありません。 RGBカラーモデルは基本的なカラー特性を表現するのがあまり得意ではないため、ピクセルをHSLに変換すると、はるかに優れた結果が得られます。 ここ は、RGBをHSLに変換して戻すC関数です。

HSLカラーモデルは、色の3つの側面を記述します。

  1. 色相-主に知覚される色-赤、緑、オレンジなど.
  2. 彩度-色がどの程度「フル」であるか(つまり、フルカラーからまったくカラーがないまで)
  3. 明度-色の明るさ

たとえば、画像内のすべての緑のピクセルを検索する場合は、各ピクセルをRGBからHSLに変換し、次に「緑に近い」色にある程度の許容差がある緑に対応するH値を探します。以下はウィキペディアの色相チャートです:

したがって、あなたのケースでは、色相が120度+/-あるピクセルを表示します。範囲が広いほど、より多くの色が選択されます。範囲を広くしすぎると、黄色とシアンのピクセルが選択されるようになるため、適切な範囲を見つける必要があり、アプリコントロールのユーザーにこの範​​囲を選択するように提案することもできます。

色相による選択に加えて、彩度と明度の範囲を許可して、カラー化のために選択するピクセルにオプションでより多くの制限を加えることができます。

最後に、画像の特定の部分をカラー化から除外できるように、「投げ縄選択」を描画する機能をユーザーに提供することができます。これにより、緑色のトラックではなく緑色のトラックのボディが必要であることをアプリに伝えることができます。

変更するピクセルがわかったら、次はその色を変更します。

ピクセルに色を付ける最も簡単な方法は、色相を変更し、彩度と明度を元のピクセルから残しておくことです。たとえば、緑のピクセルをマゼンタにしたい場合は、選択したピクセルのすべての色相値に180度を追加します(モジュロ360の計算を使用していることを確認してください)。

より洗練されたものにしたい場合は、彩度に変更を適用することもできます。これにより、より広い範囲のトーンを使用できるようになります。明度はそのままにしておいた方がいいと思います。少し調整しても画像はきれいに見えますが、元の画像から離れすぎると、プロセスピクセルと背景ピクセルの境界にあるハードエッジが見え始める場合があります。

カラー化されたHSLピクセルを取得したら、それをRGBに変換して、画像に書き戻します。

これがお役に立てば幸いです。最後に、コードのHue値は0〜255の範囲で記録されますが、多くのアプリケーションでは0〜360度の範囲のカラーホイールとして表示されます。心に留めておきます!

13
Miguel

OpenCV の使用を検討することをお勧めできますか?これはオープンソースの画像操作ライブラリであり、iOSポートも備えています。使い方や設定方法については、たくさんのブログ投稿があります。

それはあなたがあなたが試みているものの良い仕事をするのを助けるであろう機能の全山を持っています。あなたはCoreGraphicsを使用してそれを行うことができますが、最終結果はOpenCVほど良く見えません。

これはMITの一部の人々によって開発されたので、ご想像のとおり、エッジ検出やオブジェクト追跡などの点でかなりうまく機能しています。 OpenCVを使用して画像から特定の色を分離する方法についてブログを読んだことを覚えています-例はかなり良い結果を示しました。例については here を参照してください。そこから、分離された色を実際に別の色に変更するのが大変だとは思えません。

1
Jordan Smith

このためのCoreGraphics操作については知りません。また、このための適切なCoreImageフィルターも見当たりません。それが正しい場合は、正しい方向へのプッシュを次に示します。

CGImage(またはuiImage.CGImage)があると仮定します。

  • 新しいCGBitmapContextを作成することから始めます
  • ソース画像をビットマップコンテキストに描画します
  • ビットマップのピクセルデータへのハンドルを取得します。

次の形式のピクセル値の2D配列を適切に入力できるように、バッファがどのように構造化されているかを学びます。

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

次に、検索する色を作成します。

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

そしてその代替値:

const t_pixel SubstitutionColor = { 255,255,255,255 }; // << white, opaque
  • ビットマップコンテキストのピクセルバッファを反復処理し、t_pixelsを作成します。
  • ColorToLocateに一致するピクセルを見つけたら、ソース値をSubstitutionColorの値に置き換えます。

  • CGImageから新しいCGBitmapContextを作成します。

それは簡単な部分です!行うことは、CGImageを取り、exact色を置き換えて、新しいCGImageを生成することだけです。

あなたが欲しいものはより洗練されています。このタスクでは、優れたエッジ検出アルゴリズムが必要になります。

リンクしたこのアプリは使用していません。いくつかの色に制限されている場合、それらはエッジ検出とペアになっているチャネル値を単純に交換している可能性があります(バッファーは、RGBAだけでなく、複数のカラーモデルでも表される場合があることに注意してください)。

(リンクしたアプリで)ユーザーが任意の色、値、エッジしきい値を選択できる場合は、実際のブレンドとエッジ検出を使用する必要があります。これがどのように行われるかを確認する必要がある場合は、 Gimp (これは開いている画像エディターです)などのパッケージをチェックアウトすることをお勧めします。これらには、エッジを検出して色で選択するアルゴがあります。

0
justin