You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中支持16位灰度等多像素格式的内存图像存储类查询

我需要对内存缓冲区中的图像进行矩形区域黑色遮罩处理,因此最初在自研API中直接复用Bitmap类搭配ImageFormat.MemoryBmp实现该功能,本地环境运行效果良好,实现代码如下:

public static void MaskBitmap(Bitmap input, Rectangle maskRegion)
{
    var bytesPerPixel = Image.GetPixelFormatSize(input.PixelFormat) / 8;
    var row = new byte[maskRegion.Width * bytesPerPixel];

    var maskData = input.LockBits(maskRegion, ImageLockMode.WriteOnly, input.PixelFormat);
    for (var i = 0; i < maskData.Height; ++i)
    {
        Marshal.Copy(row, 0, maskData.Scan0 + (i * maskData.Stride), row.Length);
    }
    input.UnlockBits(maskData);
}

但部署到生产环境后发现,执行以下代码会抛出NotImplementedException异常:

var image16 = new Bitmap(512, 512, PixelFormat.Format16bppGrayScale);

最终溯源到问题出自mono的libgdiplus未实现对应逻辑。

因此想咨询:C#中是否有现成的类可以复用,用于存储以下pixelformat类型的图像:

  • PixelFormat.Format8bppIndexed
  • PixelFormat.Format16bppGrayScale
  • PixelFormat.Format24bppRgb

我已知晓GDI+不支持16位图像的保存/展示,我仅需要具备图像式访问能力的内存结构即可。

参考信息:我曾尝试过如下hack方案:

var image = new Bitmap(512,512,PixelFormat.Format24bppRgb);
image.Flags = ImageFlags.ColorSpaceGray;

但Flags为只读属性,该方案不可行。


可行解决方案

方案1:使用跨平台图像处理库(低改造成本)

直接使用社区成熟的纯托管图像处理库即可,无需依赖libgdiplus,原生支持你需要的全部像素格式,API逻辑和GDI+接近,原有遮罩逻辑几乎不需要大改:

  • 兼容8位索引、16位灰度、24位RGB等常见像素格式
  • 提供直接操作内存像素块的能力,性能和GDI+原生实现相当
  • 无原生组件依赖,跨Windows/Linux/macOS运行无兼容问题

遮罩逻辑示例:

// 泛型支持所有像素格式,L8对应8位灰度、L16对应16位灰度、Rgb24对应24位RGB
public static void MaskImage<TPixel>(Image<TPixel> input, Rectangle maskRegion) where TPixel : unmanaged, IPixel<TPixel>
{
    input.ProcessPixelRows(accessor =>
    {
        for (int y = maskRegion.Y; y < maskRegion.Bottom; y++)
        {
            Span<TPixel> maskRow = accessor.GetRowSpan(y).Slice(maskRegion.X, maskRegion.Width);
            maskRow.Clear(); // 直接填充对应像素格式的黑色值
        }
    });
}

方案2:自研轻量内存图像结构(零第三方依赖)

如果不想引入外部依赖,可以自行封装极简的内存图像类,完全规避GDI+的兼容问题:

public sealed class MemoryBitmap : IDisposable
{
    public int Width { get; }
    public int Height { get; }
    public PixelFormat Format { get; }
    public int BytesPerPixel { get; }
    public int Stride { get; }
    public IntPtr DataPtr { get; }
    private bool _isDisposed;

    public MemoryBitmap(int width, int height, PixelFormat format)
    {
        Width = width;
        Height = height;
        Format = format;
        BytesPerPixel = Image.GetPixelFormatSize(format) / 8;
        // 和GDI+规则一致,行宽对齐到4字节
        Stride = (width * BytesPerPixel + 3) & ~3;
        DataPtr = Marshal.AllocHGlobal(Stride * height);
        // 初始化为全黑
        unsafe
        {
            new Span<byte>((void*)DataPtr, Stride * height).Clear();
        }
    }

    // 直接获取指定行的内存切片,方便读写操作
    public Span<byte> GetRow(int rowIndex)
    {
        if (_isDisposed) throw new ObjectDisposedException(nameof(MemoryBitmap));
        if (rowIndex < 0 || rowIndex >= Height) throw new ArgumentOutOfRangeException(nameof(rowIndex));
        unsafe
        {
            return new Span<byte>((void*)(DataPtr + rowIndex * Stride), Stride);
        }
    }

    public void Dispose()
    {
        if (_isDisposed) return;
        Marshal.FreeHGlobal(DataPtr);
        _isDisposed = true;
    }
}

该实现完全不依赖GDI+的Bitmap创建逻辑,不会触发libgdiplus的未实现异常,你原有遮罩逻辑只需要稍作调整即可直接复用。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 03:24:02