幫人做一個遙感數字圖像處理的小功能,其中的數據源是Landset圖像,八位灰度。
然而Winform真的太弱了,System.Drawing下的PixelFormat(像素格式)枚舉居然沒有提供八位灰度模式,無奈只好用

PixelFormat.Format8bppIndexed(八位顏色索引)替代,可是問題又來了···

 

要使顏色索引模式生效,必須在Bitmap的調色板中設置每個索引到具體的顏色的映射:

for (int i = 0; i < 256; i++)

{

    // 每一個灰度映射到一種顏色

    bmp.Palette.Entries[i] = Color.FromArgb(i, i, i);

}

運行結果,圖像呈現了一種藍色色調,沒有在灰度模式下顯示。
我找到了bmp.Palette的定義,Bitmap的Palette屬性可讀可寫,是一個ColorPalette(調色板)對象(MSDN鏈接)。
那麼是否能夠直接對這個屬性賦值呢?
似乎有難度,因為“調色板”沒有一個公開的構造方法。於是谷歌了一下解決方法,有一位網友是這麼做的:

Bitmap bmpTemp = newBitmap(1, 1, PixelFormat.Format8bppIndexed);

ColorPalette palette = bmpTemp.Palette;

for (int i = 0; i < 256; i++)

{

   palette.Entries[i] = Color.FromArgb(i, i, i);

}

bmp.Palette = palette;

特別構造了一個臨時的Bitmap,為的就是取他的調色板。運行後,確實有效,灰度圖像正常顯示。
有沒有不用更容易的辦法呢?有!我在後面的嘗試當中,發現了另一種方法:

ColorPalette palette = bmp.Palette;

for (int i = 0; i < 256; i++)

{

   palette.Entries[i] = Color.FromArgb(i, i, i);

}

bmp.Palette = palette;

 

和第一段代碼沒有什麼差別啊,只是定義一個新的變量來保存bmp.Palette。這段代碼如何能夠起作用呢?
但確實起作用了···

 

按照正常的思維,ColorPalette是引用類型,所以palette只是複製了bmp.Palette的引用,最後一句把自己的引用重新賦值回來應當沒有用處,因此效果和第一段代碼應當一樣!!
既然MSDN不能解決疑惑,那只有求助於Reflector了。
在Bitmap的父類Image中,找到了Palette屬性的實現:

public ColorPalette get_Palette()
{
    return this._GetColorPalette();
}

private ColorPalette _GetColorPalette()
{
    ······
    ColorPalette palette = new ColorPalette(size);
    IntPtr ptr = Marshal.AllocHGlobal(size);
    ······
   
    return palette;
}

很明顯,Bitmap.Palette並沒有像普通的屬性一樣,返回對應字段的引用,而是複製了一份新的。這就好的解釋了為什麼第一段代碼完全沒有效果。

 

當然,這樣的類用起來實在不爽,不瞭解他的內部構造,就沒有辦法正常使用。能改用WPF的,還是改了吧。