【问题标题】:Saving a single channel of a bitmap image to a file [duplicate]将位图图像的单个通道保存到文件中[重复]
【发布时间】:2019-10-25 03:20:40
【问题描述】:

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

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

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

【问题讨论】:

  • 不确定File.WriteAllBytes(filename, bmp); 应该如何工作; bmp 是位图对象,不是字节数组。
  • 查看标记为重复的许多现有 Stack Overflow 问题之一,并提供解释如何从位图中提取单个颜色通道 (RGBA) 的答案。
  • @PeterDuniho 不过,这有点奇怪;它结合了优化的BitmapData 技术和超慢的SetPixel 技术。而且,由于我在此处的回答中提到的原因,它会在任何不是 32bpp ARGB 的输入上崩溃并烧毁。

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


【解决方案1】:

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

  • 不使用bmp.PixelFormat,而是将BitmapData 对象的像素格式强制为PixelFormat.Format32BppArgb,然后您100% 确定您将获得什么结构,并且在32 位模式下,步幅将始终准确匹配一个可预测的width * 4。如果你不这样做,如果读取的图像恰好是调色板或某种 16bpp 格式,其中每个像素不能被分成简单的颜色分量字节,你可能会得到意想不到的结果。
  • 循环数据并提取通道。字母“ARGB”的顺序是指一个十六进制值 0xAARRGGBB(例如,0xFF428ED0),它是一个小端序Uint32 值,这意味着颜色分量字节的实际顺序是相反的:@987654327 @。

所以,提取您的频道:

// 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 中发布了一个函数,它采用字节数组、图像尺寸和调色板并从中生成图像。

【讨论】:

    猜你喜欢
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 1970-01-01
    • 2014-07-31
    相关资源
    最近更新 更多