專案需求:使用者上傳圖片後,需提供「灰階」與「原彩色」切換預覽,且不儲存多份檔案。利用 System.Drawing (GDI+) 對每像素操作,即可即時轉換。本文提供完整可用的 update_pixelColor 方法與核心演算法解析。
#region 圖片顏色轉換 ////// 將圖片轉為灰階或還原為偽彩色(單通道映射) /// /// 圖片實體完整路徑 /// true=轉灰階,false=轉偽彩色(紅色通道主導) public void UpdatePixelColor(string filePath, bool toGray) { using (Bitmap bmp = new Bitmap(Bitmap.FromFile(filePath))) { int pixelValue; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { Color original = bmp.GetPixel(x, y); if (toGray) pixelValue = GetGrayValue(original); else pixelValue = GetPseudoColorValue(original); // 灰階:R=G=B=同一值;偽彩色:此處示範同值(可擴展為調色盤映射) bmp.SetPixel(x, y, Color.FromArgb(pixelValue, pixelValue, pixelValue)); } } bmp.Save(filePath); // 覆蓋原檔,生產建議另存新檔 } } ////// 標準灰階公式:Y = 0.299R + 0.587G + 0.114B /// 整數運算優化:(R*19595 + G*38469 + B*7472) >> 16 /// private int GetGrayValue(Color c) { return (c.R * 19595 + c.G * 38469 + c.B * 7472) >> 16; } ////// 偽彩色示範:此處同灰階公式,實務可映射至調色盤 /// private int GetPseudoColorValue(Color c) { // 範例:單純取紅通道或自訂映射 return (c.R * 19595 + c.G * 38469 + c.B * 7472) >> 16; } #endregion
人眼對綠最敏感、紅次之、藍最低。ITU-R BT.601 標準亮度公式:
Y = 0.299 × R + 0.587 × G + 0.114 × B
為避免浮點運算,乘以 65536 (2^16) 整數化:
計算後右移 16 位(>> 16)即除以 65536,得到 0~255 灰階值。
GetPixel/SetPixel 每次呼叫都會鎖定/解鎖位圖記憶體,**極慢**(百萬像素需數秒)。實務必須使用 LockBits 直接操作記憶體:
public unsafe void FastGrayScale(string filePath)
{
using (Bitmap bmp = new Bitmap(filePath))
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmpData.Stride;
byte* ptr = (byte*)bmpData.Scan0;
for (int y = 0; y < bmp.Height; y++)
{
byte* row = ptr + y * stride;
for (int x = 0; x < bmp.Width; x++)
{
int b = row[x * 3];
int g = row[x * 3 + 1];
int r = row[x * 3 + 2];
int gray = (r * 19595 + g * 38469 + b * 7472) >> 16;
row[x * 3] = row[x * 3 + 1] = row[x * 3 + 2] = (byte)gray;
}
}
bmp.UnlockBits(bmpData);
bmp.Save(filePath);
}
}
效能差異: 1000x1000 圖片,GetPixel 約 2.5 秒,LockBits 約 30ms(快 80 倍)。
若需將灰階映射為「鐵紅、深藍、綠」等偽彩色,可建立 256 色調色盤陣列:
Color[] palette = new Color[256];
for (int i = 0; i < 256; i++)
{
// 範例:藍->青->綠->黃->紅 熱力圖漸層
if (i < 64) palette[i] = Color.FromArgb(0, i * 4, 255);
else if (i < 128) palette[i] = Color.FromArgb(0, 255, 255 - (i - 64) * 4);
else if (i < 192) palette[i] = Color.FromArgb((i - 128) * 4, 255, 0);
else palette[i] = Color.FromArgb(255, 255 - (i - 192) * 4, 0);
}
// LockBits 內使用:
// int gray = ...;
// row[x*3] = palette[gray].B;
// row[x*3+1] = palette[gray].G;
// row[x*3+2] = palette[gray].R;
Bitmap 需嚴格 using 或 Dispose(),否則導致程序記憶體暴漲。ImageSharp、SkiaSharp 等跨平台純管理庫。_gray.jpg)。本文提供從簡單 GetPixel 版本到高效能 LockBits 版本的完整灰階/偽彩色轉換實作。理解亮度公式整數化原理與記憶體鎖定技巧,是 .NET 影像處理的核心基礎。現代專案建議優先採用 ImageSharp 等無非受控相依的庫,避開 GDI+ 部署坑點。