1

The b.GetPixel call in this method is pretty slow, is there a way to speed this method up with LockBits or something? But I don't know how to work with pointers to get the pixel value etc.

Background: I need the float[][] because of weighted randomizing I asked in this question.

public static float[][] ConvertToGrayScale(this Bitmap bm)
{
    var b = new Bitmap(bm);
    var data = new List<float[]>();
    for (var i = 0; i < b.Width; i++)
    {
        var row = new List<float>();
        for (int x = 0; x < b.Height; x++)
        {
            var oc = b.GetPixel(i, x);
            var grayScale = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
            row.Add(grayScale);
        }
        data.Add(row.ToArray());
    }
    return data.ToArray();
}

EDIT

As mentioned below from Paul Sasik I changed the code to this:

 public static GrayScaleResult ConvertToGrayScale(this Bitmap bm)
 {
     var result = new GrayScaleResult(bm);
     for (var x = 0; x < result.Width; x++)
     {
         for (int y = 0; y < result.Height; y++)
         {
             var oc = bm.GetPixel(x, y);
             // casting to int here - you can just use a 2d array of ints
             result.Data[x, y] = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
         }
     }
     return result;
 }

 public struct GrayScaleResult
 {
     public float[,] Data;
     public int Width;
     public int Height;
     public GrayScaleResult(Bitmap bm)
     {
         Width = bm.Width;
         Height = bm.Height;
         Data = new float[Width, Height];
     }
 }

And I checked the performance with a profiler before and after that optimization:

enter image description here

It's interesting that getHeight takes a lot of time so it seems not to be cached in the bitmap object? Because of that I stored Width and Height in the struct as well.

But the result can't be, can it? I mean the bottleneck is still GetPixel but why is that taking more time now? I didn't change anything else, the underlying bitmap is still the same.

EDIT2

Ok, found it: The problem was surprisingly the removal of the new Bitmap so I added it again:

 public static GrayScaleResult ConvertToGrayScale(this Bitmap b)
 {
     var bm = new Bitmap(b);
     var result = new GrayScaleResult(bm);
     ...

And now the Code is optimized:

enter image description here

Community
  • 1
  • 1
Marc
  • 6,749
  • 9
  • 47
  • 78

2 Answers2

2

Before trying unsafe code there are a number of optimizations you can make to your current code:

public static float[,] ConvertToGrayScale2(this Bitmap bm)
{
    var data = new float[bm.Width, bm.Height];
    for (var i = 0; i < bm.Width; i++)
    {
        for (int x = 0; x < bm.Height; x++)
        {
            var oc = bm.GetPixel(i, x);
                            // casting to int here - you can just use a 2d array of ints
            data[i, x] = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
        }
    }
    return data;
}

Optimizations:

  • No need to create a new Bitmap to work from. Reference the one you pass in
  • Use a rectangular array of floats rather than generic Lists
  • This gets rid many extra assignment and some collection creation/management overhead
  • Please take this with a grain of salt, not using an IDE
Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
  • 1
    You could optimize that code even further by flattening that 2D-array into 1D array. Also if it's possible to get all the pixel-data from BitMap ( haven't used that personally ) with one call into an array, it should be pretty easy job to parallize that. –  Mar 23 '12 at 19:26
  • @JaakkoLipsanen: +1 Those are both good points. The 2nd is achievable with LockBits: http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx – Paul Sasik Mar 23 '12 at 20:22
1

Instead of using intermediary lists, why not just create the array up-front? You know the dimensions ahead of time, so you can save creating a list for every row of your bitmap. That's the first obvious win I could spot.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393