色を変更して背景よりも似ていないようにするアルゴリズム


23

単色で塗りつぶされた背景で、互いに跳ね返る20個のボールのデモを作成しています。各ボールの色はrandint(0, 255)、R、G、Bの各タプルコンポーネントに対してランダムに選択されます。

問題は、一部のボールが背景に非常に似た色になり、見づらくなることです。選択した色が類似している場合(特定のしきい値内)に別の色を回転させるか、背景色から遠ざけることで、それを回避したいと思います。

しきい値として使用する類似性インデックスを計算する方法は?または、色を変換して、たとえば青みを弱める方法はありますか?

明白な免責事項:私は色理論について何も知らないので、上記の概念が存在するかどうかも知りません!

私はPythonでコーディングしていますが、概念と戦略を探しているので、擬似コードは問題ありません(理論のみ)。

編集:

いくつかのポイントを明確にするには:

  • 各ボールには、独自のランダムな色があります。私はそれらを可能な限りランダムに保ち、色空間を可能な限り探索したい(RGBかHSVかにかかわらず、色pygameを定義するために両方の形式を受け入れます)

  • それぞれの色が背景に「近すぎる」ことを避けたいだけです。問題は色相だけではありません:濃い青の背景に明るい青のボール(または「白っぽい」青に「フルブルー」)で完全に問題ありません

  • 今のところ、ボールの色が他のボールと似ている(または同一である)かどうかは気にしません。それを避けることはボーナスですが、それは小さな問題です。

  • 背景色は、一定の単一のRGB色です。今のところ、私は "basic" blueを使用(0, 0, 255)していますが、それで満足していません。したがって、背景は任意の色(任意の色相、明度、彩度など)であると考えてください。

EDIT2:

TL、DRバージョン: Xランダムカラーを作成する方法を与えるだけで、任意の(ただし、単一で一定の)色の背景に対して「過度にフェードイン」しないようにする



1
なぜ各ボールの周りにアウトラインを配置しないのですか?
ashes999 14

1
次に、背景色とボール色の両方からアウトラインの色を区別するのに問題があるかもしれません;)
Kromsterは、サポートMonica 14

1
@AlexM .:「任意の色を避けてランダムな色を生成する」簡単な解決策がないとは信じがたい
MestreLionは

1
@MestreLionは、アウトラインに黒を使用し、最小RGB値が128の色を生成します。それは簡単です。
ashes999

回答:


33

色が(3次元)色空間を構成するという事実を使用して、この色空間での距離を計算できます。次に、この色空間でメトリックを定義して、2つの色の間の距離を見つける必要があります。

たとえば、2点x =(x1、x2、x3)とy =(y1、y2、y3)の間のユークリッド空間の距離は、d(x、y)= sqrt((y1-x1)*(y1- x1)+(y2-x2)*(y2-x2)+(y3-x3)*(y3-x3))。

これで、ボールの色と背景色の間の距離を計算でき、小さすぎる場合は、新しいランダムな色を作成して再度テストします。

RGBやHSVでさえ、このタスクに適した色空間ではないことがわかります。色違いのWikipediaの記事採用L Bの色空間といくつかの可能な指標を提供します。

また、トピックに関するstackoverflowに関する議論があります。


それは非常に素晴らしいアプローチです、私はそれをすることができました:)ありがとう!また、適切な「最小距離」のしきい値を見つける方法について少し詳しく説明してください。RGBが適切でない場合、L a b に変換する方法は?私は正確な知覚指標を求めて努力しているわけではないので、「十分に良い」デフォルトで十分です。
メストレリオン14

1
この答えが大好きで、よく考えて、必要な方程式と参考文献を提供しながら、タスクを実行する方法の良い説明です。良い最初の答え。
トム 'ブルー'ピドック14

1
人間の知覚の「十分な」近似を探している場合は、RGBから直接変換されたYUV色空間を調べてください。
trm 14

5
少し時間を節約し、平方根をとらないでください(高価)。正方形と比較する最小色距離値を作成します。
クリスカドモア14

