2
votes

I made an Automatic image thresholding function, and wanna to save it as a bitmap file.

However, when I use the Bitmap.Save function of C# GDI+, although I set the ImageFormat as BMP, it always as the RGB color image file but not bitmap image file.

I must save it as the bitmap image file for the printer only can read the bitmap image file.

Maybe you will ask me what the bitmap image file is. I am not an expert of image processing and sorry about that can hardly explain clearly. But I can quote an example: in Photoshop, there are several color mode, such as RGB mode/CMYK mode/Index mode/Grayscale mode/Bitmap mode, I want to save the image as the Bitmap mode in C#.

Here is what Adobe explain about the Bitmap mode in their website:

Bitmap mode uses one of two color values (black or white) to represent the pixels in an image. Images in Bitmap mode are called bitmapped 1‑bit images because they have a bit depth of 1.

I googled but found nothing about this. How can I do it in C#? Thank you.

Here is my code:

Thread T = new Thread(() => {
    Bitmap processedBitmap = new Bitmap(@"G:\\0001.jpg");

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

    int bytesPerPixel = Bitmap.GetPixelFormatSize(processedBitmap.PixelFormat) / 8;
    int byteCount = bitmapData.Stride * processedBitmap.Height;
    byte[] pixels = new byte[byteCount];
    IntPtr ptrFirstPixel = bitmapData.Scan0;
    Marshal.Copy(ptrFirstPixel, pixels, 0, pixels.Length);
    int heightInPixels = bitmapData.Height;
    int widthInBytes = bitmapData.Width * bytesPerPixel;

    for (int y = 0; y < heightInPixels; y++)
    {
        int currentLine = y * bitmapData.Stride;
        for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
        {
            int oldBlue = pixels[currentLine + x];
            int oldGreen = pixels[currentLine + x + 1];
            int oldRed = pixels[currentLine + x + 2];

            double averageColor = (oldBlue + oldGreen + oldRed) / 3;

            int NewC;
            if (averageColor > 200)
            {
                NewC = 255;
            }
            else
            {
                NewC = 0;
            }
            // calculate new pixel value
            pixels[currentLine + x] = (byte)NewC;
            pixels[currentLine + x + 1] = (byte)NewC;
            pixels[currentLine + x + 2] = (byte)NewC;
        }
    }

    // copy modified bytes back
    Marshal.Copy(pixels, 0, ptrFirstPixel, pixels.Length);
    processedBitmap.UnlockBits(bitmapData);

    processedBitmap.Save("G:\\aaa.bmp", ImageFormat.Bmp);
    MessageBox.Show("Sucess!");
});
T.Start();
3
Can you explain exactly what it is Bitmap.Save is doing and what you expect, and how you proved this to your selfTheGeneral
Please share your code so we can reproduce your issue. The save function of the Bitmap class as many overloads.Mark Baijens
I think we're going to need to see your code in order to help. Also, I don't understand what you mean when you say "...it always as the RGB color image file but not bitmap image file". A bitmap being RGB is not unusual.Nanhydrin
@MarkBaijens I am sorry about that I haven't explained clearly and I just edited the question for more detail. What's more, the Bitmap.Save not made by me but using the GDI+ as the URL shown.102425074
@TheGeneral Maybe I can explain clearly for I am not an expert of image processing. For example in Photoshop, there are several color mode, such as RGB mode/CMYK mode/Index mode/Grayscale mode/Bitmap mode, I want to save it as the Bitmap mode.102425074

3 Answers

2
votes

I believe the OP is referring to the last type of image in this adobe link Bitmap is merely a container for data, the format of the data that you are storing is defined by the PixelFormat setting. As can be seen "Adobe" Bitmap mode is a 2 color format mode and corresponds to PixelFormat.Format1bppIndexed in C# Bitmap.

You have a couple of constructors for Bitmaps which have the PixelFormat as a parameter.

1.

public Bitmap (int width, int height, System.Drawing.Imaging.PixelFormat format);

2.

public Bitmap (int width, int height, int stride, System.Drawing.Imaging.PixelFormat format, IntPtr scan0);
2
votes

With your source image you have a 24 bit image. When you do your colour averaging, you're writing back to the image buffer with the following code:

pixels[currentLine + x] = (byte)NewC;
pixels[currentLine + x + 1] = (byte)NewC;
pixels[currentLine + x + 2] = (byte)NewC;

You're writing back 24 bits again.
So for example if your original values for RGB were (202, 203, 249), then NewC would be 218, and then you threshold it back to 255, so you write back (255,255,255) which is still an RGB value, it's just for white. Then you save that image using

processedBitmap.Save("G:\\aaa.bmp", ImageFormat.Bmp);

The ImageFormat class just sets the type of image, like jpeg, png, etc. And as you've discovered, you still have a 24 bit image being output.

So what you want is to save the image as a pure 1 bit per pixel black and white image. To do this you need to specify the PixelFormat of the image you're saving, and specifically you want the PixelFormat Format1bppIndexed.

If you instead change the relevant bit of your code to:

...
Marshal.Copy(pixels, 0, ptrFirstPixel, pixels.Length);
processedBitmap.UnlockBits(bitmapData);

Bitmap clone = processedBitmap.Clone(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), PixelFormat.Format1bppIndexed);
clone.Save("G:\\aaa.bmp", ImageFormat.Bmp);

MessageBox.Show("Success!");

Now your output clone will be a 1bpp image.

However, you can simplify your code even more, because this clone function can actually do all the work for you, and you can reduce your code to just the following.

Bitmap processedBitmap = new Bitmap(@"G:\0001.jpg");
Bitmap clone = processedBitmap.Clone(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), PixelFormat.Format1bppIndexed);
clone.Save("G:\\aaa.bmp", ImageFormat.Bmp);
MessageBox.Show("Success!");

Be aware though that the output is slightly different. Here are some test samples of the output.

This is my input image:

Output image with your thresholding code:

And output image using just the clone method:

0
votes

To save a BMP object to a file all you have to do it this:

bmp.Save("c:\\Path\\To\\File\\image.bmp, ImageFormat.Bmp);

Are you doing anything else?