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

C# 圖像處理實戰:Bitmap 載入、儲存與高效像素存取 (含濾波演算法)

     編輯:關於C語言

在 WinForms / WPF 應用中處理圖像,System.Drawing.Bitmap 是核心類別。本文涵蓋圖片讀寫、高效像素存取 (LockBits/unsafe),並實作灰階轉換、均值/中值濾波、直方圖均衡等常用演算法。

1. 基本圖片讀寫

// 讀取
private Bitmap _bmp;
private void BtnOpen_Click(object sender, EventArgs e)
{
    using (var ofd = new OpenFileDialog 
    { 
        Filter = "圖片檔|*.bmp;*.jpg;*.jpeg;*.png|所有檔案|*.*" 
    })
    {
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            _bmp = new Bitmap(ofd.FileName);
            picBox.Image = _bmp;
        }
    }
}

// 儲存
private void BtnSave_Click(object sender, EventArgs e)
{
    if (_bmp == null) return;
    using (var sfd = new SaveFileDialog { Filter = "JPEG|*.jpg|BMP|*.bmp|PNG|*.png" })
    {
        if (sfd.ShowDialog() == DialogResult.OK)
            _bmp.Save(sfd.FileName);
    }
}

2. 高效像素存取:LockBits + unsafe

GetPixel/SetPixel 極慢,實務上使用 Bitmap.LockBits 鎖定記憶體,配合指標直接操作位元組陣列。

灰階資料類別


public class GrayBitmapData
{
    public byte[,] Data;   // 灰階像素矩陣 [Height, Width]
    public int Width, Height;

    public GrayBitmapData(Bitmap bmp)
    {
        Width = bmp.Width; Height = bmp.Height;
        Data = new byte[Height, Width];

        var bmpData = bmp.LockBits(
            new Rectangle(0, 0, Width, Height),
            ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            byte* ptr = (byte*)bmpData.Scan0;
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    // BGR 順序,權重:R=0.299, G=0.587, B=0.114
                    int b = *ptr++; int g = *ptr++; int r = *ptr++;
                    Data[y, x] = (byte)(0.114 * b + 0.587 * g + 0.299 * r);
                }
                ptr += bmpData.Stride - Width * 3; // 跳過行填充
            }
        }
        bmp.UnlockBits(bmpData);
    }

    // 轉回 Bitmap 顯示/儲存
    public Bitmap ToBitmap()
    {
        var bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
        var bmpData = bmp.LockBits(
            new Rectangle(0, 0, Width, Height),
            ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            byte* ptr = (byte*)bmpData.Scan0;
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    byte v = Data[y, x];
                    *ptr++ = v; *ptr++ = v; *ptr++ = v; // 灰階轉 BGR
                }
                ptr += bmpData.Stride - Width * 3;
            }
        }
        bmp.UnlockBits(bmpData);
        return bmp;
    }
}

3. 經典濾波演算法實作

所有演算法直接操作 byte[,] Data,完成後呼叫 ToBitmap() 顯示。

均值濾波 (Mean Filter) — 平滑去噪


public void AverageFilter(int windowSize)
{
    if (windowSize % 2 == 0) return;
    int half = windowSize / 2;
    var newData = new byte[Height, Width];

    for (int y = 0; y < Height; y++)
        for (int x = 0; x < Width; x++)
        {
            int sum = 0, count = 0;
            for (int dy = -half; dy <= half; dy++)
                for (int dx = -half; dx <= half; dx++)
                {
                    int ny = Math.Clamp(y + dy, 0, Height - 1);
                    int nx = Math.Clamp(x + dx, 0, Width - 1);
                    sum += Data[ny, nx];
                    count++;
                }
            newData[y, x] = (byte)(sum / count);
        }
    Data = newData;
}

中值濾波 (Median Filter) — 保留邊緣去椒鹽噪點


public void MedianFilter(int windowSize)
{
    if (windowSize % 2 == 0) return;
    int half = windowSize / 2, kernelSize = windowSize * windowSize;
    var newData = new byte[Height, Width];
    var window = new int[kernelSize];

    for (int y = 0; y < Height; y++)
        for (int x = 0; x < Width; x++)
        {
            int idx = 0;
            for (int dy = -half; dy <= half; dy++)
                for (int dx = -half; dx <= half; dx++)
                {
                    int ny = Math.Clamp(y + dy, 0, Height - 1);
                    int nx = Math.Clamp(x + dx, 0, Width - 1);
                    window[idx++] = Data[ny, nx];
                }
            Array.Sort(window, 0, kernelSize);
            newData[y, x] = (byte)window[kernelSize / 2];
        }
    Data = newData;
}

直方圖均衡 — 增強對比度


public void HistogramEqualization()
{
    double[] hist = new double[256];
    foreach (byte v in Data) hist[v]++;

    double[] cdf = new double[256];
    cdf[0] = hist[0];
    for (int i = 1; i < 256; i++) cdf[i] = cdf[i-1] + hist[i];

    double total = Width * Height;
    byte[] map = new byte[256];
    for (int i = 0; i < 256; i++) 
        map[i] = (byte)Math.Round(cdf[i] / total * 255);

    for (int y = 0; y < Height; y++)
        for (int x = 0; x < Width; x++)
            Data[y, x] = map[Data[y, x]];
}

4. 使用範例


private void BtnGray_Click(object sender, EventArgs e)
{
    if (_bmp == null) return;
    var gray = new GrayBitmapData(_bmp);
    picBox.Image = gray.ToBitmap();
}

private void BtnMedian_Click(object sender, EventArgs e)
{
    if (_bmp == null) return;
    var gray = new GrayBitmapData(_bmp);
    gray.MedianFilter(3); // 3x3 視窗
    picBox.Image = gray.ToBitmap();
}

關鍵效能提示

  • Stride ≠ Width * BytesPerPixel:每行可能有填充位元組,必須用 bmpData.Stride 計算下一行起始位址。
  • PixelFormat.Format24bppRgb 記憶體排列為 BGR,非 RGB。
  • 大圖處理建議改用 Parallel.For 平行化外層迴圈。
  • 生產環境可考慮 System.Drawing.Common 替代方案 (如 ImageSharp, SkiaSharp) 以支援跨平台。

總結

掌握 LockBits + unsafe 指標操作,是 C# 高效影像處理的基石。結合經典濾波演算法,即可構建功能完整的圖像編輯工具。

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