4

C# で画像を 10 色に量子化しようとしていますが、量子化された画像の描画に問題があります。マッピング テーブルを作成しましたが、正しいです。元の画像のコピーを作成し、色を変更しています。マッピングテーブルに基づくピクセル、私は以下のコードを使用しています:

bm = new Bitmap(pictureBox1.Image);
        Dictionary<Color, int> histo = new Dictionary<Color, int>();
        for (int x = 0; x < bm.Size.Width; x++)
            for (int y = 0; y < bm.Size.Height; y++)
            {
                Color c = bm.GetPixel(x, y);
                if (histo.ContainsKey(c))
                    histo[c] = histo[c] + 1;
                else
                    histo.Add(c, 1);
            }
        var result1 = histo.OrderByDescending(a => a.Value);
                  int ind = 0;
        List<Color> mostusedcolor = new List<Color>();
        foreach (var entry in result1)
        {
            if (ind < 10)
            {
                mostusedcolor.Add(entry.Key);
                ind++;
            }
            else
                break;
        }
        Double temp_red,temp_green,temp_blue,temp;
        Dictionary<Color, Double> dist = new Dictionary<Color, double>();
        Dictionary<Color, Color> mapping = new Dictionary<Color, Color>();
        foreach (var p in result1)
        {
            dist.Clear();
            foreach (Color pp in mostusedcolor)
            {
                temp_red = Math.Pow((Convert.ToDouble(p.Key.R) - Convert.ToDouble(pp.R)), 2.0);
                temp_green = Math.Pow((Convert.ToDouble(p.Key.G) - Convert.ToDouble(pp.G)), 2.0);
                temp_blue = Math.Pow((Convert.ToDouble(p.Key.B) - Convert.ToDouble(pp.B)), 2.0);
                temp = Math.Sqrt((temp_red + temp_green + temp_blue));
                dist.Add(pp, temp);
            }
            var min = dist.OrderBy(k=>k.Value).FirstOrDefault();
            mapping.Add(p.Key, min.Key);
        }
  Bitmap copy = new Bitmap(bm);

        for (int x = 0; x < copy.Size.Width; x++)
            for (int y = 0; y < copy.Size.Height; y++)
            {
                Color c = copy.GetPixel(x, y);
                Boolean flag = false;
                foreach (var entry3 in mapping)
                {
                    if (c.R == entry3.Key.R && c.G == entry3.Key.G && c.B == entry3.Key.B)
                    {
                        copy.SetPixel(x, y, entry3.Value);
                        flag = true;
                    }
                    if (flag == true)
                        break;

                }
            }
pictureBox2.Image=copy;
4

1 に答える 1

5

コードには 2 つの問題があります。

  • それはひどく遅いです
  • 量子化は私が期待するものではありません。

元の画像、コードの結果、および 10 色に減らすように求められたときに Photoshop が行うことは次のとおりです。

ここに画像の説明を入力

  • コードを高速化するには、次の 2 つの手順を実行します。

    • 最も不快な時間の浪費者を取り除きます
    • GetPixelとループSetPixelをループに変えLockbitsます。

コードを少なくとも 100 倍高速化するステップ 1 のソリューションを次に示します。

Bitmap bm = (Bitmap)Bitmap.FromFile("d:\\ImgA_VGA.png");
pictureBox1.Image = bm;

Dictionary<Color, int> histo = new Dictionary<Color, int>();
for (int x = 0; x < bm.Size.Width; x++)
    for (int y = 0; y < bm.Size.Height; y++)
    {
        Color c = bm.GetPixel(x, y);   // **1**
        if (histo.ContainsKey(c))  histo[c] = histo[c] + 1;
        else histo.Add(c, 1);
    }
var result1 = histo.OrderByDescending(a => a.Value);
int number = 10;
var mostusedcolor = result1.Select(x => x.Key).Take(number).ToList();

Double temp;
Dictionary<Color, Double> dist = new Dictionary<Color, double>();
Dictionary<Color, Color> mapping = new Dictionary<Color, Color>();
foreach (var p in result1)
{
    dist.Clear();
    foreach (Color pp in mostusedcolor)
    {
        temp = Math.Abs(p.Key.R - pp.R) + 
               Math.Abs(p.Key.R - pp.R) + 
               Math.Abs(p.Key.R - pp.R);
        dist.Add(pp, temp);
    }
    var min = dist.OrderBy(k => k.Value).FirstOrDefault();
    mapping.Add(p.Key, min.Key);
}
Bitmap copy = new Bitmap(bm);

for (int x = 0; x < copy.Size.Width; x++)
    for (int y = 0; y < copy.Size.Height; y++)
    {
        Color c = copy.GetPixel(x, y);   // **2**
        copy.SetPixel(x, y, mapping[c]);
    }
pictureBox2.Image = copy;

色を並べるだけなら、ピタゴラスの力をフルに使って距離を計算する必要はないことに注意してください。マンハッタンの距離は問題ありません。

また、画像内のすべての色をキーとして含むルックアップ ディクショナリが既にあるmappingため、値に直接アクセスできることにも注意してください。(これは最悪の時間の無駄でした..)

テスト画像は1 秒以内に処理されるため、LockBits変更も必要ありません..

  • しかし、量子化を修正するのはそれほど簡単ではありません。残念ながら、imoは良いSOの質問の範囲を超えています。

    • しかし、何がうまくいかないのかを見てみましょう: 結果を見ると、一目でほとんどわかります: たくさんの空があり、これらの多くの青のピクセルはすべて 10 を超える色相を持っているため、すべての色がトップ 10 に含まれています。リストは青色です。

    • したがって、画像全体に他の色合いは残っていません。

    • これを回避するには、一般的な量子化アルゴリズムを研究するのが最善です..

コードを修復する単純なアプローチの 1 つは、最もよく使用されるリストから、既に持っている色のいずれかに近すぎるすべての色を破棄/マッピングすることです。しかし、最適な最小距離を見つけるには、相馬データ分析が必要です..

更新コードを改善するもう 1 つの非常に簡単な方法は、実際の色をいくつかの下位ビットでマスクして、類似した色を一緒にマップすることです。10 色だけを選択するのはまだ少なすぎますが、このテスト画像でも改善が見られます。

Color cutOff(Color c, byte mask)
{  return Color.FromArgb(255, c.R & mask, c.G & mask, c.B & mask );   }

これをここに挿入します ( 1 ) :

byte mask = (byte)255 << 5 & 0xff;  // values of 3-5 worked best
Color c = cutOff(bm.GetPixel(x, y), mask);

そしてここ(2):

Color c = cutOff(copy.GetPixel(x, y), mask);

そして、次のようになります。

ここに画像の説明を入力

まだ黄色、オレンジ、または茶色の色相はすべて失われていますが、1 行追加するだけで改善されます..

于 2016-01-01T21:46:50.413 に答える