2

I am capturing data from some camera (array of RAW data).

Then I'm mapping this data to RGB values according to color palette.

I need to map it as fast as possible, so I use BitmapDdata and edit pixels in unsafe piece of code using pointers.

public void dataAcquired(int[] data)
{
    Bitmap bmp = new Bitmap(width, height); 
    BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

    for (int i = 0; i < data.Length; i++)
    {
        int x = i % bmp.Width;
        int y = i / bmp.Width;
        Rgb rgb = mapColors[data[i]];

        unsafe
        {
            byte* ptr = (byte*)data.Scan0;
            ptr[(x * 3) + y * data.Stride] = rgb.b;
            ptr[(x * 3) + y * data.Stride + 1] = rgb.g;
            ptr[(x * 3) + y * data.Stride + 2] = rgb.r;
        }
    }
    bmp.UnlockBits(data);
}

And I'm doing this for every incoming frame. It works fine, but it still takes something like 30ms for each frame for 320x240 pixels.

Is it possible to make it even more faster? Maybe I couldlock/unlock data in memory only once, but I'm not sure about this.

Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
Majak
  • 1,543
  • 1
  • 14
  • 28

2 Answers2

6

Instead of calculating x and y for each pixel, you could make them loop counters, like this:

for( y = 0;  y < bmp.Height;  y++ )
    for( x = 0;  x < bmp.Width;  x++ )

Better yet, ditch x and y altogether and just keep incrementing the ptr pointer instead of recalculating an offset from the ptr pointer three times per pixel.

Try this (warning: I have not checked it.)

public void dataAcquired()
{
    Bitmap bmp = new Bitmap(width, height); 
    BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    unsafe
    {
        int i = 0;
        byte* ptr = (byte*)data.Scan0;
        for( int y = 0;  y < bmp.Height;  y++ )
        {
            byte* ptr2 = ptr;
            for( int x = 0;  x < bmp.Width;  x++ )
            {
                Rgb rgb = mapColors[data[i++]];
                *(ptr2++) = rgb.b;
                *(ptr2++) = rgb.g;
                *(ptr2++) = rgb.r;
            }
            ptr += data.Stride;
        }
    }
    bmp.UnlockBits(data);
}
Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • Thanks! Works perfect, especially two loops reduced computation time to half. Two pointers solution has no significant effect in this case, but it's more elegant at least. – Majak Feb 04 '15 at 14:30
  • 1
    @Majak great! Glad to be of help. I think that parallelization would *not* have helped much, because this operation is extremely memory bound, and no matter how many cores your CPU has, it still only has one address bus and one data bus. But if you tried parallelization, please tell us what you observed. – Mike Nakis Feb 04 '15 at 14:32
  • 1
    I left idea of parallelization because of difficult synchronization, which would be overkill here for me as I'm not so experienced in that. But, I made one more modification, I'm using now already initialized "width, height" values instead of getting it from Bitmap class, and at this moment, I'm getting 0 ms time! I thought that this time is measured mistakely, but my camera is now working in 60 fps mode, so the time consumption seems to be really near to zero. – Majak Feb 05 '15 at 09:45
2

Try run code in parallel: change

for (int i = 0; i < data.Length; i++) {
  ...
}

into

  Parallel.For(0, data.Length, (i) => {
    int x = i % bmp.Width;
    int y = i / bmp.Width;
    Rgb rgb = mapColors[data[i]];

    unsafe {
      byte* ptr = (byte*) data.Scan0;
      ptr[(x * 3) + y * data.Stride] = rgb.b;
      ptr[(x * 3) + y * data.Stride + 1] = rgb.g;
      ptr[(x * 3) + y * data.Stride + 2] = rgb.r;
    }
  });
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215