Faster Imaging Processing Width and Level in C#

i got this block of code that adjust the width and level of a grayscale image.  
The problem is that it is extremely slow, can any expert give me any tips to make it faster?
I'm trying to make the process in real time.

Backimage is where I store the original image in the buffer.

int range1 = ImageInfoObj.MaxPixel;
            int range0 = ImageInfoObj.MinPixel;
            int[] pixels = ImageInfoObj.GetPixels();
            if (ImageInfoObj != null)
            {
                ImgWidth += distX * (range1 - range0 + 1) / ImageDrwPanel.Width;
                ImgLevel -= distY * (range1 - range0 + 1) / ImageDrwPanel.Height;
                if (ImgWidth <= 0) ImgWidth = 0;
                if (ImgWidth > range1 - range0 + 1) ImgWidth = range1 - range0 + 1;
                if (ImgLevel < range0) ImgLevel = range0;
                if (ImgLevel > range1) ImgLevel = range1;

            }
            System.Drawing.Bitmap bm = new System.Drawing.Bitmap(Backimage.Width, Backimage.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
            System.Drawing.Imaging.BitmapData bmData = bm.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;
            int index = -1;
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - bm.Width * 4;
                for (int y = 0; y < bm.Height; ++y)
                {
                    for (int x = 0; x < bm.Width; ++x)
                    {
                        int color = pixels[++index];
                        if (color < (ImgLevel - ImgWidth / 2))
                        {
                            color = 0;
                        }
                        else if (color > (ImgLevel + ImgWidth / 2))
                        {
                            color = 255;
                        }
                        else
                        {
                            if (ImgWidth == 0)
                            {
                                color = 0;
                            }
                            else
                            {
                                color = 255 * (color - ImgLevel + ImgWidth / 2) / ImgWidth;
                            }
                        }
                        p[2] = (byte)color;
                        p[1] = (byte)color;
                        p[0] = (byte)color;
                        p[3] = (byte)255;

                        p += 4;
                    }
                    p += nOffset;
                }
            }
            bm.UnlockBits(bmData);
            bm.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipX);
            Backimage = bm;
raw_enhaAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

_TAD_Commented:

A few things....

1) How much of your CPU is being used for this process?   If you aren't using 100% of your CPU, you can multithread this piece and have 1 thread convert the top half of the image and the second thread convert the bottom half (or have more threads).

2) Division is an expensive process.  I'm not sure how .Net optimizes its code, but on the cpu level multiplying by 0.5 is 20 times faster than dividing by 2.  .Net may do this conversion for you, but it's worth the small change just to test it out.

3) A switch statement is, in most cases, most efficient.  If/else blocks can approach the efficiency of a switch statement, but will never be faster than a switch.

4) I also see that you are doing the same calculation over and over and over again within the loop.  By the time you get into the loop, the ImgWidth is constant - don't divide by 2 for each itteration.  Create a new variable halfImgWidth and work with that value.
0
AlexFMCommented:
1. Replace 32bppPArgb bitmap with true graylevel bitmap which has one byte per pixel.

2. int color = pixels[++index];
This requires calculating of pixel offset. Replace this with unsafe pointer which is incremented in every iteration.

3. Make some math optimizations. For example:

if (color < (ImgLevel - ImgWidth / 2))
ImgLevel - ImgWidth / 2 can be calculated once before loop. However, I beleive that JIT compiler optimizes this.

if (ImgWidth == 0)
Is this test really necessary?
0
AlexFMCommented:
I think also that bm.RotateFlip takes significant time - test this with StopWatch class. Maybe you need to change algorithm by such way that it fills bitmap already with rotation.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ennixoCommented:
THE thing to know before everything else is that Bitmap.Width and Bitmap.Height are very slow when you access it
store these values in additional variables like this :

int width = bm.Width;
int height = bm.Height;

and the use width and height instead of bm.Width and bm.Height, the result will be a lot faster !

and as the other said, pre-compute every value before the loop so that it is not computed at each iteration.
even a division by 2 has to be computed before the loop.

another thing, divisions by 2, 4, 8 , 16, etc. on integers can be avoided with >> 1, >> 2, >> 3, >> 4 etc. if you need to do it inside the loop
same for multiplications with << 1, << 2 etc.

yet another thing, you may create a lookup table.
as the value of color is between 0 and 255 included, precompute the values in a table byte[256] and then, inside your loop just find the value of the table using color as an index.

Last thing but you are good on this point, remember that the processor have cache and they store a part of the image table when you iterate.
it is faster to do for y = etc. and inside for x = etc. always do this when you can.

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.