首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 從 Byte 陣列建立 256 階灰階 Bitmap 影像

     編輯:關於C#

在影像處理、掃描儀驅動、醫療影像等場景,常需將原始灰階像素資料(byte[],每像素 1 byte,0-255)轉為標準 Bitmap 物件。本文提供完整實作,處理 BMP 檔頭、調色盤、資料列對齊(DWORD padding)、上下翻轉等細節,直接可用於生產環境。

核心挑戰

  • BMP 像素資料需 4 位元組對齊(每列大小為 4 的倍數)。
  • BMP 儲存順序為 從下往上、從左往右,需垂直翻轉。
  • 8bpp 灰階需自訂 256 色調色盤(R=G=B=索引值)。

完整實作 CreateBitmap

public static Bitmap CreateBitmap(byte[] originalImageData, int originalWidth, int originalHeight)
{
    // 1. 建立 8bpp 空白 Bitmap
    var resultBitmap = new Bitmap(originalWidth, originalHeight, PixelFormat.Format8bppIndexed);

    // 2. 存入 MemoryStream 以取得 BMP 標頭結構
    var ms = new MemoryStream();
    resultBitmap.Save(ms, ImageFormat.Bmp);
    ms.Flush();

    // 3. 計算每列補齊位元組數(DWORD 對齊)
    int stride = ((originalWidth * 8 + 31) / 32) * 4; // 位元組/列
    int padBytes = stride - originalWidth;             // 每列補 0 數
    int bitmapDataSize = stride * originalHeight;      // 總像素資料大小

    // 4. 讀取檔頭中資料區偏移量(位移 10,長度 4)
    int dataOffset = ReadInt32(ms, 10);

    // 5. 重寫調色盤(位移 54 到 dataOffset)
    for (int i = 54, color = 0; i < dataOffset; i += 4, color++)
    {
        ms.Position = i;
        ms.Write(new byte[4] { (byte)color, (byte)color, (byte)color, 0 }, 0, 4);
    }

    // 6. 準備翻轉後的像素資料
    byte[] destData = new byte[bitmapDataSize];
    for (int srcRow = originalHeight - 1, dstRow = 0; srcRow >= 0; srcRow--, dstRow++)
    {
        int srcOffset = srcRow * originalWidth;
        int dstOffset = dstRow * stride;
        Buffer.BlockCopy(originalImageData, srcOffset, destData, dstOffset, originalWidth);
        // 補齊區域保持 0(new byte[] 預設為 0)
    }

    // 7. 寫入像素資料
    ms.Position = dataOffset;
    ms.Write(destData, 0, bitmapDataSize);
    ms.Flush();

    // 8. 從記憶體流重建 Bitmap
    return new Bitmap(ms);
}

// 輔助:從 MemoryStream 讀取 Int32 (Little Endian)
public static int ReadInt32(MemoryStream ms, int position)
{
    ms.Position = position;
    byte[] buf = new byte[4];
    ms.Read(buf, 0, 4);
    return BitConverter.ToInt32(buf, 0);
}

// 輔助:寫入 Int32
public static void WriteInt32(MemoryStream ms, int position, int value)
{
    ms.Position = position;
    ms.Write(BitConverter.GetBytes(value), 0, 4);
}

使用範例

// 模擬 100x100 灰階漸層資料
byte[] raw = new byte[100 * 100];
for (int y = 0; y < 100; y++)
    for (int x = 0; x < 100; x++)
        raw[y * 100 + x] = (byte)(x * 255 / 100);

Bitmap bmp = CreateBitmap(raw, 100, 100);
bmp.Save("gray_gradient.bmp");

關鍵細節補充

項目說明
PixelFormat.Format8bppIndexed指定 8 位元索引色彩模式(256 色)
stride 計算((width * bitsPerPixel + 31) / 32) * 4 為標準 BMP 列對齊公式
垂直翻轉BMP 底部為第 0 列,陣列索引 0 為影像頂部,故需倒序複製
調色盤8bpp 預設調色盤為 32bpp 彩色,必須改寫為灰階漸層
記憶體效率全程使用 MemoryStream + Buffer.BlockCopy,避免頻繁物件配置

替代方案(.NET Core / .NET 5+)

若不需相容舊版 BMP 格式,可直接使用 Bitmap.LockBits 寫入像素資料,省去檔頭解析:

var bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
// 設定調色盤
var pal = bmp.Palette;
for (int i = 0; i < 256; i++) pal.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = pal;

var bd = bmp.LockBits(new Rectangle(0,0,width,height), ImageLockMode.WriteOnly, bmp.PixelFormat);
for (int y = 0; y < height; y++)
{
    IntPtr rowPtr = bd.Scan0 + y * bd.Stride;
    Marshal.Copy(raw, y * width, rowPtr, width);
}
bmp.UnlockBits(bd);

此法更簡潔、效能更高,建議新專案優先採用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved