http://blog.csdn.net/dz45693/article/details/7725458
一.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屬性:步幅,也稱為掃描寬度.
如上圖所示,數組的長度並不一定等於圖像像素數組的長度,還有一部分未用區域,這涉及到位圖的數據結構,系統要保證每行的字節數必須為4的倍數.
三.Graphics類
Graphics對象是GDI+的關鍵所在,許多對象都是由Graphics類表示的,該類定義了繪制和填充圖形對象的方法和屬性,一個應用程序只要需要進行繪制或著色,它就必須使用Graphics對象.
四.Image類
這個類提供了位圖和元文件操作的函數.Image類被聲明為abstract,也就是說Image類不能實例化對象,而只能做為一個基類
1.FromFile方法:它根據輸入的文件名產生一個Image對象,它有兩種函數形式:
public static Image FromFile(string filename);
public static Image FromFile(string filename, bool useEmbeddedColorManagement);
2.FromHBitmap方法:它從一個windows句柄處創建一個bitmap對象,它也包括兩種函數形式:
public static bitmap fromhbitmap(intptr hbitmap);
public static bitmap fromhbitmap(intptr hbitmap, intptr hpalette);
3. FromStream方法:從一個數據流中創建一個image對象,它包含三種函數形式:
public static image fromstream(stream stream);
public static image fromstream(stream stream, bool useembeddedcolormanagement);
fromstream(stream stream, bool useembeddedcolormanagement, bool validateimagedata);
有了上面的了解,我們便可以開始利用C#做圖像處理,下面介紹幾種方法:
一. 打開、保存、顯示圖像
privateBitmap srcBitmap = null; privateBitmap showBitmap = null; //打開文件 privatevoid menuFileOpen_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = newOpenFileDialog(); openFileDialog.Filter = @"Bitmap文件(*.bmp)|*.bmp|Jpeg文件(*.jpg)|*.jpg|所有合適文件(*.bmp,*.jpg)|*.bmp;*.jpg"; openFileDialog.FilterIndex = 3; openFileDialog.RestoreDirectory = true; if (DialogResult.OK == openFileDialog.ShowDialog()) { srcBitmap = (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false); showBitmap = srcBitmap; this.AutoScroll = true; this.AutoScrollMinSize = newSize((int)(showBitmap.Width), (int)(showBitmap.Height)); this.Invalidate(); } } //保存圖像文件 privatevoid menuFileSave_Click(object sender, EventArgs e) { if (showBitmap != null) { SaveFileDialog saveFileDialog = newSaveFileDialog(); saveFileDialog.Filter = @"Bitmap文件(*.bmp)|*.bmp|Jpeg文件(*.jpg)|*.jpg|所有合適文件(*.bmp,*.jpg)|*.bmp;*.jpg"; saveFileDialog.FilterIndex = 3; saveFileDialog.RestoreDirectory = true; if (DialogResult.OK == saveFileDialog.ShowDialog()) { ImageFormat format = ImageFormat.Jpeg; switch (Path.GetExtension(saveFileDialog.FileName).ToLower()) { case".jpg": format = ImageFormat.Jpeg; break; case".bmp": format = ImageFormat.Bmp; break; default: MessageBox.Show(this, "Unsupported image format was specified", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { showBitmap.Save(saveFileDialog.FileName,format ); } catch (Exception) { MessageBox.Show(this, "Failed writing image file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }
c#中將bitmap或者image保存為清晰的gif
在c#中默認可以講bitmap保存為gif等格式,但是這種保存方法保存的gif會嚴重失真,正常情況下的代碼:
1 System.Drawing.Bitmap b = new System.Drawing.Bitmap(「c://original_image.gif「);
2 System.Drawing.Image thmbnail = b.GetThumbnailImage(100,75,null,new IntPtr());
3 thmbnail.Save(「c://thumnail.gif「, System.Drawing.Imaging.ImageFormat.Gif);
一個批量處理圖片的軟件,包括各種處理方式,處理效果,但是在保存為gif的時候出現了問題,在網上查了很久也沒有發現一個可用的改善gif圖片質量的方法,找到了一個解決辦法,保存出來的gif容量大減,但是效果基本符合常規這中方法就是就是「Octree「 算法。
「Octree「 算法允許我們插入自己的算法來量子化我們的圖像。
一個好的「顏色量子化」算法應該考慮在兩個像素顆粒之間填充與這兩個像素顏色相近的過渡顏色,提供更多可視顏色空間。
Morgan Skinner提供了很好的「Octree「 算法代碼,大家可以下載參考使用。
使用OctreeQuantizer很方便:
System.Drawing.Bitmap b = new System.Drawing.Bitmap(「c://original_image.gif「); System.Drawing.Image thmbnail = b.GetThumbnailImage(100,75,null,new IntPtr()); OctreeQuantizer quantizer = new OctreeQuantizer ( 255 , 8 ) ; using ( Bitmap quantized = quantizer.Quantize ( thmbnail ) ) { quantized.Save(「c://thumnail.gif「, System.Drawing.Imaging.ImageFormat.Gif); } OctreeQuantizer grayquantizer = new GrayscaleQuantizer ( ) ; using ( Bitmap quantized = grayquantizer.Quantize ( thmbnail ) ) { quantized.Save(「c://thumnail.gif「, System.Drawing.Imaging.ImageFormat.Gif); }
你可以點擊這裡下載類的文件(項目文件),根據我的試用,只需要兩個類文件(OctreeQuantizer.cs,Quantizer.cs)即可運行,將這兩個類文件的namespace改成
你項目的名稱就行,還有,需要在不安全編譯的方式下編譯,右擊項目名稱,在生成選項卡裡選擇"允許不安全代碼"即可
//窗口重繪,在窗體上顯示圖像,重載Paint privatevoid frmMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { if (showBitmap != null) { Graphics g = e.Graphics; g.DrawImage(showBitmap, newRectangle(this.AutoScrollPosition.X, this.AutoScrollPosition.Y , (int)(showBitmap.Width), (int)(showBitmap.Height))); } } //灰度化 privatevoid menu2Gray_Click(object sender, EventArgs e) { if (showBitmap == null) return; showBitmap = RGB2Gray(showBitmap);//下面都以RGB2Gray為例 this.Invalidate(); }
二. 提取像素法
這種方法簡單易懂,但相當耗時,完全不可取.
publicstaticBitmap RGB2Gray(Bitmap srcBitmap) { Color srcColor; int wide = srcBitmap.Width; int height = srcBitmap.Height; for (int y = 0; y < height; y++) for (int x = 0; x < wide; x++) { //獲取像素的RGB顏色值 srcColor = srcBitmap.GetPixel(x, y); byte temp = (byte)(srcColor.R * .299 + srcColor.G * .587 + srcColor.B * .114); //設置像素的RGB顏色值 srcBitmap.SetPixel(x, y, Color.FromArgb(temp, temp, temp)); } return srcBitmap ; }
三. 內存法
這是比較常用的方法
publicstaticBitmap RGB2Gray(Bitmap srcBitmap) { int wide = srcBitmap.Width; int height = srcBitmap.Height; Rectangle rect = newRectangle(0, 0, wide, height); //將Bitmap鎖定到系統內存中,獲得BitmapData BitmapData srcBmData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //創建Bitmap Bitmap dstBitmap = CreateGrayscaleImage(wide, height);//這個函數在後面有定義 BitmapData dstBmData = dstBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); //位圖中第一個像素數據的地址。它也可以看成是位圖中的第一個掃描行 System.IntPtr srcPtr = srcBmData.Scan0; System.IntPtr dstPtr = dstBmData.Scan0; //將Bitmap對象的信息存放到byte數組中 int src_bytes = srcBmData.Stride * height; byte[] srcValues = newbyte[src_bytes]; int dst_bytes = dstBmData.Stride * height; byte[] dstValues = newbyte[dst_bytes]; //復制GRB信息到byte數組 System.Runtime.InteropServices.Marshal.Copy(srcPtr, srcValues, 0, src_bytes); System.Runtime.InteropServices.Marshal.Copy(dstPtr, dstValues, 0, dst_bytes); //根據Y=0.299*R+0.114*G+0.587B,Y為亮度 for (int i = 0; i < height; i++) for (int j = 0; j < wide; j++) { //只處理每行中圖像像素數據,舍棄未用空間 //注意位圖結構中RGB按BGR的順序存儲 int k = 3 * j; byte temp = (byte)(srcValues[i * srcBmData.Stride + k + 2] * .299 + srcValues[i * srcBmData.Stride + k + 1] * .587 + srcValues[i * srcBmData.Stride + k] * .114); dstValues[i * dstBmData.Stride + j] = temp; } System.Runtime.InteropServices.Marshal.Copy(dstValues, 0, dstPtr, dst_bytes); //解鎖位圖 srcBitmap.UnlockBits(srcBmData); dstBitmap.UnlockBits(dstBmData); return dstBitmap; }
四 指針法
C/C++的習慣,不是C#的特點
publicstaticBitmap RGB2Gray(Bitmap srcBitmap) { int wide = srcBitmap.Width; int height = srcBitmap.Height ; Rectangle rect = newRectangle(0, 0, wide, height); BitmapData srcBmData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); Bitmap dstBitmap = CreateGrayscaleImage(wide, height); BitmapData dstBmData = dstBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); System.IntPtr srcScan = srcBmData.Scan0; System.IntPtr dstScan = dstBmData.Scan0; Unsafe //啟動不安全代碼 { byte* srcP = (byte*)(void*) srcScan; byte* dstP = (byte*)(void*) dstScan; int srcOffset = srcBmData.Stride - wide * 3; int dstOffset = dstBmData.Stride - wide ; byte red, green, blue; for (int y = 0; y < height; y++) { for (int x = 0; x <wide ; x++, srcP += 3, dstP++) { blue = srcP [0]; green = srcP [1]; red = srcP [2]; * dstP = (byte)(.299 * red + .587 * green + .114 * blue); } srcP += srcOffset; dstP += dstOffset; } } srcBitmap.UnlockBits(srcBmData); dstBitmap.UnlockBits(dstBmData ); return dstBitmap; }
五. 矩陣法
並不是什麼新方法,只是將圖像數據分做R,G,B三個矩陣(二維數組)存儲,類似MATLAB的習慣.
publicstaticbool GetRGB(Bitmap Source, outint[,] R, outint[,] G, outint[,] B) { try { int iWidth = Source.Width; int iHeight = Source.Height; Rectangle rect = newRectangle(0, 0, iWidth, iHeight); System.Drawing.Imaging.BitmapData bmpData = Source.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, Source.PixelFormat); IntPtr iPtr = bmpData.Scan0; int iBytes = iWidth * iHeight * 3; byte[] PixelValues = new byte[iBytes]; System.Runtime.InteropServices.Marshal.Copy(iPtr, PixelValues, 0, iBytes); Source.UnlockBits(bmpData); R = newint[iHeight, iWidth]; G = newint[iHeight, iWidth]; B = newint[iHeight, iWidth]; int iPoint = 0; for (int i = 0; i < iHeight; i++) { for (int j = 0; j < iWidth; j++) { B[i, j] = Convert.ToInt32(PixelValues[iPoint++]); G[i, j] = Convert.ToInt32(PixelValues[iPoint++]); R[i, j] = Convert.ToInt32(PixelValues[iPoint++]); } } return true; } catch (Exception) { R = null; G = null; B = null; returnfalse; } } publicstaticBitmap FromRGB(int[,] R, int[,] G, int[,] B) { int iWidth = G.GetLength(1); int iHeight = G.GetLength(0); Bitmap Result = newBitmap(iWidth, iHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Rectangle rect = newRectangle(0, 0, iWidth, iHeight); System.Drawing.Imaging.BitmapData bmpData = Result.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb); IntPtr iPtr = bmpData.Scan0; int iStride = bmpData.Stride; int iBytes = iWidth * iHeight * 3; byte[] PixelValues = newbyte[iBytes]; int iPoint = 0; for (int i = 0; i < iHeight; i++) for (int j = 0; j < iWidth; j++) { int iG = G[i, j]; int iB = B[i, j]; int iR = R[i, j]; PixelValues[iPoint] = Convert.ToByte(iB); PixelValues[iPoint + 1] = Convert.ToByte(iG); PixelValues[iPoint + 2] = Convert.ToByte(iR); iPoint += 3; } System.Runtime.InteropServices.Marshal.Copy(PixelValues, 0, iPtr, iBytes); Result.UnlockBits(bmpData); return Result; } publicstaticbool GetGray(Bitmap srcBitmap, outbyte [,] gray) { Bitmap tempBitmap; if (srcBitmap.PixelFormat != PixelFormat.Format8bppIndexed) tempBitmap = ImageProcess.Image.Gray(srcBitmap); else tempBitmap = srcBitmap; int wide = tempBitmap.Width; int height = tempBitmap.Height; gray = newbyte [height, wide]; BitmapData gbmData = tempBitmap.LockBits(newRectangle(0, 0, wide, height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); System.IntPtr ScanG = gbmData.Scan0; int gOffset = gbmData.Stride - wide; unsafe { byte* g = (byte*)(void*)ScanG; for (int y = 0; y < height; y++) { for (int x = 0; x < wide; x++, g++) { gray[y ,x ] =*g; } g += gOffset; } } tempBitmap.UnlockBits(gbmData); returntrue ; } Public static Bitmap FromGray(byte [,] Gray) { int iWidth = Gray.GetLength(1); int iHeight = Gray.GetLength(0); Bitmap dstBitmap = ImageProcess.Image.CreateGrayscaleImage(iWidth, iHeight); BitmapData gbmData = dstBitmap.LockBits(newRectangle(0, 0, iWidth, iHeight), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); System.IntPtr ScanG = gbmData.Scan0; int gOffset = gbmData.Stride - iWidth; unsafe { byte* g = (byte*)(void*)ScanG; for (int i = 0; i < iHeight; i++) { for (int j = 0; j < iWidth; j++) { *g=(byte )Gray[i, j] ; g++; } g += gOffset; } } dstBitmap.UnlockBits(gbmData); return dstBitmap; } ///<summary> /// Create and initialize grayscale image ///</summary> publicstaticBitmap CreateGrayscaleImage( int width, int height ) { // create new image Bitmap bmp = newBitmap( width, height, PixelFormat.Format8bppIndexed ); // set palette to grayscale SetGrayscalePalette( bmp ); // return new image return bmp; }//# ///<summary> /// Set pallete of the image to grayscale ///</summary> publicstaticvoid SetGrayscalePalette( Bitmap srcImg ) { // check pixel format if ( srcImg.PixelFormat != PixelFormat.Format8bppIndexed ) thrownewArgumentException( ); // get palette ColorPalette cp = srcImg.Palette; // init palette for ( int i = 0; i < 256; i++){ cp.Entries[i] = Color.FromArgb( i, i, i ); } srcImg.Palette = cp; }
C#數字圖像處理的3種典型方法(精簡版)
C#數字圖像處理有3種典型方法:提取像素法、內存法、指針法。其中提取像素法使用的是GDI+中的Bitmap.GetPixel和Bitmap.SetPixel方法;內存法是通過LockBits方法來獲取位圖的首地址,從而把圖像數據直接復制到內存中進行處理;指針法與內存法相似,但該方法直接應用指針對位圖進行操作,由於在默認情況下,C#不支持指針運算,所以該方法只能在unsafe關鍵字所標記的代碼塊中使用。以一幅真彩色圖像的灰度化為例,下面代碼分別展現了這3種方法的使用,方便大家學習圖像處理的基本技巧。
(1) 像素提取法
if (curBitmap != null) { Color curColor; int gray; for (int i = 0; i < curBitmap.Width; i++) { for (int j = 0; j < curBitmap.Height; j++) { curColor = curBitmap.GetPixel(i, j); gray = (int)(0.3 * curColor.R + 0.59 * curColor.G * 0.11 * curColor.B); curBitmap.SetPixel(i, j, curColor); } } }
if (curBitmap != null) { int width = curBitmap.Width; int height = curBitmap.Height; int length = height * 3 * width; RGB = new byte[length]; BitmapData data = curBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); System.IntPtr Scan0 = data.Scan0; System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length); double gray = 0; for (int i = 0; i < RGB.Length; i=i+3) { gray = RGB[i + 2] * 0.3 + RGB[i + 1] * 0.59 + RGB[i] * 0.11; RGB[i + 2] = RGB[i + 1] = RGB[i] = (byte)gray; } System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length); curBitmap.UnlockBits(data); }
if (curBitmap != null) { int width = curBitmap.Width; int height = curBitmap.Height; BitmapData data = curBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); System.IntPtr Scan0 = data.Scan0; int stride = data.Stride; System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length); unsafe { byte* p = (byte*)Scan0; int offset = stride - width * 3; double gray = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { gray = 0.3 * p[2] + 0.59 * p[1] + 0.11 * p[0]; p[2] = p[1] = p[0] = (byte)gray; p += 3; } p += offset; } } curBitmap.UnlockBits(data); }
簡單圖片處理函數代碼(C#)
一、生成圖片並實現顏色漸變效果
Response.Clear(); Bitmap imgOutput = new Bitmap(100, 50); Graphics gic = Graphics.FromImage(imgOutput); gic.Clear(Color.BlueViolet); gic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; gic.DrawString("漸變圖形", new Font("黑體",16,FontStyle.Italic), new SolidBrush(Color.White),new PointF(2,2)); gic.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(new Point(0,0), new Point(100,50), Color.FromArgb(0,0,0,0), Color.FromArgb(255,255,255,255)),0,0,100,50); imgOutput.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg); gic.Dispose(); imgOutput.Dispose(); Response.End();
System.Drawing.Image drawimage = System.Drawing.Image.FromFile(photopath);//photopath表示圖片的物理地址 drawimage.RotateFlip(RotateFlipType.Rotate270FlipNone); if(File.Exists(photopath)) { File.SetAttributes(photopath,FileAttributes.Normal); File.Delete(photopath); } drawimage.Save(photopath,System.Drawing.Imaging.ImageFormat.Jpeg); drawimage.Dispose(); 三、對圖片進行縮放 System.Drawing.Image drawimage = System.Drawing.Image.FromFile(photopath); Bitmap imgOutput = new Bitmap(drawimage,60,30); imgOutput.Save(newphotppath, System.Drawing.Imaging.ImageFormat.Jpeg); imgOutput.Dispose(); Response.End();
其他還有一些畫線、畫矩形、畫圓等的函數和方法都可以在System.Drawing中找到;
本文的實例是一個數字圖像處理的應用程序,它完成的功能包括對圖像顏色的翻轉、對圖像進行灰度處理和對圖像進行增亮處理。該程序對圖像進行處理部分的代碼包含在一個專門的Filters類裡面,通過調用該類裡的靜態成員函數,我們就可以實現相應的圖像處理功能了。為實現圖像處理,我們要對圖像進行逐個象素處理。我們知道圖像是由一個個的象素點組成的,對一幅圖像的每個象素進行了相應的處理,最後整個圖像也就處理好了。在這個過程中,我們只需對每個象素點進行相應的處理,在處理過程中卻不需要考慮周圍象素點對其的影響,所以相對來說程序的實現就變得簡單多了。
由於GDI+中的BitmapData類不提供對圖像內部數據的直接訪問的方法,我們唯一的辦法就是使用指針來獲得圖像的內部數據,這時我們就得運用unsafe這個關鍵字來指明函數中訪問圖像內部數據的代碼塊了。在程序中,我還運用了打開文件和保存文件等選項,以使我們的辛勤勞動不付之東流。
二.程序的實現:
Invert()、Gray()、Brightness()等三個函數均包含在Filters類裡面,
Invert()函數的算法如下:
public static bool Invert(Bitmap b) { BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte * p = (byte *)(void *)Scan0; int nOffset = stride - b.Width*3; int nWidth = b.Width * 3; for(int y=0;y<b.Height;++y) { for(int x=0; x < nWidth; ++x ) { p[0] = (byte)(255-p[0]); ++p; } p += nOffset; } } b.UnlockBits(bmData); return true; } |
該函數以及後面的函數的參數都是Bitmap類型的,它們傳值的對象就是程序中所打開的圖像文件了。該函數中的BitmapData類型的bmData包含了圖像文件的內部信息,bmData的Stride屬性指明了一條線的寬度,而它的Scan0屬性則是指向圖像內部信息的指針。本函數完成的功能是圖像顏色的翻轉,實現的方法即用255減去圖像中的每個象素點的值,並將所得值設置為原象素點處的值,對每個象素點進行如此的操作,只到整幅圖像都處理完畢。函數中的unsafe代碼塊是整個函數的主體部分,首先我們取得圖像內部數據的指針,然後設置好偏移量,同時設置nWidth為b.Width*3,因為每個象素點包含了三種顏色成分,對每個象素點進行處理時便要進行三次處理。接下來運用兩個嵌套的for循環完成對每個象素點的處理,處理的核心便是一句:p[0] = (byte)(255-p[0]);。在unsafe代碼塊後,便可運用b.UnlockBits(bmData)進行圖像資源的釋放。函數執行成功,最後返回true值。注:由於是要編譯不安全代碼,所以得將項目屬性頁中的"允許不安全代碼塊"屬性設置為true,
Gray()函數的算法如下:
public static bool Gray(Bitmap b) { BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; unsafe { byte * p = (byte *)(void *)Scan0; int nOffset = stride - b.Width*3; byte red, green, blue; for(int y=0;y<b.Height;++y) { for(int x=0; x < b.Width; ++x ) { blue = p[0]; green = p[1]; red = p[2]; p[0] = p[1] = p[2] = (byte)(.299 * red + .587 * green + .114 * blue); p += 3; } p += nOffset; } } b.UnlockBits(bmData); return true; } |
本函數完成的功能是對圖像進行灰度處理,我們的基本想法可是將每個象素點的三種顏色成分的值取平均值。然而由於人眼的敏感性,這樣完全取平均值的做法的效果並不好,所以在程序中我取了三個效果最好的參數:.299,.587,.114。不過在這裡要向讀者指明的是,在GDI+中圖像存儲的格式是BGR而非RGB,即其順序為:Blue、Green、Red。所以在for循環內部一定要設置好red、green、blue等變量的值,切不可顛倒。函數執行成功後,同樣返回true值。
Brightness()函數的算法如下:
public static bool Brightness(Bitmap b, int nBrightness) { if (nBrightness < -255 || nBrightness > 255) return false; BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int stride = bmData.Stride; System.IntPtr Scan0 = bmData.Scan0; int nVal = 0; unsafe { byte * p = (byte *)(void *)Scan0; int nOffset = stride - b.Width*3; int nWidth = b.Width * 3; for(int y=0;y<b.Height;++y) { for(int x=0; x < nWidth; ++x ) { nVal = (int) (p[0] + nBrightness); if (nVal < 0) nVal = 0; if (nVal > 255) nVal = 255; p[0] = (byte)nVal; ++p; } p += nOffset; } } b.UnlockBits(bmData); return true; } |
本函數完成的功能是對圖像進行增亮處理,它比上面兩個函數多了一個增亮參數-nBrightness,該參數由用戶輸入,范圍為-255~255。在取得了增亮參數後,函數的unsafe代碼部分對每個象素點的不同顏色成分進行逐個處理,即在原來值的基礎上加上一個增亮參數以獲得新的值。同時代碼中還有一個防止成分值越界的操作,因為RGB成分值的范圍為0~255,一旦超過了這個范圍就要重新設置。函數最後執行成功後,同樣得返回true值。
該函數實現的程序效果如下:
首先,我們把圖像增亮的參數設置為100(其范圍為-255~255),然後執行效果如下,讀者也可嘗試其他的參數值。
三.小結:
本文通過一個簡單的實例向大家展現了用Visual C#以及GDI+完成數字圖像處理的基本方法,通過實例,我們不難發現合理運用新技術不僅可以大大簡化我們的編程工作,還可以提高編程的效率。不過我們在運用新技術的同時也得明白掌握基本的編程思想才是最主要的,不同的語言、不同的機制只是實現的具體方式不同而已,其內在的思想還是相通的。對於上面的例子,掌握了編寫圖像處理函數的算法,用其他的方式實現也應該是可行的。同時,在上面的基礎上,讀者不妨試著舉一反三,編寫出更多的圖像處理的函數來,以充實並完善這個簡單的實例。
http://www.koders.com/csharp/fid0644880EA62A4A2E3DD67693F63B72BE7EB4BB4A.aspx
http://blog.csdn.net/jiangxinyu/article/details/6222302