将 12 位单色图像转换为 8 位灰度

Convert 12-bit Monochrome Image to 8-bit Grayscale

我有一个用于嵌入式开发的图像传感器板,我需要为其捕获图像流并以 8 位单色/灰度格式输出它们。成像器输出为 12 位单色(每个像素占用 2 个字节)。

在代码中,我有一个 IntPtr 到具有 12 位图像数据的内存缓冲区,我必须从中提取该数据并将其转换为 8 位图像。这在内存中表示如下(用亮光激活像素):

如您所见,每隔一个字节包含我要丢弃的 LSB,从而只保留奇数字节(换句话说)。我可以概念化的最佳解决方案是遍历内存,但这就是问题所在。我无法让它工作。我需要帮助的是 C# 中的算法来执行此操作。

这是一个示例图像,表示从 IntPtr 直接创建 Bitmap 对象,如下所示:

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

// 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.

或者,如果有一种方法可以使用 Bitmapbyte[]MemoryStream 等来做到这一点,那么我洗耳恭听,但我已经尝试失败。

这是我的同事帮助制定的算法。它创建了两个新的(非托管)指针;一个 8 位宽,另一个 16 位。

通过一次单步执行一个字并移出源的最后 4 位,我们得到一个只有 MSB 的新 8 位图像。每个缓冲区都有相同数量的单词,但由于单词的大小不同,因此在我们迭代它们时它们以不同的速度前进。

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);
    }
}

就性能而言,这似乎非常快,帧率没有明显差异。

特别感谢 @Herohtar spending a substantial amount of time in chat with me 试图帮助我解决这个问题。