標籤:
先說Image,Image 就是個映像,不能執行個體化,提供了位元影像和源檔案操作的函數。本篇文章他就是來打醬油的,這裡提供一個Bitmap轉成BitmapSource的方法。
1 [DllImport("gdi32")] 2 static extern int DeleteObject(IntPtr o); 3 /// <summary> 4 /// bitmap轉換為bitmapsource 以適應wpf的image 5 /// </summary> 6 /// <param name="pic"></param> 7 /// <returns></returns> 8 public static BitmapSource GetMapSource(Bitmap pic) 9 {10 IntPtr ip = pic.GetHbitmap();11 BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(12 ip, IntPtr.Zero, Int32Rect.Empty,13 System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());14 DeleteObject(ip);15 return bitmapSource;16 }
接下來說Bitmap和BitmapData。
Bitmap類
Bitmap對象封裝了GDI+中的一個位元影像,此位元影像由圖形映像及其屬性的像素資料群組成.因此Bitmap是用於處理由像素資料定義的映像的對象.該類的主要方法和屬性如下:
1. GetPixel方法和SetPixel方法:擷取和設定一個映像的指定像素的顏色.
2. PixelFormat屬性:返回映像的像素格式.
3. Palette屬性:擷取和設定映像所使用的顏色調色盤.
4. Height Width屬性:返回映像的高度和寬度.
5. LockBits方法和UnlockBits方法:分別鎖定和解鎖系統記憶體中的位元影像像素.在基於像素點的影像處理方法中使用LockBits和UnlockBits是一個很好的方式,這兩種方法可以使我們指定像素的範圍來控制位元影像的任意一部分,從而消除了通過迴圈對位元影像的像素逐個進行處理,每調用LockBits之後都應該調用一次UnlockBits.
BitmapData類
BitmapData對象指定了位元影像的屬性
1. Height屬性:被鎖定位元影像的高度.
2. Width屬性:被鎖定位元影像的高度.
3. PixelFormat屬性:資料的實際像素格式.
4. Scan0屬性:被鎖定數組的首位元組地址,如果整個映像被鎖定,則是映像的第一個位元組地址.
5. Stride屬性:步幅,也稱為掃描寬度.
這裡要重點說說Stride屬性,這個和Width有什麼區別呢,可以這麼說,如果你的圖片大小也就是圖片位元組是4的整數倍,那麼Stride與Width是相等的,否則Stride就是大於Width的最小4的整數倍。在處理過程中,Stride肯定是4的整數倍,這裡是個坑啊。。。
盜張圖,串連寫在文章底部
先看看BitmapData的應用,我的情境是,我有一個一維像素點陣數組,裡面放的是每個像素點的灰階值,知道寬和高,要轉換成bitmap
1 /// <summary> 2 /// 像素點陣轉換為bitmap 3 /// </summary> 4 /// <param name="rawValues">byte[]數組</param> 5 /// <param name="width">圖片的寬度</param> 6 /// <param name="height">圖片的高度</param> 7 /// <returns>bitmap圖片</returns> 8 public static Bitmap ToGrayBitmap(byte[] rawValues, int width, int height) 9 {10 Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);11 BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);12 //// 擷取映像參數 13 //bmpData.Stride = width;14 int stride = bmpData.Stride; // 掃描線的寬度 15 int offset = stride - width; // 顯示寬度與掃描線寬度的間隙 16 IntPtr iptr = bmpData.Scan0; // 擷取bmpData的記憶體起始位置 17 int scanBytes = stride * height;// 用stride寬度,表示這是記憶體地區的大小 18 //// 下面把原始的顯示大小位元組數群組轉換為記憶體中實際存放的位元組數組 19 int posScan = 0, posReal = 0;// 分別設定兩個位置指標,指向源數組和目標數組 20 byte[] pixelValues = new byte[scanBytes]; //為目標數組分配記憶體 21 for (int x = 0; x < height; x++)22 {23 //// 下面的迴圈節是類比行掃描 24 for (int y = 0; y < width; y++)25 {26 pixelValues[posScan++] = rawValues[posReal++];27 }28 posScan += offset; //行掃描結束,要將目標位置指標移過那段“間隙” 29 }30 //// 用Marshal的Copy方法,將剛才得到的記憶體位元組數組複製到BitmapData中 31 System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes);32 bmp.UnlockBits(bmpData); // 解鎖記憶體地區 33 //// 下面的代碼是為了修改產生位元影像的索引表,從偽彩修改為灰階 34 ColorPalette tempPalette;35 using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed))36 {37 tempPalette = tempBmp.Palette;38 }39 for (int i = 0; i < 256; i++)40 {41 tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);42 }43 44 bmp.Palette = tempPalette;45 46 //// 演算法到此結束,返回結果 47 return bmp;48 }
這代碼也是網上找的,具體哪裡已經忘記了。至於24位位元影像資料其實就是 一個像素點有rgb三個值而已,道理一樣。
同樣,我們也可以根據圖片得到他的灰階數組
1 //8位位元影像得到除去檔案頭資訊的一位灰階數組 2 3 4 BitmapData bmpData = map.LockBits(new System.Drawing.Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); 5 6 //// 擷取映像參數 7 8 int stride = bmpData.Stride; // 掃描線的寬度 9 10 int offset = stride - map.Width; // 顯示寬度與掃描線寬度的間隙 11 12 IntPtr iptr = bmpData.Scan0; // 擷取bmpData的記憶體起始位置 13 14 int scanBytes = stride * map.Height;// 用stride寬度,表示這是記憶體地區的大小 15 16 //// 下面把原始的顯示大小位元組數群組轉換為記憶體中實際存放的位元組數組 17 18 mapdata = new byte[scanBytes]; //為目標數組分配記憶體19 20 System.Runtime.InteropServices.Marshal.Copy(iptr, mapdata, 0, scanBytes); //copy記憶體中資料到數組中
這裡對與bitmapdata的操作方式是ReadOnly
為什麼說stride是坑呢,因為在工作中,我有一個大小不為4的整數倍的檔案,通過上面方法將他們轉為圖片,然後操作之後我需要存回去,繼續存成檔案的形式,如果你直接存回去你會發現你的檔案變大了。這時候就需要避開stride。其實stride佔據的空間什麼都沒有做,我們如何遍曆構建圖片,就如何反遍曆回數組就可以了
public static byte[] GetMapData(byte[] MapData,int width,int height){ var length = MapData.Length; if(width==length/height) { return MapData; } int offset=length/height-width; var scanBytes = width * height; byte[] RawMapData = new byte[scanBytes]; int posScan = 0, posReal = 0; for(int x=0;x<height;x++) { for (int y=0;y<width;y++) { RawMapData[posScan++] = MapData[posReal++]; } posReal += offset; } return RawMapData;}
至於24位位元影像轉8位位元影像,還是看這位博主的部落格,他總結了很多,我還是覺得opencv比較快捷方便。
http://blog.csdn.net/jiangxinyu/article/details/6222302
另外還看到了一下c#處理圖片的方法,比如光照,霧化,浮雕等,請移步下面連結
http://www.pin5i.com/showtopic-20228.html
種一棵樹最好的時間是十年前,其次是現在。
C#中Image , Bitmap 和 BitmapData