Pergunta

Na minha C # (3,5) aplicativo eu preciso para obter os valores médios de cores para os canais vermelho, verde e azul de um bitmap. De um modo preferido sem utilizar uma biblioteca externa. isso pode ser feito? Se sim, como? Agradecemos antecipadamente.

Tentando fazer as coisas um pouco mais preciso: Cada pixel no bitmap tem um determinado valor de cor RGB. Eu gostaria de obter os valores médios RGB para todos os pixels da imagem.

Foi útil?

Solução

A maneira mais rápida é usando o código inseguro:

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

Cuidado: Eu não testei este código ... (I pode ter cortado alguns cantos)

Este código também asssumes uma imagem de 32 bits. Para imagens de 24 bits. Alterar o x * 4 para x * 3

Outras dicas

Aqui está uma maneira muito mais simples:

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.

Basicamente, você usa DrawImage para copiar o bitmap original em um Bitmap de 1 pixel. Os valores RGB de que 1 pixel, então, representam as médias para todo o original. GetPixel é relativamente lento, mas só quando você usá-lo em um grande mapa de bits, pixel por pixel. Chamando-o de uma vez aqui é nada demais.

Usando LockBits é realmente rápido, mas alguns usuários do Windows têm políticas de segurança que impedem a execução de código "inseguro". Digo isto porque este fato só me mordeu no traseiro recentemente.

Atualizar : com InterpolationMode conjunto para HighQualityBicubic, este método leva cerca de duas vezes mais que a média com LockBits; com HighQualityBilinear, leva apenas um pouco maior que LockBits. Assim a menos que seus usuários tenham uma política de segurança que proíbe código unsafe, definitivamente não usar o meu método.

Update 2: com o passar do tempo, agora percebo por que essa abordagem não funciona em todos. Mesmo os algoritmos de interpolação mais alta qualidade incorporam apenas alguns pixels vizinhos, por isso há um limite para o quanto uma imagem pode ser comprimida para baixo, sem perda de informações. E esmagando uma imagem até um pixel é bem além desse limite, não importa o algoritmo que você usar.

A única maneira de fazer isso seria a de reduzir a imagem em passos (talvez reduzindo-a metade de cada vez) até que você obtê-lo para baixo para o tamanho de um pixel. Eu não posso expressar em meras palavras o que é um total desperdício de tempo escrevendo algo como isto seria, então eu estou feliz que eu me parou quando eu pensei nisso. :)

Por favor, ninguém votação para esta resposta mais -. Talvez seja a minha ideia mais estúpida que nunca

Este tipo de coisa vai funcionar, mas pode não ser rápido o suficiente para ser tão útil.

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);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top