ビットマップの平均 RGB カラー値を計算する方法
質問
私の C# (3.5) アプリケーションでは、ビットマップの赤、緑、青のチャネルの平均カラー値を取得する必要があります。できれば外部ライブラリを使用しないでください。これはできるでしょうか?もしそうなら、どのようにして?前もって感謝します。
もう少し正確にしてみます。ビットマップ内の各ピクセルは特定の RGB カラー値を持ちます。画像内のすべてのピクセルの平均 RGB 値を取得したいと考えています。
解決
最速の方法は、安全でないコードを使用することです。
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ビットの画像をasssumes。 24ビット画像の。変更の X * 4 のへの X * 3 の
他のヒント
もっと簡単な方法は次のとおりです。
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 ピクセルのサイズになるまで画像を段階的に縮小することです (おそらく毎回半分ずつ縮小します)。このようなことを書くことがどれほど時間の無駄であるかは、単なる言葉では言い表すことができないので、思いついたときにやめておいてよかったと思います。:)
もう誰もこの回答に投票しないでください。これは私のこれまでで最も愚かなアイデアかもしれません。
この種のことは動作しますが、その有用であるために十分な速さではないかもしれない。
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);
}