1
@MestreLion L a b *色空間は、知覚的に均一になるように考案されました。つまり、2つの色の間の知覚距離は、色空間の実際の距離と同じです。RGBカラーはデバイスに依存するため、LabとRGBの間で直接変換することはできません。ただし、特定の絶対RGBカラースペースを使用する場合は、変換できます。この記事:en.wikipedia.org/wiki/SRGB_color_spaceおよびこの記事:en.wikipedia.org/wiki/Lab_color_space
jwir3 14

8

国連のWebアクセシビリティ標準ページ(http://www.un.org/webaccessibility/1_visual/13_colourcontrast.shtml)は、適切なことを保証するための標準を示しています、一部のユーザーが色覚異常であることを念頭に置いて、Webサイトでテキストコントラスト。一部のユーザーも同様に色覚異常の可能性があるため、これは特に重要です(同じ輝度の場合、緑の背景から赤のボールを見分けるのは難しいでしょう)。

国連のページは、背景のテキストのコントラスト比が4.5:1でなければならないことを示す(http://juicystudio.com/services/luminositycontrastratio.php#specify)のJuicy Studios Luminosity Color Contrast Ratio Analyzerを指していますあなたのケースに似ていると考えています:

  • 大規模なテキスト:大規模なテキストと大規模なテキストの画像のコントラスト比は少なくとも3:1です

では、コントラスト比とは何ですか?ハイパーリンクをさらにたどって(これは楽しいです!)コントラスト比を次のように定義するW3ページに移動します

Contrast Ratio = (L1 + 0.05)/(L2 + 0.05)

ここで、L1とL2は色の相対的な輝度です。L1はより明るい色、L2はより明るい色です。繰り返しますが、以下を指定する同じW3ページへのリンクをたどります。

L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:

if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4
if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4
if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4
and RsRGB, GsRGB, and BsRGB are defined as:

RsRGB = R8bit/255
GsRGB = G8bit/255
BsRGB = B8bit/255

:上記のキャレット記号^は、べき乗(x²)を表します

コントラストアルゴリズムのためのかなり良いフレームワークがあります(色覚異常に優しい!)

  • 各色のR、G、B値を決定する
  • 各色の輝度を決定する
  • 最も明るい色と最も明るい色のコントラスト比が3未満の場合、再ロール!

2
^はべき乗であることに注意してください。クレイジーなCのようなユーザーにとっては、xorを意味します(1分間、浮動小数点値に対してビット演算を実行しようとしていると本当に思っていました)。
ファラプ14

それは素晴らしいアプローチです、私はそれが好きです!:)
メストレリオン14

心配しない^で、このコンテキストでビット単位のXORについて考えることはありません。。(または任意の実際召し上がれCのphreaksで、^非常に少数の言語が実際にそれを使用していますが、それはだpythonで...(擬似コードで累乗のためのデファクトsimbolです**
MestreLion

1
タイプミスを修正するだけです。さらに、ある人が^に問題を抱えている場合、将来的には他の人にもチャンスがあります。具体的であっても害はありません。
ジョン14

1
@John心配いりません。混乱の原因はわかりません(つまり、最初にどの言語が^を使用したか)。私はVBでの経験のために^がべき乗に使用されることを知っています(それを累乗として使用する他の言語は知りませんが、おそらくいくつかあります)。ただ、ヘッドアップかのように、それはだキャレット記号ではなく、ニンジンシンボル。誰かが野菜(特に平方根が関係するもの)を使ったプログラミングについてひどい冗談を言う前に、それを変更したいかもしれません。
ファラプ14

2

ランダムを生成してRGB値を使用する 0-255各コンポーネントの値をすると、背景に似た色が作成されるだけでなく、ボールの色が非常に似たものになる可能性があります。

おそらく、ランダムではないアプローチを選択し、代わりに異なることが保証されている色を作成するでしょう。色相範囲の各約32度ごとに色を作成し、明度または彩度を変更できます(RGBの代わりにHSVカラーモデルを使用します)。

このようなもの:

numColors = 20;
stepSize = 360 / (numColors / 2 + 1); // hue step size
for(i = 0; i < numColors; i++){
    color = HSV(i / 2 * stepSize, 0.5 + (i % 2) * 0.5, 1.0) 
}

うまく分散された20色を作成します。これは、整数演算を使用していることを前提としているためi = 0i = 1同じ色相値を作成します。

もちろん、これはあまりうまくスケーリングしません..作成する色の値が100ある場合、色相の間隔が十分でない可能性があり、再び同様の色になります。その場合、あなたも変えることができますV(輝度)値をます。

背景色がどのようなものかはわかりませんが、HSVを使用すると色の比較も簡単になります(3つのコンポーネントを比較するだけで、HUEが円形(0 == 360)であることを忘れないでください)。

更新:

私はあなたの質問には本当に答えなかったが、別のアプローチを提供したので、任意の背景と完全にランダムな色のボールのアルゴリズムは次のようになります(これはブルートフォースアプローチですが、ユースケースではうまく機能します)。

// background color, currently equals RGB(0, 0, 255)
bg = HSV(240, 1.0, 1.0)

// number of colors to create is equal to the amount of balls
numColors = balls.length

// set threshold to compare colors. you can tweak this to your liking
threshold = 0.15

for(i = 0; i < numColors; i++){
    do {
        // assuming rnd returns a random float 0-1 inclusive
        color = HSV(rnd() * 360, rnd(), rnd())
        // calculate delta values, normalize hue to 0-1
        dH = (180 - abs(abs(bg.h - color.h) - 180)) / 360
        dS = bg.s - color.s
        dV = bg.v - color.v
        // "distance" between bg and color
        difference = sqrt(dH * dH + dS * dS + dV * dV)
    } while(difference < threshold)

    ball[i].color = color
}

あなたのアルゴリズムについて、背景色をどのように考慮していますか?
メストレリオン14

現在、背景色は考慮されていません...生成された色を比較することで微調整でき(最後の段落で説明)、異なる色を作成できますV(現在のアルゴリズムでは変更されないため)。
bummzack 14

代替アルゴリズムと@MestreLion追加アップデート
bummzack

素晴らしい、それは素晴らしい戦略のようです。ブルートフォースは問題ありません。色は初期化時に一度だけ生成され、ゲームループ内では生成されません。HSVでの単純なユークリッド距離で、Reneのアプローチと同じアプローチを使用しているようです。HSVに適したしきい値の参照はありますか?H、S、およびVは、RGBのように距離が同じですか?
メストレリオン14

@MestreLionはい、私の2番目のアルゴリズムは、ルネが答えで説明したものとほぼ同じです。悲しいことに、色の変化は均一に認識されないため、適切なしきい値の決定的な値はありません。緑の変化は青の変化よりもはるかに速く認識されます...そのため、緑のトーンの場合、小さいしきい値は機能する可能性がありますが、他の色などには十分ではありません。 HSV色空間で望ましい結果が得られない場合に最適です。
bummzack

1

あなたは静止した背景を得たと言ったので、ボールの色はまだランダムである可能性がありますが、それらはまだ背景を補完する特定の範囲に落ちなければなりません。

基本。その前に、基本を知る必要があります。次の色を考慮してください。

Black   #000000 rgb(0,0,0)
Red     #FF0000 rgb(255,0,0)
Green   #00FF00 rgb(0,255,0)
Blue    #0000FF rgb(0,0,255)
Yellow  #FFFF00 rgb(255,255,0)
Cyan    #00FFFF rgb(0,255,255)
Pink    #FF00FF rgb(255,0,255)
Gray    #C0C0C0 rgb(192,192,192)
White   #FFFFFF rgb(255,255,255)

色の混合RGB [(0..255)、(0..255)、(0..255)]は、上記のように新しい色を作成します。

負の色の計算負の色の計算は、赤をシアンに、緑を紫に、青を黄色に変換するようなものです。

Red     #FF0000 rgb(255,0,0) ->     Cyan    #00FFFF rgb(0,255,255)
Green   #00FF00 rgb(0,255,0) ->     Purple   #FF00FF    rgb(255,0,255)
Blue    #0000FF rgb(0,0,255) ->     Yellow  #FFFF00 rgb(255,255,0)

補色

補色の計算に関するリファレンス:http : //serennu.com/colour/rgbtohsl.php

HSLについて

HSLは、色を色相、彩度、明度で表現し、これら3つの色の属性ごとに番号を付けます。

色相は、カラーホイール上の色の位置で、0°から359°の角度で表され、ホイールの360°を表します。0°は赤、180°は赤の反対色のシアンなどです。

彩度とは、色の強さ、色の鈍さまたは明るさです。彩度が低いほど、色が鈍くなります(灰色になります)。これは百分率で表され、100%は完全な彩度で最も明るく、0%は彩度なし、灰色です。

明るさは色の明るさです。彩度とは少し異なります。色の白が多いほどその明度値が高くなり、黒が多いほどその明度は低くなります。したがって、100%の明度は色を白に、0%の明度は色を黒に、「純粋な」色は50%の明度になります。

彩度と明度の違いは、説明するよりも簡単にわかります。明確にしたい場合は、色の計算ページで明度と彩度のバリエーションを表示し、スターターカラーとして非常に明るい色を選択してください。

HSL表記は次のようになり、色相、彩度、明度の値を次の順序で示します。

赤:0°100%50%淡いピンク:0°100%90%シアン:180°100%50%手順は次のとおりです。

  1. 色をHSLに変換します。

  2. Hueの値を反対のHueの値に変更します(たとえば、Hueが50°の場合、反対のHueはホイール上で230°になります-さらに180°回ります)。

  3. 彩度と明度の値をそのままにします。

  4. この新しいHSL値を元の色表記(RGBなど)に変換します。

EasyRGB.comのようなサイトは、RGBからHSLまたはその逆への一般的な変換を行うことができます。

プログラミングリファレンスごとにPHPで行われた例

RGBからHSLへの変換

青#0000FF rgb(0,0,255)より上の値は、赤16進数00 +緑16進数00 +青16進数FFとして表示できます。

$redhex  = substr($hexcode,0,2);
$greenhex = substr($hexcode,2,2);
$bluehex = substr($hexcode,4,2);

赤10進数0 +緑10進数0 +青10進数255としても表示できます。

$var_r = (hexdec($redhex)) / 255;
$var_g = (hexdec($greenhex)) / 255;
$var_b = (hexdec($bluehex)) / 255;

次に、これらの値をrgb2hslルーチンにプラグインします。以下は、その変換のためのEasyRGB.comの汎用コードのPHPバージョンです。

入力は上記の$ var_r、$ var_g、および$ var_bです。出力は、HSLで$ h、$ s、および$ lに相当します。これらは、入力値のように、1の小数部として表されます。

$var_min = min($var_r,$var_g,$var_b);ttt
$var_max = max($var_r,$var_g,$var_b);
$del_max = $var_max - $var_min;

$l = ($var_max + $var_min) / 2;

if ($del_max == 0)
{
        $h = 0;
        $s = 0;
}
else
{
        if ($l < 0.5)
        {
                $s = $del_max / ($var_max + $var_min);
        }
        else
        {
                $s = $del_max / (2 - $var_max - $var_min);
        };

        $del_r = ((($var_max - $var_r) / 6) + ($del_max / 2)) / $del_max;
        $del_g = ((($var_max - $var_g) / 6) + ($del_max / 2)) / $del_max;
        $del_b = ((($var_max - $var_b) / 6) + ($del_max / 2)) / $del_max;

        if ($var_r == $var_max)
        {
                $h = $del_b - $del_g;
        }
        elseif ($var_g == $var_max)
        {
                $h = (1 / 3) + $del_r - $del_b;
        }
        elseif ($var_b == $var_max)
        {
                $h = (2 / 3) + $del_g - $del_r;
        };

        if ($h < 0)
        {
                $h += 1;
        };

        if ($h > 1)
        {
                $h -= 1;
        };
};

これで、変数$ h、$ s、および$ lにHSL値として色ができました。これらの3つの出力変数は、この段階では度やパーセンテージとしてではなく、1の端数として保持されます。たとえば、シアン(180°100%50%)は、$ h = 0.5、$ s = 1、および$ l = 0.5として出力されます。

次に、反対側の色相の値、つまり180°または0.5離れた色相の値を見つけます(数学者はこれをよりエレガントにdotでる方法を持っているはずですが)。

反対の色相$ h2を計算します

            $h2 = $h + 0.5;

            if ($h2 > 1)
                    {
                            $h2 -= 1;
                    };

補色のHSL値は、現在$ h2、$ s、$ lです。したがって、これをRGBに変換する準備が整いました(これも、PHPバージョンのEasyRGB.com公式)。今回は入力形式と出力形式が異なることに注意してください。コードの上部にある私のコメントをご覧ください。

入力は補色のHSL値であり、1の小数として$ h2、$ s、$ lに保持出力は通常の255 255 255形式のRGB、$ r、$ g、$ bに保持Hueは関数hue_2_rgbを使用して変換されますこのコードの最後に

    if ($s == 0)
    {
            $r = $l * 255;
            $g = $l * 255;
            $b = $l * 255;
    }
    else
    {
            if ($l < 0.5)
            {
                    $var_2 = $l * (1 + $s);
            }
            elset
            {
                    $var_2 = ($l + $s) - ($s * $l);
            };

            $var_1 = 2 * $l - $var_2;
            $r = 255 * hue_2_rgb($var_1,$var_2,$h2 + (1 / 3));
            $g = 255 * hue_2_rgb($var_1,$var_2,$h2);
            $b = 255 * hue_2_rgb($var_1,$var_2,$h2 - (1 / 3));
    };

   // Function to convert hue to RGB, called from above

    function hue_2_rgb($v1,$v2,$vh)
    {
            if ($vh < 0)
            {
                    $vh += 1;
            };

            if ($vh > 1)
            {
                    $vh -= 1;
            };

            if ((6 * $vh) < 1)
            {
                    return ($v1 + ($v2 - $v1) * 6 * $vh);
            };

            if ((2 * $vh) < 1)
            {
                    return ($v2);
            };

            if ((3 * $vh) < 2)
            {
                    return ($v1 + ($v2 - $v1) * ((2 / 3 - $vh) * 6));
            };

            return ($v1);
    };

そして、そのルーチンの後、最終的に255 255 255(RGB)形式の$ r、$ g、および$ bが得られ、6桁の16進数に変換できます。

    $rhex = sprintf("%02X",round($r));
    $ghex = sprintf("%02X",round($g));
    $bhex = sprintf("%02X",round($b));

    $rgbhex = $rhex.$ghex.$bhex;

$ rgbhexが私たちの答えです。16進数の補色です。

色の背景は青または0,0,255なので、HSLは

色相(H):240度/彩度(S):100%/明度(L):4.9%

240の反対は円で60で、その後RGBに変換すると値が#181800になります


ありがとう!しかし、リンクはそれ自体の「補色」(それが何であれ)の生成について述べています。私は問題を解決するためにそれをどのように使用するかについてまだ手がかりがありません:各ランダムな色が背景色に似ていないことを確認してください。背景色は一定の単色で、複数のボールがあります。
メストレリオン14

Kromが今それをしてくれてありがとう。@MestreLion、背景は一定であるため、背景を補完する範囲をボールに指定できます。
エースカセリャ14

現在、私の答えを詳しく説明しています。
エースカセリャ14

できた それは十分に説明されているので、私はサイトの説明で多くを変更することはありません。EasyRGB.comでみんなにクレジット
エースCaserya


1

アウトラインを作成するという@ ashes999のアイデアを拡張したいと思います。

私のコードは、私の頭の中でできる限りPythonのようになります(私の人生でPythonの行を書いたことはありませんが、1回か2回本を見たことがあります)。

def lerp(a,b,value): # assuming your library was not kind enough to give you this for free
    return a + ((b - a) * value);

def drawRandomBall(bgColour,radius,point):
    var ballColour = Colour(random(0,255),random(0,255),random(0,255);
    var outlineColour = Colour(lerp(bgColour.R,255 - ballColour.R,0.5),lerp(bgColour.G,255 - ballColour.G,0.5),lerp(bgColour.B,255 - ballColour.B,0.5));
    fillCircle(point,radius+1,outlineColour);
    fillCircle(point,radius,ballColour);

それは特にきれいに見えないかもしれず、本番コードには本当に理想的ではありませんが、クイックフィックスとしては十分でしょう。「画面上で跳ねるボール」プログラムを正確に持っているわけではないので、コードをテストしていません。


編集:

使用中の実際のコードを見ると、状況は頭をひっくり返しているので、代わりにこのソリューションを提供します。

行276〜284を次の行に置き換えます。

# Colour generator
def generateColourNonBG(leeway):
    color = (randint(0,255), randint(0,255), randint(0,255))
    while color[0] >= BG_COLOR[0]-leeway and color[0] =< BG_COLOR[0] + leeway and
    color[1] >= BG_COLOR[1]-leeway and color[1] =< BG_COLOR[1] + leeway and
    color[2] >= BG_COLOR[2]-leeway and color[2] =< BG_COLOR[2] + leeway:
        color = (randint(0,255), randint(0,255), randint(0,255))

# Ball generator
def generateBall():
    Ball(generateColourNonBG(5),
                   radius=randint(10, radius),
                   position=[randint(100, screen.get_size()[0]-radius),
                             randint(100, screen.get_size()[0]-radius)],
                   velocity=[randint(50, 50+vel[0]), randint(50, 50+vel[1])],
                   )                       

# Create the balls
balls = pygame.sprite.Group()
for _ in xrange(BALLS):
    balls.add(generateBall())

この新しい簡易バージョンでは、ファクトリーメソッドがボールを吐き出し、特殊なメソッドが色を生成します。カラージェネレーターは、ランダムに生成された色をテストして、背景色にある程度近い範囲内にあるかどうかを確認します。カラーチャネルの3つすべてleewayがBGの両側にある場合、カラーはBGカラーに近すぎると見なされます。そのため、leewayが0の場合、BGと完全に一致する色のみが拒否されます。デフォルトとして5を選択しましたが、どちらの側もBG色と5色を拒否します。BGの色は白なので、数字がくるくると黒を拒否するかどうかはわかりません。min関数とmax関数を使用すれば回避できますが、簡潔にするために省略しました。


私はどんな戦略に開いている:)不具にプログラムのためとして、私はそれをあなたを与えることができます。github.com/MestreLion/rainballs関連する行が279から284になります
MestreLion

pygameライブラリとして使用しています。Lerpなし、基本的なRGB-HSV-CYMK変換のみ: pygame.org/docs/ref/color.html
MestreLion

@MestreLion Ohロード、テストするためにpygameとpythonをインストールする必要があります。私はこの1つのXPで自分の足を撃ちました。Lerpingは簡単lerpです。私の答えの機能はそれだけです。(Lerpは「線形補間」を意味します)。
ファラプ14

@MestreLion少しいじって、私のバージョンのpythonはprintが括弧なしで呼び出されることを嫌います。それを修正しましたが、pygameが適切なライブラリを見つけられないというエラーが発生しました。あなたが持っているものに合うようにコードを書き直しますが、pygameを修正するまではテストできません。まだPythonを書いてくれない人がいれば、質問にファイルを追加することをお勧めします。
ファラプ14

printPython 3を使用しているのであなたは異なります。私はまだPython 2 pygameを使用しています。そして、まだPy3に移植されたかどうかはわかりません。ボトムライン:気にしないでください:)質問は色に関するもので、実際にボールが跳ね回る必要はありません:)
MestreLion 14

1

RGB形式の場合、これは十分に機能するようで、簡単です。

多様性スコア=(abs(r1-r2)+ abs(g1-g2)+ abs(b1-b2))/ 765.0

これにより、0.0〜1.0のスコアが得られます。低いほど、2色を区別するのが難しくなります。

明らかなはずですが、完全を期すために、r、g、bの値は最初に浮動小数点数にキャストする必要があり、最大値は255であると想定されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.