画像から目立つ色のパレットを生成するにはどうすればよいですか?
-
26-10-2019 - |
質問
画像ですべてのピクセルをサンプリングし、そこから色のパレットを生成する方法を見つけようとしています。 これ また これ. 。どこから始めればいいのかわかりません。誰かが私を正しい方向に向けることができますか?
__編集: __
これは私がこれまでに終わったことです:
私はこれを使用しました ピクセル酸塩 Joe_Coolishのような大きなブロックセクションを取得する機能を提案します。それは完璧に機能しており、私にはかなり良い色のサンプルを提供しています(これはWindowsサンプルゼリーフィッシュの写真からです):
さて、誰かが私が5つの最も明確な色(最も暗い青、最も明るい青、オレンジ、グレー、ピーチ(?)を手伝うことができれば、私はあなたを永遠に愛します。私は本当に方法がわかりません 平均 また 追加 一緒に色。また、色がプログラム的に似ているかどうかを判断する方法を理解することはできません。あなたの説明には、何をしているのかを理解しようとして失われてしまう説明がいくつかあります。
解決
コードを含む回答は、フルパレットを取得する方法を示しています。投稿したWebサイトのような平均的な色を取得したい場合は、これが私がそれをする方法です。
ソース画像:
まず、ローパスフィルター(ガウスのぼかしのようなもの)を適用することで色を平均します。
そうすれば、合計パレットを制限しています。そこから画面をNブロックに分割します(nはパレットに必要なピクセルの総数です)
そこから、各ブロックをターゲットにし、各ピクセルを反復し、そのブロックの平均ピクセルを取得し、パレットインデックスに追加します。結果は次のようなものです:
そうすれば、パレットが制限されており、さまざまな地域から平均色を取得します。そのすべてをコードで行うことができます。それを手伝ってほしい場合は、私に知らせてください。投稿してください。これは、「私がやること」という高いレベルです。
他のヒント
まず、写真のピクセルを取ります:( using System.Drawing.Imaging;
と using System.Runtime.InteropServices
)
Bitmap b = new Bitmap(myImage);
BitmapData bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, ImageFormat.Format32Bpp);
int[] arr = new int[bd.Width * bd.Height - 1];
Marshal.Copy(bd.Scan0, arr, 0, arr.Length);
b.UnlockBits(bd);
その後、パレットを作成できます。
var distinctColors = arr.Distinct();
オプションでは、好みのパレットサイズになるまで同様の色を排除します。あなたがそれを行う方法は次のとおりです(これは間違いなく最も効率的または正確な方法ではなく、最も単純な方法ではありません):
var dc = distinctColors.toArray(); // int dc[] = distinctColors.toArray() is what it used to be
int cmIndex1 = -1;
int cmIndex2 = -1;
int cmDiff = -1;
for (int i = 0; i < dc.length; i++) {
Color c1 = Color.FromArgb(dc[i]);
for (int j = i + 1; j < dc.length; j++) {
Color c2 = Color.FromArgb(dc[j]);
// Note: you might want to include alpha below
int diff = Math.Abs(c1.R - c2.R) + Math.Abs(c1.G - c2.G) + Math.Abs(c1.B - c2.B);
if (cmDiff < 0 || diff < cmDiff) {
cmIndex1 = i;
cmIndex2 = j;
cmDiff = diff;
}
}
}
// Remove the colors, replace with average, repeat until you have the desired number of colors
どんな豊かなイメージでも、ほとんどの色が何らかの形でユニークになる可能性があります。したがって、独特の色を取得することは、あなたがあなたの目標を達成するのに役立つ可能性が高いということです。
画像の各ピクセルのHSV値を検査することをお勧めします。 HSV値の配列として画像を取得する数え切れないほどのオンライン例に任せます。
HSV値を使用すると、256色のカウントの整数配列を作成して、画像データの色相のヒストグラムを計算することにより、顕著な色相のクラスターを計算できます。 4〜6のシーケンシャルヒューのクラスターを高いカウント合計で見つけることで、顕著な色相を決定できます。
いくつかの顕著な色合いを選んだ後、これらの色相のピクセルを細分化した飽和を測定する別のヒストグラムに分割し、顕著なクラスターなどを選択します。
大まかな例
以下のコードは、顕著な色合いを特定するのに役立ついくつかの試みを行います。これを行う他の素晴らしい方法はおそらくあります。ただし、これはいくつかのアイデアを提供する可能性があります。
最初に、私はすべての画像の色をの配列として取得します Color
そのようなオブジェクト:
private static Color[] GetImageData(Image image)
{
using (var b = new Bitmap(image))
{
var bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte[] arr = new byte[bd.Width * bd.Height * 3];
Color[] colors = new Color[bd.Width * bd.Height];
Marshal.Copy(bd.Scan0, arr, 0, arr.Length);
b.UnlockBits(bd);
for (int i = 0; i < colors.Length; i++)
{
var start = i*3;
colors[i] = Color.FromArgb(arr[start], arr[start + 1], arr[start + 2]);
}
return colors;
}
}
あなたは私がRGBの注文を取得したことを検証することを検討するかもしれません Color.FromArgb
メソッドコールは正しい順序で呼び出します。
次に、HSVに変換するためのユーティリティ方法を脇に置きます。私の例では、私は色相を使用するだけですが、これが変換の完全な実用的な例です。
private static void ColorToHSV(Color color, out int hue, out int saturation, out int value)
{
int max = Math.Max(color.R, Math.Max(color.G, color.B));
int min = Math.Min(color.R, Math.Min(color.G, color.B));
hue = (int)(color.GetHue() * 256f / 360f);
saturation = (max == 0) ? 0 : (int)(1d - (1d * min / max));
value = (int)(max / 255d);
}
最後に、色相ヒストグラムを構築し、カウントを一緒に集約するための色相の幅(たとえば、9つの色相)を定義し、コンソールにカウントを報告します。
private static void ProcessImage(Color[] imagecolors)
{
var hues = new int[256];
var hueclusters = new int[256];
int hue, saturation, value;
// build hue histogram.
foreach (var color in imagecolors) {
ColorToHSV(color, out hue, out saturation, out value);
hues[hue]++;
}
// calculate counts for clusters of colors.
for (int i = 0; i < 256; i++) {
int huecluster = 0;
for (int count = 0, j = i; count < 9; count++, j++) {
huecluster += hues[j % 256];
}
hueclusters[(i + 5) % 256] = huecluster;
}
// Print clusters on the console
for (int i = 0; i < 256; i++) {
Console.WriteLine("Hue {0}, Score {1}.", i, hueclusters[i]);
}
}
私はフィルタリングしようとはしませんでした どれの 選択する色合い。おそらく、色のスペクトルで多少分離されている色合いを選びたいので、おそらくトップのソーマニュカウントを盲目的に選ぶのではなく、いくつかのヒューリスティックを考慮する必要があるかもしれません。これ以上探求する時間はありませんが、これがあなたが考慮できる戦略についての洞察を提供することを願っています。
私はここから始めました:
System.Drawing.Image img = System.Drawing.Bitmap.FromFile("file");
System.Drawing.Imaging.ColorPalette palette = img.Palette;
foreach (Color color in palette.Entries)
{
//...
}
非常に高いレベルで最高のアプローチを説明します。
最初に、写真とその周波数に色のヒストグラムを作成します。
画像内のすべての色のリストが表示されます。データクラスタリングを使用して、候補の色を見つけることができます。元の色の頻度に基づいて、合併して加重平均にマージされます。
このようにして、パレットを徐々に希望の忠実に減らし、高いコントラストであるが細かい詳細を維持し、グラデーションがはるかに微妙な場合に忠実を失うだけです。
縮小されたパレットができたら、パレットにある最近隣接の色を使用して画像を再注文します。
k-means クラスタリングアルゴリズムは、この問題に適しています。画像カラークラスターの重心を抽出するという素晴らしい仕事をしますが、その非決定的な動作により、各クラスターの実際の顕著性が困難になることに注意してください。