カラーホイール作成機能【公開終了】
-
08-06-2019 - |
質問
これは私が何度も疑似解決してきましたが、完全に解決策を見つけたことはありません。
問題は、生成する方法を考え出すことです。 N
可能な限り識別可能な色 N
はパラメータです。
解決
これについて私が最初に考えたのは、「相互の距離を最大化する空間内で N 個のベクトルを生成する方法」です。
RGB (または色空間の基礎を形成するその他のスケール) が単なるベクトルであることがわかります。を見てみましょう ランダムな点の選択. 。最大限に離れたベクトルのセットを取得したら、後で使用できるようにそれらをハッシュ テーブルなどに保存し、ランダムな回転を実行するだけで、互いに最大限に離れた希望の色をすべて取得できます。
この問題をさらに詳しく考えると、色を線形にマッピングし、おそらく辞書順に (0,0,0) → (255,255,255) マッピングし、それらを均等に分配する方がよいでしょう。
これがどれだけうまくいくかは本当にわかりませんが、うまくいくはずです。次のように言ってみましょう。
n = 10
16777216 色 (256^3) があることがわかっています。
使用できます バックルアルゴリズム 515 辞書順にインデックス付けされた色を検索します。. 。おそらく、オーバーフローを回避するためにアルゴリズムを編集し、若干の速度向上を追加する必要があるでしょう。
他のヒント
「知覚的に均一な」色空間で最大限に離れた色を見つけるのが最善です。CIELAB (距離メトリックとして L*、a*、b* 座標間のユークリッド距離を使用) を使用して、選択した色空間に変換します。知覚的な均一性は、人間の視覚システムの非線形性を近似するために色空間を微調整することによって実現されます。
いくつかの関連リソース:
カラーブリューワー - 地図上で使用するために最大限に区別できるように設計された色のセット。
RGBland のエスケープ:統計グラフィックスの色の選択 - 良いものを生成するための一連のアルゴリズムを説明する技術レポート。最大限に区別可能な) hcl 色空間のカラー セット。
以下は、指定された明度の HSL カラー ホイールの周囲に RGB カラーを均等に割り当てるコードです。
class cColorPicker
{
public:
void Pick( vector<DWORD>&v_picked_cols, int count, int bright = 50 );
private:
DWORD HSL2RGB( int h, int s, int v );
unsigned char ToRGB1(float rm1, float rm2, float rh);
};
/**
Evenly allocate RGB colors around HSL color wheel
@param[out] v_picked_cols a vector of colors in RGB format
@param[in] count number of colors required
@param[in] bright 0 is all black, 100 is all white, defaults to 50
based on Fig 3 of http://epub.wu-wien.ac.at/dyn/virlib/wp/eng/mediate/epub-wu-01_c87.pdf?ID=epub-wu-01_c87
*/
void cColorPicker::Pick( vector<DWORD>&v_picked_cols, int count, int bright )
{
v_picked_cols.clear();
for( int k_hue = 0; k_hue < 360; k_hue += 360/count )
v_picked_cols.push_back( HSL2RGB( k_hue, 100, bright ) );
}
/**
Convert HSL to RGB
based on http://www.codeguru.com/code/legacy/gdi/colorapp_src.zip
*/
DWORD cColorPicker::HSL2RGB( int h, int s, int l )
{
DWORD ret = 0;
unsigned char r,g,b;
float saturation = s / 100.0f;
float luminance = l / 100.f;
float hue = (float)h;
if (saturation == 0.0)
{
r = g = b = unsigned char(luminance * 255.0);
}
else
{
float rm1, rm2;
if (luminance <= 0.5f) rm2 = luminance + luminance * saturation;
else rm2 = luminance + saturation - luminance * saturation;
rm1 = 2.0f * luminance - rm2;
r = ToRGB1(rm1, rm2, hue + 120.0f);
g = ToRGB1(rm1, rm2, hue);
b = ToRGB1(rm1, rm2, hue - 120.0f);
}
ret = ((DWORD)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)));
return ret;
}
unsigned char cColorPicker::ToRGB1(float rm1, float rm2, float rh)
{
if (rh > 360.0f) rh -= 360.0f;
else if (rh < 0.0f) rh += 360.0f;
if (rh < 60.0f) rm1 = rm1 + (rm2 - rm1) * rh / 60.0f;
else if (rh < 180.0f) rm1 = rm2;
else if (rh < 240.0f) rm1 = rm1 + (rm2 - rm1) * (240.0f - rh) / 60.0f;
return static_cast<unsigned char>(rm1 * 255);
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<DWORD> myCols;
cColorPicker colpick;
colpick.Pick( myCols, 20 );
for( int k = 0; k < (int)myCols.size(); k++ )
printf("%d: %d %d %d\n", k+1,
( myCols[k] & 0xFF0000 ) >>16,
( myCols[k] & 0xFF00 ) >>8,
( myCols[k] & 0xFF ) );
return 0;
}
どの順番で色を設定するかも重要ではないでしょうか?
たとえば、Dillie-O のアイデアを使用する場合は、できるだけ色を混ぜる必要があります。0 64 128 256 は次から次へと続きます。しかし、ホイール内の 0 256 64 128 はより「離れた」状態になります。
これには意味がありますか?
人間の目は 4 つ未満の値を区別できないとどこかで読んだことがあります。これは心に留めておくべきことです。次のアルゴリズムはこれを補償しません。
これがまさにあなたが望むものであるかどうかはわかりませんが、これは非反復カラー値をランダムに生成する 1 つの方法です。
(一貫性のない疑似コードがあることに注意してください)
//colors entered as 0-255 [R, G, B]
colors = []; //holds final colors to be used
rand = new Random();
//assumes n is less than 16,777,216
randomGen(int n){
while (len(colors) < n){
//generate a random number between 0,255 for each color
newRed = rand.next(256);
newGreen = rand.next(256);
newBlue = rand.next(256);
temp = [newRed, newGreen, newBlue];
//only adds new colors to the array
if temp not in colors {
colors.append(temp);
}
}
}
視認性を高めるためにこれを最適化する 1 つの方法は、新しい各色と配列内のすべての色の間の距離を比較することです。
for item in color{
itemSq = (item[0]^2 + item[1]^2 + item[2]^2])^(.5);
tempSq = (temp[0]^2 + temp[1]^2 + temp[2]^2])^(.5);
dist = itemSq - tempSq;
dist = abs(dist);
}
//NUMBER can be your chosen distance apart.
if dist < NUMBER and temp not in colors {
colors.append(temp);
}
ただし、このアプローチではアルゴリズムの速度が大幅に低下します。
別の方法は、ランダム性を排除し、4 つの値ごとに体系的に調べて、上記の例の配列に色を追加することです。
これが古い投稿であることは知っていますが、このトピックに対する PHP ソリューションを探しているときに見つけて、最終的に簡単なソリューションを見つけました。
function random_color($i = null, $n = 10, $sat = .5, $br = .7) {
$i = is_null($i) ? mt_rand(0,$n) : $i;
$rgb = hsv2rgb(array($i*(360/$n), $sat, $br));
for ($i=0 ; $i<=2 ; $i++)
$rgb[$i] = dechex(ceil($rgb[$i]));
return implode('', $rgb);
}
function hsv2rgb($c) {
list($h,$s,$v)=$c;
if ($s==0)
return array($v,$v,$v);
else {
$h=($h%=360)/60;
$i=floor($h);
$f=$h-$i;
$q[0]=$q[1]=$v*(1-$s);
$q[2]=$v*(1-$s*(1-$f));
$q[3]=$q[4]=$v;
$q[5]=$v*(1-$s*$f);
return(array($q[($i+4)%6]*255,$q[($i+2)%6]*255,$q[$i%6]*255)); //[1]
}
}
したがって、random_color() 関数を呼び出すだけです。ここで、$i は色、$n は可能な色の数、$sat は彩度、$br は明るさを指定します。
「最も区別しやすい」を実現するには、RGB ではなく、Lab のような知覚色空間 (またはその他の知覚的に線形な色空間) を使用する必要があります。また、この空間を量子化して、空間のサイズを縮小することもできます。
可能なすべての量子化エントリを含む完全な 3D 空間を生成し、次のように K 平均法アルゴリズムを実行します。 k=N
. 。結果として得られる中心/「手段」は、互いにほぼ区別できるはずです。