كيفية حساب متوسط ​​قيم ألوان 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 بت.غير ال ×*4 ل ×*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 الخاصة بالبكسل الواحد متوسطات النسخة الأصلية بأكملها.يعد 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