Chiramisu
Chiramisu

Reputation: 4726

Convert 12-bit Monochrome Image to 8-bit Grayscale

I have an image sensor board for embedded development for which I need to capture a stream of images and output them in 8-bit monochrome / grayscale format. The imager output is 12-bit monochrome (which takes 2 bytes per pixel).

In the code, I have an IntPtr to a memory buffer that has the 12-bit image data, from which I have to extract and convert that data down to an 8-bit image. This is represented in memory something like this (with a bright light activating the pixels):

enter image description here

As you can see, every second byte contains the LSB that I want to discard, thereby keeping only the odd-numbered bytes (to put it another way). The best solution I can conceptualize is to iterate through the memory, but that's the rub. I can't get that to work. What I need help with is an algorithm in C# to do this.

Here's a sample image that represents a direct creation of a Bitmap object from the IntPtr as follows:

bitmap = new Bitmap(imageWidth, imageHeight, imageWidth, PixelFormat.Format8bppIndexed, pImage);

Click to expand in order to view correct representation

// Failed Attempt #1
unsafe
{
    IntPtr pImage;  // pointer to buffer containing 12-bit image data from imager
    int i = 0, imageSize = (imageWidth * imageHeight * 2);  // two bytes per pixel
    byte[] imageData = new byte[imageSize];
    do
    {
        // Should I bitwise shift?
        imageData[i] = (byte)(pImage + i) << 8;  // Doesn't compile, need help here!
    } while (i++ < imageSize);
}

// Failed Attempt #2
IntPtr pImage;  // pointer to buffer containing 12-bit image data from imager
imageSize = imageWidth * imageHeight;
byte[] imageData = new byte[imageSize];
Marshal.Copy(pImage, imageData, 0, imageSize);
// I tried with and without this loop. Neither gives me images.
for (int i = 0; i < imageData.Length; i++)
{
    if (0 == i % 2) imageData[i / 2] = imageData[i];
}
Bitmap bitmap;
using (var ms = new MemoryStream(imageData))
{
    bitmap = new Bitmap(ms);
}
// This also introduced a memory leak somewhere.

Alternatively, if there's a way to do this with a Bitmap, byte[], MemoryStream, etc. that works, I'm all ears, but everything I've tried has failed.

Upvotes: 1

Views: 1768

Answers (1)

Chiramisu
Chiramisu

Reputation: 4726

Here is the algorithm that my coworkers helped formulate. It creates two new (unmanaged) pointers; one 8-bits wide and the other 16-bits.

By stepping through one word at a time and shifting off the last 4 bits of the source, we get a new 8-bit image with only the MSBs. Each buffer has the same number of words, but since the words are different sizes, they progress at different rates as we iterate over them.

unsafe
{
    byte* p_bytebuffer = (byte*)pImage;
    short* p_shortbuffer = (short*)pImage;

    for (int i = 0; i < imageWidth * imageHeight; i++)
    {
        *p_bytebuffer++ = (byte)(*p_shortbuffer++ >> 4);
    }
}

In terms of performance, this appears to be very fast with no perceivable difference in framerate.

Special thanks to @Herohtar for spending a substantial amount of time in chat with me attempting to help me solve this.

Upvotes: 1

Related Questions