0

I have a problem. I need to perform this function with lockbits. Please I need help.

 public void xPix(Bitmap bmp, int n, Color cx, Color nx)
    {
        try
        {

            for (int y = 0; y < bmp.Height; y++)
            {
                   for (int x = 0; x < bmp.Width; x += (n * 2))
                  {
                       cx = bmp.GetPixel(x, y);
                       if (x + n <= bmp.Width - 1) nx = bmp.GetPixel(x + n, y);
                       bmp.SetPixel(x, y, nx);
                       if (x + n <= bmp.Width - 1) bmp.SetPixel(x + n, y, cx);
                  }
            }
        }
        catch { }
    }
DDK
  • 136
  • 1
  • 1
  • 11

2 Answers2

1

There were lots of things that didn't make sense to me about your code. I fixed the pieces that were preventing an image from appearing and here is the result. I will explain my changes after the code.

  public void xPix(Bitmap bmp, int n, Color cx, Color nx)
  {
     var img = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
     byte[] bmpBytes = new byte[Math.Abs(img.Stride) * img.Height];
     System.Runtime.InteropServices.Marshal.Copy(img.Scan0, bmpBytes, 0, bmpBytes.Length);

     for (int y = 0; y < img.Height; y++)
     {
        for (int x = 0; x < img.Width; x+=n*2)
        {
           cx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
           if (x + n <= img.Width - 1) nx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
           BitConverter.GetBytes(nx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + x * 4);
           if (x + n <= img.Width - 1) BitConverter.GetBytes(cx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + (x + n) * 4);
        }
     }
     System.Runtime.InteropServices.Marshal.Copy(bmpBytes, 0, img.Scan0, bmpBytes.Length);
     bmp.UnlockBits(img);
  }

  protected override void OnClick(EventArgs e)
  {
     base.OnClick(e);
     Bitmap bmp = new Bitmap(@"C:\Users\bluem\Downloads\Default.png");
     for (int i = 0; i < bmp.Width; i++)
     {
        xPix(bmp, new Random().Next(20) + 1, System.Drawing.Color.White, System.Drawing.Color.Green);
     }

     Canvas.Image = bmp;
  }
  1. There's no such class as LockBitmap so I replaced it with the result of a call to Bitmap.LockBits directly.
  2. The result of LockBits does not include functions for GetPixel and SetPixel, so I did what one normally does with the result of LockBits (see https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netframework-4.7.2) and copied the data into a byte array instead.
  3. When accessing the byte data directly, some math must be done to convert the x and y coordinates into a 1-dimensional coordinate within the array of bytes, which I did.
  4. When accessing the byte data directly under the System.Drawing.Imaging.PixelFormat.Format32bppArgb pixel format, multiple bytes must be accessed to convert between byte data and a pixel color, which I did with BitConverter.GetBytes, BitConverter.ToInt32, Color.FromArgb and Color.ToArgb.
  5. I don't think it's a good idea to be changing the Image in the middle of painting it. You should either be drawing the image directly during the Paint event, or changing the image outside the Paint event and allowing the system to draw it. So I used the OnClick of my form to trigger the function instead.
  6. The first random number I got was 0, so I had to add 1 to avoid an endless loop.
  7. The cx and nx parameters never seem to be used as inputs, so I put arbitrary color values in for them. Your x and y variables were not defined/declared anywhere.
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • thanks it worked but still slow how can i make it faster i added parallel.For but still not fast enough – Ayman Benyahia Jan 27 '19 at 16:06
  • @AymanBenyahia Why do you have a loop for `i` from `0` to `bmp.Width`? You are already looping through the width of the image with x. Can you somehow combine these loops? It would be much faster. – BlueMonkMN Jan 28 '19 at 14:11
0

If you want faster on-image-action, you can use Marshall.Copy method with Parallel.For

Why dont use GetPixel method? Because every time you call it, your ALL image is loaded to memory. GetPixel get one pixel, and UNLOAD all image. And in every iteration, ALL image is loaded to memory (for example, if u r working on 500x500 pix image, GetPixel will load 500x500 times whole pixels to memory). When you work on images with C# (CV stuff), work on raw bytes from memory.

I will show how to use with Lockbits in Binarization because its easy to explain.

 int pixelBPP = Image.GetPixelFormatSize(resultBmp.PixelFormat) / 8;

        unsafe
        {
            BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height), ImageLockMode.ReadWrite, resultBmp.PixelFormat);

            byte* ptr = (byte*)bmpData.Scan0; //addres of first line

            int height = resultBmp.Height;
            int width = resultBmp.Width * pixelBPP;

            Parallel.For(0, height, y =>
            {
                byte* offset = ptr + (y * bmpData.Stride); //set row
                for(int x = 0; x < width; x = x + pixelBPP)
                {
                    byte value = (offset[x] + offset[x + 1] + offset[x + 2]) / 3 > threshold ? Byte.MaxValue : Byte.MinValue;
                    offset[x] = value;
                    offset[x + 1] = value;
                    offset[x + 2] = value;

                    if (pixelBPP == 4)
                    {
                        offset[x + 3] = 255;
                    }
                }
            });

            resultBmp.UnlockBits(bmpData);
        }

Now, example with Marshall.copy:

BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height), 
                                                ImageLockMode.ReadWrite, 
                                                resultBmp.PixelFormat
                                                );


        int bytes = bmpData.Stride * resultBmp.Height;
        byte[] pixels = new byte[bytes];

        Marshal.Copy(bmpData.Scan0, pixels, 0, bytes); //loading bytes to memory
        int height = resultBmp.Height;
        int width  = resultBmp.Width;

        Parallel.For(0, height - 1, y => //seting 2s and 3s
        {
            int offset = y * stride; //row
            for (int x = 0; x < width - 1; x++)
            {
                int positionOfPixel = x + offset + pixelFormat; //remember about pixel format!
                    //do what you want with pixel
                }
            }
        });

       Marshal.Copy(pixels, 0, bmpData.Scan0, bytes); //copying bytes to bitmap
        resultBmp.UnlockBits(bmpData);

Remember, when you warking with RAW bytes very important is to remember about PixelFormat. If you work on RGBA image, you need to set up every channel. (for example offset + x + pixelFormat). I showed it in Binarization example, how to deak with RGBA image with raw data. If lockbits are not fast enough, use Marshall.Copy

michasaucer
  • 4,562
  • 9
  • 40
  • 91