user9687180
user9687180

Reputation:

C#, Lockbits image processing with artefacts, changing pixel format

Im implementing my own function to binarize fingerprint images. In my method i try to work with LockBits for the first time.

Could you explain me, why i get a lot of artefacts on my images? Eample:

enter image description here

On the left picture i binarize image by Get/SetPixel and it is working pretty ok, but why i cant get same good result on the right image (a lot of red dots)? Did i forget or dont know about something?

private Bitmap Binarization(Bitmap tempBmp)
    {

        int threshold = otsuValue(tempBmp); //calculating threshold with Otsu method

        unsafe
        {
            BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, tempBmp.PixelFormat);
            byte* ptr = (byte*)bmpData.Scan0;


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

                }
            });

            tempBmp.UnlockBits(bmpData);
        }

        return tempBmp;
    }

The same history, when i want to cut a little of bytes from image, but problem looks a little more complex.

enter image description here

Why it dont even get into good "if" statemant?

private Bitmap Binarization(Bitmap tempBmp)
    {

        int threshold = otsuValue(tempBmp);

        unsafe
        {
            BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); 
            //Format8bpp, not pixel format from image
            byte* ptr = (byte*)bmpData.Scan0;


            int height = tempBmp.Height;
            int width = bmpData.Width; //i cut "* 4" here because of one channel image
            Parallel.For(0, height, y =>
            {
                byte* offset = ptr + (y * bmpData.Stride); //set row
                for (int x = 0; x < width; x++)
                {
                    //changing pixel values
                    offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;

                }
            });

            tempBmp.UnlockBits(bmpData);
        }

        return tempBmp;
    }

Thanks for any advices for improve my functions.

Upvotes: 1

Views: 628

Answers (2)

Milan Ćirić
Milan Ćirić

Reputation: 1

For this problem, I have my solution which looks like this:

public static bool Offset(Bitmap b_mapa, int threshold)
        {
            int threshold3 = threshold * 3;
            int red, green, blue;
            BitmapData bmData = b_mapa.LockBits(new Rectangle(0, 0, b_mapa.Width, b_mapa.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - b_mapa.Width * 3;
                for (int y = 0; y < b_mapa.Height; ++y)
                {
                    for (int x = 0; x < b_mapa.Width; ++x)
                    {
                        blue = p[0];
                        green = p[1];
                        red = p[2];
                        int v = ((int)(blue + green + red) > threshold3 ? (int)255 : (int)0);
                        p[0] = p[1] = p[2] = (byte)v;
                        p += 3;
                    }
                    p += nOffset;
                }
            }
            b_mapa.UnlockBits(bmData);
            return true;
        }

This function uses a shared resource called b_mapa,and our function should return a bool value depending result of processing. I dont use parallel functionality here, because, when we use LockBits, we work directly with memory, without using embeded functions which can be 1000 faster and no point parallelize it. If the filter are convolutional then the parallelization may be used for a little bit faster code. Buy the way, when I load images, I convert them into Bitmap, its simplier to do everything later and dont think about how much bits I have for color representation.

As you see, in bitmap array values goes BGR not RGB, just to let you know, because many make that mistake and messed up with colors.

Upvotes: 0

TaW
TaW

Reputation: 54433

1) The algorithm you use does not guarantee a b/w result.

You are testing the RGB channels separately, so if R > threshold but G or B < threshold it will be turned on but G and/or B not etc..

In fact the real question is why not a lot more artifacts come up. Must have to do with the source image.

Solution: Add all three channels and compare to threshold * 3; then set all to the same black or white value!

2) There is another issue: The code you copied from somewhere blindly assumes the image has 32 bpp. But whiile the image you posted is apng it still only has 24 bpp. Therefore your code will do all sorts of funny things..

Updated and corrected example:

        ..
        int pixWidth = tempBmp.PixelFormat == IMG.PixelFormat.Format24bppRgb ? 3 : 
                       tempBmp.PixelFormat == IMG.PixelFormat.Format32bppArgb ? 4 : 4;


        byte* ptr = (byte*)bmpData.Scan0;

        threshold3 = threshold * 3;
        int height = tempBmp.Height;
        int width = bmpData.Width * pixWidth;
        Parallel.For(0, height, y =>
        {
            byte* offset = ptr + (y * bmpData.Stride); //set row
            for (int x = 0; x < width; x = x + pixWidth)
            {
                //changing pixel value 
                int v = (offset[x] + offset[x + 1] + offset[x + 2]) > threshold3 ? 
                        Byte.MaxValue : Byte.MinValue;;
                offset[x] = (byte)v ;
                offset[x+1] = (byte)v;
                offset[x+2] = (byte)v;
                if (pixWidth == 4) offset[x+3] = 255;

            }
        });

Upvotes: 1

Related Questions