Как рассчитать средние значения цвета rgb растрового изображения

StackOverflow https://stackoverflow.com/questions/1068373

Вопрос

В моем приложении на 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-битное изображение.Для 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 пиксель.Затем значения 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);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top