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

  • Двойное число равно 64, а одинарная точность (число с плавающей запятой) составляет 32 бита.
  • Двойник имеет большую мантиссу (целые биты действительного числа).
  • Любые неточности будут меньше в два раза.
17
задан Mats 30 June 2009 в 23:41
поделиться

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);

Осторожно: я не тестировал этот код ... (возможно, я срезал некоторые углы)

Этот код также предполагает 32-битное изображение. Для 24-битных изображений. Измените x * 4 на x * 3

21
ответ дан 22 October 2019 в 01:26
поделиться

Такие вещи будут работать, но они могут быть недостаточно быстрыми, чтобы быть настолько полезными.

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);
}
10
ответ дан 22 October 2019 в 01:26
поделиться

Вот гораздо более простой способ:

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 для копирования исходного Bitmap в 1-пиксельное Bitmap. Значения RGB этого 1 пикселя будут тогда представлять средние для всего оригинала. GetPixel работает относительно медленно, но только если вы используете его на большом Bitmap, пиксель за пикселем. Один раз вызвать его здесь не составляет большого труда.

Использование LockBits действительно быстро, но у некоторых пользователей Windows есть политики безопасности, предотвращающие выполнение «небезопасного» кода. Я упоминаю об этом, потому что этот факт недавно укусил меня.

Обновление : с InterpolationMode, установленным на HighQualityBicubic, этот метод занимает примерно вдвое больше времени, чем усреднение с LockBits; с HighQualityBilinear это занимает немного больше времени, чем LockBits. Поэтому, если у ваших пользователей нет политики безопасности, запрещающей небезопасный код, определенно не используйте мой метод.

Обновление 2: с течением времени я теперь понимаю, почему этот подход вообще не работает. Даже самые качественные алгоритмы интерполяции включают только несколько соседних пикселей, поэтому существует предел того, насколько изображение может быть сжато без потери информации. И сжатие изображения до одного пикселя выходит далеко за рамки этого предела, независимо от того, какой алгоритм вы используете.

Единственный способ сделать это - сжимать изображение пошагово (возможно, уменьшая его вдвое каждый раз), пока вы не получите это до размера одного пикселя. Я не могу выразить словами, что написание чего-то подобного было бы пустой тратой времени, поэтому я рад, что остановился, когда подумал об этом. :)

Пожалуйста, никто больше не голосует за этот ответ - возможно, это самая глупая моя идея.

11
ответ дан 22 October 2019 в 01:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: