将位图图像的单个通道保存到文件

时间:2019-06-10 17:51:59

标签: c# .net bitmap system.drawing color-channel

我正在使用此代码将位图另存为二进制数据。

Bitmap bmp = new Bitmap(screenWidth, position);            
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);    


Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);

IntPtr ptr = bmpData.Scan0;

int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];

System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);            
File.WriteAllBytes(filename, bmp);    
g.Dispose();

由于我只需要第一个通道的值,是否可以从位图中检索到它?性能至关重要。

1 个答案:

答案 0 :(得分:0)

您快到了,但是缺少一些关键细节:

  • 不是使用bmp.PixelFormat,而是将BitmapData对象的像素格式强制为PixelFormat.Format32BppArgb,那么您将100%确定将获得哪种结构,并处于32位模式,步幅将始终与可预测的width * 4完全匹配。如果不这样做,如果读取的图像恰好是调色板或某种16bpp格式,其中每个像素不能分为简单的颜色分量字节,则可能会得到意外的结果。
  • 遍历数据并提取通道。字母“ ARGB”的顺序指的是十六进制值0xAARRGGBB(例如0xFF428ED0),它是一个低端Uint32值,这意味着颜色分量字节的实际顺序是相反的: { BB, GG, RR, AA }

因此,要提取您的频道:

// Channels are: B=0, G=1, R=2, A=3
Int32 channel = 1 // for this example, extract the Green channel.

Int32 width;
Int32 height;
Byte[] rgbaValues;
using (Bitmap bmp = new Bitmap(screenWidth, position))
using (Graphics g = Graphics.FromImage(bmp))
{
    width = bmp.Width
    height = bmp.Height;
    g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);    
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    Int32 bytes = bmpData.Stride * bmp.Height;
    rgbaValues = new byte[bytes];
    Marshal.Copy(bmpData.Scan0, rgbValues, 0, bytes);
    bmp.UnlockBits(bmpData);
    g.Dispose();
}
Byte[] channelValues = new byte[width * height];

Int32 lineStart = 0;
Int32 lineStartChannel = 0;
for (Int32 y = 0; y < height; ++y)
{
    Int32 offset = lineStart;
    Int32 offsetChannel = lineStartChannel;
    for (Int32 x = 0; x < width; ++x)
    {
        // For reference:
        //Byte blue  = rgbaValues[offset + 0];
        //Byte green = rgbaValues[offset + 1];
        //Byte red   = rgbaValues[offset + 2];
        //Byte alpha = rgbaValues[offset + 3];
        channelValues[offsetChannel] = rgbaValues[offset + channel];
        offset += 4;
        offsetChannel++;
    }
    lineStart += stride;
    lineStartChannel += width;
}
File.WriteAllBytes(filename, channelValues);

这只是将数据保存为字节数组。如果要将其写为图像,最简单的方法可能是制作一个8位的位图,在其上打开一个BitmapData对象,然后一行一行地写入其中,然后将其调色板设置为从0,0,0到255,255,255。

我发布了一个函数,该函数采用字节数组,图像尺寸和调色板,并在this answer中将其制成图像。