Как вычислить средние значения цвета rgb растрового изображения
в моем приложении C# (3.5) мне нужно получить средние значения цвета для красного, зеленого и синего каналов растрового изображения. Желательно без использования внешней библиотеки. Можно ли это сделать? Если да, то как? Спасибо заранее.
попытка сделать вещи немного точнее: каждый пиксель в растровом изображении имеет определенное значение цвета RGB. Я хотел бы получить средние значения RGB для всех пикселей изображения.
3 ответов
самый быстрый способ-использовать небезопасный код:
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);
осторожно: я не тестировал этот код... (Возможно, я срезал несколько углов)
этот код также asssumes 32-битного изображения. Для 24-битных изображений. Изменить x * 4 to 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 пикселя. Значения RGB этого 1 пикселя будут представлять средние значения для всего оригинала. GetPixel относительно медленный, но только когда вы используете его на большом растровом изображении, пиксель за пикселем. Позвонить один раз-это не важно.
использование LockBits действительно быстро, но некоторые пользователи Windows имеют политики безопасности, которые предотвращают выполнение "небезопасного" кода. Я упоминаю об этом, потому что этот факт просто укусил меня сзади в последнее время.
обновление: при InterpolationMode, установленном в HighQualityBicubic, этот метод занимает примерно в два раза больше времени, чем усреднение с помощью LockBits; при HighQualityBilinear он занимает немного больше времени, чем LockBits.  Поэтому, если у ваших пользователей нет политики безопасности, которая запрещает unsafe код, определенно не используйте мой метод.
обновление 2: С течением времени я теперь понимаю, почему такой подход вообще не работает. Даже самые качественные алгоритмы интерполяции включают только несколько соседних пикселей, поэтому есть предел тому, насколько изображение может быть раздавлено без потери информации. И сжатие изображения до одного пикселя намного превышает этот предел, независимо от того, какой алгоритм вы используете.
единственный способ сделать это-уменьшить изображение шагами (возможно, сокращая его наполовину каждый раз), пока вы не получите его до размера одного пикселя. Я не могу выразите в простых словах, какой пустой тратой времени было бы написать что-то подобное, поэтому я рад, что остановил себя, когда подумал об этом. :)
пожалуйста, никто больше не голосует за этот ответ - это может быть моя самая глупая идея.
такие вещи будут работать, но это может быть недостаточно быстро, чтобы быть полезным.
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);
}
