先說(shuō)Image,Image 就是個(gè)圖像,不能實(shí)例化,提供了位圖和源文件操作的函數(shù)。本篇文章他就是來(lái)打醬油的,這里提供一個(gè)Bitmap轉(zhuǎn)成BitmapSource的方法。
1 [DllImport("gdi32")] 2 static extern int DeleteObject(IntPtr o); 3 /// <summary> 4 /// bitmap轉(zhuǎn)換為bitmapsource 以適應(yīng)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 }
接下來(lái)說(shuō)Bitmap和BitmapData。
Bitmap類(lèi)
Bitmap對(duì)象封裝了GDI+中的一個(gè)位圖,此位圖由圖形圖像及其屬性的像素?cái)?shù)據(jù)組成.因此Bitmap是用于處理由像素?cái)?shù)據(jù)定義的圖像的對(duì)象.該類(lèi)的主要方法和屬性如下:
1. GetPixel方法和SetPixel方法:獲取和設(shè)置一個(gè)圖像的指定像素的顏色.
2. PixelFormat屬性:返回圖像的像素格式.
3. Palette屬性:獲取和設(shè)置圖像所使用的顏色調(diào)色板.
4. Height Width屬性:返回圖像的高度和寬度.
5. LockBits方法和UnlockBits方法:分別鎖定和解鎖系統(tǒng)內(nèi)存中的位圖像素.在基于像素點(diǎn)的圖像處理方法中使用LockBits和UnlockBits是一個(gè)很好的方式,這兩種方法可以使我們指定像素的范圍來(lái)控制位圖的任意一部分,從而消除了通過(guò)循環(huán)對(duì)位圖的像素逐個(gè)進(jìn)行處理,每調(diào)用LockBits之后都應(yīng)該調(diào)用一次UnlockBits.
BitmapData類(lèi)
BitmapData對(duì)象指定了位圖的屬性
1. Height屬性:被鎖定位圖的高度.
2. Width屬性:被鎖定位圖的高度.
3. PixelFormat屬性:數(shù)據(jù)的實(shí)際像素格式.
4. Scan0屬性:被鎖定數(shù)組的首字節(jié)地址,如果整個(gè)圖像被鎖定,則是圖像的第一個(gè)字節(jié)地址.
5. Stride屬性:步幅,也稱(chēng)為掃描寬度.
這里要重點(diǎn)說(shuō)說(shuō)Stride屬性,這個(gè)和Width有什么區(qū)別呢,可以這么說(shuō),如果你的圖片大小也就是圖片字節(jié)是4的整數(shù)倍,那么Stride與Width是相等的,否則Stride就是大于Width的最小4的整數(shù)倍。在處理過(guò)程中,Stride肯定是4的整數(shù)倍,這里是個(gè)坑啊。。。
盜張圖,連接寫(xiě)在文章底部
先看看BitmapData的應(yīng)用,我的場(chǎng)景是,我有一個(gè)一維像素點(diǎn)陣數(shù)組,里面放的是每個(gè)像素點(diǎn)的灰度值,知道寬和高,要轉(zhuǎn)換成bitmap
1 /// <summary> 2 /// 像素點(diǎn)陣轉(zhuǎn)換為bitmap 3 /// </summary> 4 /// <param name="rawValues">byte[]數(shù)組</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 //// 獲取圖像參數(shù) 13 //bmpData.Stride = width;14 int stride = bmpData.Stride; // 掃描線的寬度 15 int offset = stride - width; // 顯示寬度與掃描線寬度的間隙 16 IntPtr iptr = bmpData.Scan0; // 獲取bmpData的內(nèi)存起始位置 17 int scanBytes = stride * height;// 用stride寬度,表示這是內(nèi)存區(qū)域的大小 18 //// 下面把原始的顯示大小字節(jié)數(shù)組轉(zhuǎn)換為內(nèi)存中實(shí)際存放的字節(jié)數(shù)組 19 int posScan = 0, posReal = 0;// 分別設(shè)置兩個(gè)位置指針,指向源數(shù)組和目標(biāo)數(shù)組 20 byte[] pixelValues = new byte[scanBytes]; //為目標(biāo)數(shù)組分配內(nèi)存 21 for (int x = 0; x < height; x++)22 {23 //// 下面的循環(huán)節(jié)是模擬行掃描 24 for (int y = 0; y < width; y++)25 {26 pixelValues[posScan++] = rawValues[posReal++];27 }28 posScan += offset; //行掃描結(jié)束,要將目標(biāo)位置指針移過(guò)那段“間隙” 29 }30 //// 用Marshal的Copy方法,將剛才得到的內(nèi)存字節(jié)數(shù)組復(fù)制到BitmapData中 31 System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes);32 bmp.UnlockBits(bmpData); // 解鎖內(nèi)存區(qū)域 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 //// 算法到此結(jié)束,返回結(jié)果 47 return bmp;48 }
這代碼也是網(wǎng)上找的,具體哪里已經(jīng)忘記了。至于24位位圖數(shù)據(jù)其實(shí)就是 一個(gè)像素點(diǎn)有rgb三個(gè)值而已,道理一樣。
同樣,我們也可以根據(jù)圖片得到他的灰度數(shù)組
1 //8位位圖得到除去文件頭信息的一位灰度數(shù)組 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 //// 獲取圖像參數(shù) 7 8 int stride = bmpData.Stride; // 掃描線的寬度 9 10 int offset = stride - map.Width; // 顯示寬度與掃描線寬度的間隙 11 12 IntPtr iptr = bmpData.Scan0; // 獲取bmpData的內(nèi)存起始位置 13 14 int scanBytes = stride * map.Height;// 用stride寬度,表示這是內(nèi)存區(qū)域的大小 15 16 //// 下面把原始的顯示大小字節(jié)數(shù)組轉(zhuǎn)換為內(nèi)存中實(shí)際存放的字節(jié)數(shù)組 17 18 mapdata = new byte[scanBytes]; //為目標(biāo)數(shù)組分配內(nèi)存19 20 System.Runtime.InteropServices.Marshal.Copy(iptr, mapdata, 0, scanBytes); //copy內(nèi)存中數(shù)據(jù)到數(shù)組中
這里對(duì)與bitmapdata的操作方式是ReadOnly
為什么說(shuō)stride是坑呢,因?yàn)樵诠ぷ髦校矣幸粋€(gè)大小不為4的整數(shù)倍的文件,通過(guò)上面方法將他們轉(zhuǎn)為圖片,然后操作之后我需要存回去,繼續(xù)存成文件的形式,如果你直接存回去你會(huì)發(fā)現(xiàn)你的文件變大了。這時(shí)候就需要避開(kāi)stride。其實(shí)stride占據(jù)的空間什么都沒(méi)有做,我們?nèi)绾伪闅v構(gòu)建圖片,就如何反遍歷回?cái)?shù)組就可以了
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位位圖轉(zhuǎn)8位位圖,還是看這位博主的博客,他總結(jié)了很多,我還是覺(jué)得opencv比較快捷方便。
http://blog.csdn.net/jiangxinyu/article/details/6222302
另外還看到了一下c#處理圖片的方法,比如光照,霧化,浮雕等,請(qǐng)移步下面鏈接
http://www.pin5i.com/showtopic-20228.html
種一棵樹(shù)最好的時(shí)間是十年前,其次是現(xiàn)在。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注