C# LockBits perfomance (int[,] to byte[])

2019-01-28 11:34发布

Graphics g;
using (var bmp = new Bitmap(_frame, _height, PixelFormat.Format24bppRgb))
{
    var data = bmp.LockBits(new Rectangle(0, 0, _frame, _height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    var bmpWidth = data.Stride;
    var bytes = bmpWidth * _height;
    var rgb = new byte[bytes];
    var ptr = data.Scan0;
    Marshal.Copy(ptr, rgb, 0, bytes);

    for (var i = 0; i < _frame; i++)
    {
        var i3 = (i << 1) + i;
        for (var j = 0; j < _height; j++)
        {
            var ij = j * bmpWidth + i3;
            var val = (byte)(_values[i, j]);
            rgb[ij] = val;
            rgb[ij + 1] = val;
            rgb[ij + 2] = val;
        }
    }

    Marshal.Copy(rgb, 0, ptr, bytes);
    bmp.UnlockBits(data);

    g = _box.CreateGraphics();
    g.InterpolationMode = InterpolationMode.NearestNeighbor;
    g.DrawImage(bmp, 0, 0, _box.Width, _box.Height);
}
g.Dispose();

I use this code to convert an array of RGB values ​​(grayscale) in the PictureBox, but it's slow. Please tell me my mistakes. At the moment, an array of 441 000 items handled for 35 ms. I need to handle an array of 4 million for the same time.

5条回答
Emotional °昔
2楼-- · 2019-01-28 12:09

Try this using unsafe code:

byte* rp0;
int* vp0;
fixed (byte* rp1 = rgb)
{
    rp0 = rp1;
    fixed (int* vp1 = _values)
    {
        vp0 = vp1;
        Parallel.For(0, _width, (i) =>
        {
            var val = (byte)vp0[i];
            rp0[i] = val;
            rp0[i + 1] = val;
            rp0[i + 2] = val;
        });
    }
}

Runs very fast for me

查看更多
甜甜的少女心
3楼-- · 2019-01-28 12:19

In addition to Guffa's excellent advice, I would suggest that you profile your code to see where it's taking the time. Be sure that when you're timing this, you are running in release mode without the debugger attached.

I wouldn't be surprised if the call to DrawImage is taking up most of the time. You're scaling the image there, which can be pretty expensive. How large is the box that you're drawing the image to?

Finally, although this won't affect performance, you should change your code to read:

using (Graphics g = _box.CreateGraphics())
{
    g.InterpolationMode = InterpolationMode.NearestNeighbor;
    g.DrawImage(bmp, 0, 0, _box.Width, _box.Height);
}

And get rid of the first and last lines in your example.

查看更多
放荡不羁爱自由
4楼-- · 2019-01-28 12:21

You can skip the first Array.Copy where you copy the data from the image to the array, as you will be overwriting all the data in the array anyway.

That will shave off something like 25% of time, but if you want it faster you will have to use an unsafe code block so that you can use pointers. That way you can get around the range checking when you access arrays, and you can write the data directly into the image data instead of copying it.

查看更多
Root(大扎)
5楼-- · 2019-01-28 12:23

My understanding is that multidimentional (square) arrays are pretty slow in .Net. You might try changing your _values array to be a single dimension array instead. Here is one reference, there are many more if you search: http://odetocode.com/articles/253.aspx

Array perf example.

using System;
using System.Diagnostics;

class Program
{
static void Main(string[] args)
{
    int w = 1000;
    int h = 1000;

    int c = 1000;

    TestL(w, h);
    TestM(w, h);


    var swl = Stopwatch.StartNew();
    for (int i = 0; i < c; i++)
    {
        TestL(w, h);
    }
    swl.Stop();

    var swm = Stopwatch.StartNew();
    for (int i = 0; i < c; i++)
    {
        TestM(w, h);
    }
    swm.Stop();

    Console.WriteLine(swl.Elapsed);
    Console.WriteLine(swm.Elapsed);
    Console.ReadLine();
}


static void TestL(int w, int h)
{
    byte[] b = new byte[w * h];
    int q = 0;
    for (int x = 0; x < w; x++)
        for (int y = 0; y < h; y++)
            b[q++] = 1;
}

static void TestM(int w, int h)
{
    byte[,] b = new byte[w, h];

    for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
            b[y, x] = 1;
}
}
查看更多
SAY GOODBYE
6楼-- · 2019-01-28 12:31

I totally agree with Guffa's answer. Using an unsafe code block will speed up things. To further improve performance, you could execute your for loop in parallel by using the Parallel class in the .Net framework. For large bitmaps this improves performance. Here is a small code sample:

using (Bitmap bmp = (Bitmap)Image.FromFile(@"mybitmap.bmp"))
{
  int width = bmp.Width;
  int height = bmp.Height;

  BitmapData bd = bmp.LockBits(new Rectangle(0, 0, width, height),
    System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

  byte* s0 = (byte*)bd.Scan0.ToPointer();
  int stride = bd.Stride;

  Parallel.For(0, height, (y1) =>
  {
    int posY = y1*stride;
    byte* cpp = s0 + posY;

    for (int x = 0; x < width; x++)
    {              
      // Set your pixel values here.
      cpp[0] = 255;
      cpp[1] = 255;
      cpp[2] = 255;
      cpp += 3;
    }
  });

  bmp.UnlockBits(bd);
}

To keep the example simple I've set the pixel values to a constant value. Note, to compile the example above you have to allow unsafe code.

Hope, this helps.

查看更多
登录 后发表回答