20

私のC#(3.5)アプリケーションでは、ビットマップの赤、緑、青のチャネルの平均カラー値を取得する必要があります。できれば外部ライブラリを使用せずに。これはできますか?もしそうなら、どのように?前もって感謝します。

もう少し正確にしようとしています。ビットマップの各ピクセルには、特定のRGBカラー値があります。画像内のすべてのピクセルの平均RGB値を取得したいと思います。

4

3 に答える 3

25

最速の方法は、安全でないコードを使用することです。

BitmapData srcData = bm.LockBits(
            new Rectangle(0, 0, bm.Width, bm.Height), 
            ImageLockMode.ReadOnly, 
            PixelFormat.Format32bppArgb);

int stride = srcData.Stride;

IntPtr Scan0 = srcData.Scan0;

long[] totals = new long[] {0,0,0};

int width = bm.Width;
int height = bm.Height;

unsafe
{
  byte* p = (byte*) (void*) Scan0;

  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      for (int color = 0; color < 3; color++)
      {
        int idx = (y*stride) + x*4 + color;

        totals[color] += p[idx];
      }
    }
  }
}

int avgB = totals[0] / (width*height);
int avgG = totals[1] / (width*height);
int avgR = totals[2] / (width*height);

注意: 私はこのコードをテストしませんでした... (私はいくつかのコーナーをカットしたかもしれません)

このコードも 32 ビット イメージを想定しています。24 ビット イメージの場合。x*4x*3に変更します

于 2009-07-01T10:34:54.797 に答える
13

この種のことは機能しますが、それほど便利であるほど高速ではない場合があります。

public static Color GetDominantColor(Bitmap bmp)
{

       //Used for tally
       int r = 0;
       int g = 0;
       int b = 0;

     int total = 0;

     for (int x = 0; x < bmp.Width; x++)
     {
          for (int y = 0; y < bmp.Height; y++)
          {
               Color clr = bmp.GetPixel(x, y);

               r += clr.R;
               g += clr.G;
               b += clr.B;

               total++;
          }
     }

     //Calculate average
     r /= total;
     g /= total;
     b /= total;

     return Color.FromArgb(r, g, b);
}
于 2009-07-01T10:33:17.843 に答える
12

はるかに簡単な方法を次に示します。

Bitmap bmp = new Bitmap(1, 1);
Bitmap orig = (Bitmap)Bitmap.FromFile("path");
using (Graphics g = Graphics.FromImage(bmp))
{
    // updated: the Interpolation mode needs to be set to 
    // HighQualityBilinear or HighQualityBicubic or this method
    // doesn't work at all.  With either setting, the results are
    // slightly different from the averaging method.
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.DrawImage(orig, new Rectangle(0, 0, 1, 1));
}
Color pixel = bmp.GetPixel(0, 0);
// pixel will contain average values for entire orig Bitmap
byte avgR = pixel.R; // etc.

基本的に、DrawImage を使用して元のビットマップを 1 ピクセルのビットマップにコピーします。その 1 ピクセルの RGB 値は、オリジナル全体の平均を表します。GetPixel は比較的低速ですが、大きなビットマップでピクセル単位で使用する場合のみです。ここで一度呼び出すことは大したことではありません。

LockBits の使用は確かに高速ですが、一部の Windows ユーザーは、「安全でない」コードの実行を防止するセキュリティ ポリシーを持っています。私がこれについて言及するのは、この事実が最近私を悩ませているからです。

更新: InterpolationMode を HighQualityBicubic に設定すると、このメソッドは LockBits で平均化するよりも約 2 倍の時間がかかります。HighQualityBilinear を使用すると、LockBits よりもわずかに時間がかかります。したがって、ユーザーがコードを禁止するセキュリティ ポリシーを持っていない限り、unsafe私の方法は絶対に使用しないでください。

更新 2: 時間が経つにつれて、このアプローチがまったく機能しない理由がわかりました。最高品質の補間アルゴリズムでさえ、少数の隣接ピクセルしか取り込まないため、情報を失わずに画像を縮小できる量には限界があります。また、画像を 1 ピクセルに縮小することは、使用するアルゴリズムに関係なく、この制限をはるかに超えています。

これを行う唯一の方法は、画像を 1 ピクセルのサイズになるまで段階的に縮小することです (おそらく毎回半分ずつ縮小します)。このようなことを書くのがどれほど時間の無駄になるか、単なる言葉では表現できないので、考えたときに自分を止めてよかったです。:)

もう誰もこの回答に投票しないでください。これは私の最もばかげた考えかもしれません。

于 2009-08-26T15:36:50.073 に答える