You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在WinForms中显示IDS Peak框架输出的16(12)位灰度图像

解决IDS Peak 16位灰度图在WinForms PictureBox显示的问题

我之前也踩过WinForms Bitmap不支持16bpp灰度格式的坑,针对你的需求,整理了两个可行方案,优先推荐无文件IO的第一种:

方案1:直接转换为WinForms支持的格式(无文件IO)

既然原始图像是12位单色打包格式,我们可以先解析出每个像素的12位值,再映射为WinForms Bitmap支持的8位灰度或24位RGB格式,全程不用碰磁盘文件。

步骤1:解析12位打包数据

12位像素的打包规则通常是每3字节存储2个像素

  • 第一个像素:字节0的全部8位 + 字节1的高4位
  • 第二个像素:字节1的低4位 + 字节2的全部8位

下面是解析并转换为8位灰度数据的方法:

private byte[] Convert12BitPackedTo8BitGray(IntPtr rawData, int width, int height)
{
    int totalPixels = width * height;
    byte[] result = new byte[totalPixels];
    int packedDataLength = (totalPixels * 3) / 2; // 2像素占3字节
    byte[] packedData = new byte[packedDataLength];
    Marshal.Copy(rawData, packedData, 0, packedDataLength);

    for (int i = 0; i < totalPixels; i += 2)
    {
        // 解析第一个12位像素,映射到8位(0-4095 → 0-255)
        int pixel1 = (packedData[i * 3 / 2] << 4) | (packedData[i * 3 / 2 + 1] >> 4);
        result[i] = (byte)(pixel1 / 16);

        if (i + 1 < totalPixels)
        {
            // 解析第二个12位像素
            int pixel2 = ((packedData[i * 3 / 2 + 1] & 0x0F) << 8) | packedData[i * 3 / 2 + 2];
            result[i + 1] = (byte)(pixel2 / 16);
        }
    }
    return result;
}

步骤2:创建可显示的Bitmap并加载到PictureBox

用解析后的8位数据创建Format8bppIndexed格式的Bitmap(WinForms完全支持),记得设置灰度调色板避免显示异常:

// 假设已获取width、height和iplImg.Data()的IntPtr
byte[] gray8BitData = Convert12BitPackedTo8BitGray(iplImg.Data(), width, height);

using (var bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
{
    // 设置灰度调色板
    ColorPalette palette = bitmap.Palette;
    for (int i = 0; i < 256; i++)
    {
        palette.Entries[i] = Color.FromArgb(i, i, i);
    }
    bitmap.Palette = palette;

    // 将数据写入Bitmap
    BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
    Marshal.Copy(gray8BitData, 0, bmpData.Scan0, gray8BitData.Length);
    bitmap.UnlockBits(bmpData);

    // 克隆后赋值给PictureBox,避免Dispose后图像失效
    pictureBox1.Image = (Bitmap)bitmap.Clone();
}

如果不想处理调色板,也可以转成24位RGB格式(每个通道用映射后的8位灰度值),代码会更简洁:

private byte[] Convert12BitPackedTo24BitRgb(IntPtr rawData, int width, int height)
{
    int totalPixels = width * height;
    byte[] result = new byte[totalPixels * 3];
    int packedDataLength = (totalPixels * 3) / 2;
    byte[] packedData = new byte[packedDataLength];
    Marshal.Copy(rawData, packedData, 0, packedDataLength);

    int rgbIndex = 0;
    for (int i = 0; i < totalPixels; i += 2)
    {
        int pixel1 = (packedData[i * 3 / 2] << 4) | (packedData[i * 3 / 2 + 1] >> 4);
        byte grayVal = (byte)(pixel1 / 16);
        result[rgbIndex++] = grayVal;
        result[rgbIndex++] = grayVal;
        result[rgbIndex++] = grayVal;

        if (i + 1 < totalPixels)
        {
            int pixel2 = ((packedData[i * 3 / 2 + 1] & 0x0F) << 8) | packedData[i * 3 / 2 + 2];
            grayVal = (byte)(pixel2 / 16);
            result[rgbIndex++] = grayVal;
            result[rgbIndex++] = grayVal;
            result[rgbIndex++] = grayVal;
        }
    }
    return result;
}

// 创建24位RGB Bitmap并显示
byte[] rgbData = Convert12BitPackedTo24BitRgb(iplImg.Data(), width, height);
using (var bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
    BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
    Marshal.Copy(rgbData, 0, bmpData.Scan0, rgbData.Length);
    bitmap.UnlockBits(bmpData);
    pictureBox1.Image = (Bitmap)bitmap.Clone();
}

方案2:加载16位灰度PNG到控件

如果想用框架的PNG保存功能,建议保存到内存流而非磁盘,避免文件IO开销,再直接从内存流加载图像:

using (MemoryStream ms = new MemoryStream())
{
    // 调用IDS Peak的PNG保存方法(替换为框架实际API)
    iplImg.Save(ms, ImageFormat.Png);
    ms.Seek(0, SeekOrigin.Begin);

    // 从内存流加载图像到PictureBox
    pictureBox1.Image = Image.FromStream(ms);
}

注:System.Drawing.Image加载标准16位灰度PNG时,会自动处理格式转换,不会触发之前的“参数无效”异常。


内容的提问来源于stack exchange,提问作者Jay Croghan

火山引擎 最新活动