C#影象處理 (各種旋轉、改變大小、柔化、銳化、霧化、底片、浮雕、黑白、濾鏡效果) 一、各種旋轉、改變大小 注意:先要新增畫圖相關的using引用。 //向右旋轉影象90°程式碼如下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Bitmap bmp = new Bitmap("rama.jpg");//載入影象 g.FillRectangle(Brushes.White, this.ClientRectangle);//填充窗體背景為白色 Point[] destinationPoints = { new Point(100, 0), // destination for upper-left point of original new Point(100, 100),// destination for upper-right point of original new Point(0, 0)}; // destination for lower-left point of original g.DrawImage(bmp, destinationPoints); } //旋轉影象180°程式碼如下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Bitmap bmp = new Bitmap("rama.jpg"); g.FillRectangle(Brushes.White, this.ClientRectangle); Point[] destinationPoints = { new Point(0, 100), // destination for upper-left point of original new Point(100, 100),// destination for upper-right point of original new Point(0, 0)}; // destination for lower-left point of original g.DrawImage(bmp, destinationPoints); } //影象切變程式碼: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Bitmap bmp = new Bitmap("rama.jpg"); g.FillRectangle(Brushes.White, this.ClientRectangle); Point[] destinationPoints = { new Point(0, 0), // destination for upper-left point of original new Point(100, 0), // destination for upper-right point of original new Point(50, 100)};// destination for lower-left point of original g.DrawImage(bmp, destinationPoints); } //影象擷取: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Bitmap bmp = new Bitmap("rama.jpg"); g.FillRectangle(Brushes.White, this.ClientRectangle); Rectangle sr = new Rectangle(80, 60, 400, 400);//要擷取的矩形區域 Rectangle dr = new Rectangle(0, 0, 200, 200);//要顯示到Form的矩形區域 g.DrawImage(bmp, dr, sr, GraphicsUnit.Pixel); } //改變影象大小: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Bitmap bmp = new Bitmap("rama.jpg"); g.FillRectangle(Brushes.White, this.ClientRectangle); int width = bmp.Width; int height = bmp.Height; // 改變影象大小使用低質量的模式 g.InterpolationMode = InterpolationMode.NearestNeighbor; g.DrawImage(bmp, new Rectangle(10, 10, 120, 120), // source rectangle new Rectangle(0, 0, width, height), // destination rectangle GraphicsUnit.Pixel); // 使用高質量模式 //g.CompositingQuality = CompositingQuality.HighSpeed; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage( bmp, new Rectangle(130, 10, 120, 120), new Rectangle(0, 0, width, height), GraphicsUnit.Pixel); } //設定影象的分辯率: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Bitmap bmp = new Bitmap("rama.jpg"); g.FillRectangle(Brushes.White, this.ClientRectangle); bmp.SetResolution(300f, 300f); g.DrawImage(bmp, 0, 0); bmp.SetResolution(1200f, 1200f); g.DrawImage(bmp, 180, 0); } //用GDI+畫圖 private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics gForm = e.Graphics; gForm.FillRectangle(Brushes.White, this.ClientRectangle); for (int i = 1; i <= 7; ++i) { //在窗體上面畫出橙色的矩形 Rectangle r = new Rectangle(i*40-15, 0, 15, this.ClientRectangle.Height); gForm.FillRectangle(Brushes.Orange, r); } //在記憶體中建立一個Bitmap並設定CompositingMode Bitmap bmp = new Bitmap(260, 260, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Graphics gBmp = Graphics.FromImage(bmp); gBmp.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; // 建立一個帶有Alpha的紅色區域 // 並將其畫在記憶體的點陣圖裡面 Color red = Color.FromArgb(0x60, 0xff, 0, 0); Brush redBrush = new SolidBrush(red); gBmp.FillEllipse(redBrush, 70, 70, 160, 160); // 建立一個帶有Alpha的綠色區域 Color green = Color.FromArgb(0x40, 0, 0xff, 0); Brush greenBrush = new SolidBrush(green); gBmp.FillRectangle(greenBrush, 10, 10, 140, 140); //在窗體上面畫出點陣圖 now draw the bitmap on our window gForm.DrawImage(bmp, 20, 20, bmp.Width, bmp.Height); // 清理資源 bmp.Dispose(); gBmp.Dispose(); redBrush.Dispose(); greenBrush.Dispose(); } //在窗體上面繪圖並顯示影象 private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Pen blackPen = new Pen(Color.Black, 1); if (ClientRectangle.Height / 10 > 0) { for (int y = 0; y < ClientRectangle.Height; y += ClientRectangle.Height / 10) { g.DrawLine(blackPen, new Point(0, 0), new Point(ClientRectangle.Width, y)); } } blackPen.Dispose(); } C# 使用Bitmap類進行圖片裁剪 在Mapwin(手機遊戲地圖編輯器)生成的地圖txt檔案中新增自己需要處理的資料後轉換成可在手機(Ophone)開發環境中使用的位元組流地圖檔案的小工具,其中就涉及到圖片的裁剪和生成了。有以下幾種方式。 方法一:拷貝畫素。 當然這種方法是最笨的,效率也就低了些。 在Bitmap類中我們可以看到這樣兩個方法:GetPixel(int x, int y)和SetPixel(int x, int y, Color color)方法。從字面的含以上就知道前者是獲取影象某點畫素值,是用Color物件返回的;後者是將已知畫素描畫到制定的位置。 下面就來做個例項檢驗下: 1.首先建立一個Windows Form窗體程式,往該窗體上拖放7個PictureBox控制元件,第一個用於放置並顯示原始的大圖片,其後6個用於放置並顯示裁剪後新生成的6個小圖; 2.放置原始大圖的PictureBox控制元件name屬性命名為pictureBoxBmpRes,其後pictureBox1到pictureBox6依次命名,並放置在合適的位置; 3.雙擊Form窗體,然後在Form1_Load事件中加入下面的程式碼即可。 //匯入影象資源 Bitmap bmpRes = null; String strPath = Application.ExecutablePath; try{ int nEndIndex = strPath.LastIndexOf('//'); strPath = strPath.Substring(0,nEndIndex) + "//Bmp//BmpResMM.bmp"; bmpRes = new Bitmap(strPath); //窗體上顯示載入圖片 pictureBoxBmpRes.Width = bmpRes.Width; pictureBoxBmpRes.Height = bmpRes.Height; pictureBoxBmpRes.Image = bmpRes; } catch(Exception ex) { System.Windows.Forms.MessageBox.Show("圖片資源載入失敗!/r/n" + ex.ToString()); } //裁剪圖片(裁成2行3列的6張圖片) int nYClipNum = 2, nXClipNum = 3; Bitmap[] bmpaClipBmpArr = new Bitmap[nYClipNum * nXClipNum]; for (int nYClipNumIndex = 0; nYClipNumIndex < nYClipNum; nYClipNumIndex++) { for (int nXClipNumIndex = 0; nXClipNumIndex < nXClipNum; nXClipNumIndex++) { int nClipWidth = bmpRes.Width / nXClipNum; int nClipHight = bmpRes.Height / nYClipNum; int nBmpIndex = nXClipNumIndex + nYClipNumIndex * nYClipNum + (nYClipNumIndex > 0?1:0); bmpaClipBmpArr[nBmpIndex] = new Bitmap(nClipWidth, nClipHight); for(int nY = 0; nY < nClipHight; nY++) { for(int nX = 0; nX < nClipWidth; nX++) { int nClipX = nX + nClipWidth * nXClipNumIndex; int nClipY = nY + nClipHight * nYClipNumIndex; Color cClipPixel = bmpRes.GetPixel(nClipX, nClipY); bmpaClipBmpArr[nBmpIndex].SetPixel(nX, nY, cClipPixel); } } } } PictureBox[] picbShow = new PictureBox[nYClipNum * nXClipNum]; picbShow[0] = pictureBox1; picbShow[1] = pictureBox2; picbShow[2] = pictureBox3; picbShow[3] = pictureBox4; picbShow[4] = pictureBox5; picbShow[5] = pictureBox6; for (int nLoop = 0; nLoop < nYClipNum * nXClipNum; nLoop++) { picbShow[nLoop].Width = bmpRes.Width / nXClipNum; picbShow[nLoop].Height = bmpRes.Height / nYClipNum; picbShow[nLoop].Image = bmpaClipBmpArr[nLoop]; } 現在看看那些地方需要注意的了。其中 int nBmpIndex = nXClipNumIndex + nYClipNumIndex * nYClipNum + (nYClipNumIndex > 0?1:0); 這句定義了儲存裁剪圖片物件在陣列中的索引,需要注意的就是後面的(nYClipNumIndex > 0?1:0)——因為只有當裁剪的物件處於第一行以外的行時需要將索引加1; 另外,因為這種方法的效率不高,程式執行起來還是頓了下。如果有興趣的話,可以將以上的程式碼放到一個按鈕Click事件函式中,當單擊該按鈕時就可以感覺到了。 方法二:運用Clone函式區域性複製。 同樣在Bitmap中可以找到Clone()方法,該方法有三個過載方法。Clone(),Clone(Rectangle, PixelFormat)和Clone(RectangleF, PixelFormat)。第一個方法將建立並返回一個精確的例項物件,後兩個就是我們這裡需要用的區域性裁剪了(其實後兩個方法本人覺得用法上差不多)。 將上面的程式稍稍改進下——將裁剪的處理放到一個按鈕事件函式中,然後再託一個按鈕好窗體上,最後將下面的程式碼複製到該按鈕的事件函式中。 for (int nYClipNumIndex = 0; nYClipNumIndex < nYClipNum; nYClipNumIndex++) { for (int nXClipNumIndex = 0; nXClipNumIndex < nXClipNum; nXClipNumIndex++) { int nClipWidth = bmpRes.Width / nXClipNum; int nClipHight = bmpRes.Height / nYClipNum; int nBmpIndex = nXClipNumIndex + nYClipNumIndex * nYClipNum + (nYClipNumIndex > 0 ? 1 : 0); Rectangle rClipRect = new Rectangle(nClipWidth * nXClipNumIndex, nClipHight * nYClipNumIndex, nClipWidth, nClipHight); bmpaClipBmpArr[nBmpIndex] = bmpRes.Clone(rClipRect, bmpRes.PixelFormat); } } 執行程式,單擊按鈕檢驗下,發現速度明顯快可很多。 其實這種方法較第一中方法不同的地方僅只是變換了for迴圈中的拷貝部分的處理, Rectangle rClipRect = new Rectangle(nClipWidth * nXClipNumIndex, nClipHight * nYClipNumIndex, nClipWidth, nClipHight); bmpaClipBmpArr[nBmpIndex] = bmpRes.Clone(rClipRect, bmpRes.PixelFormat); 一. 底片效果 原理: GetPixel方法獲得每一點畫素的值, 然後再使用SetPixel方法將取反後的顏色值設定到對應的點. 效果圖: 程式碼實現: private void button1_Click(object sender, EventArgs e) { //以底片效果顯示影象 try { int Height = this.pictureBox1.Image.Height; int Width = this.pictureBox1.Image.Width; Bitmap newbitmap = new Bitmap(Width, Height); Bitmap oldbitmap = (Bitmap)this.pictureBox1.Image; Color pixel; for (int x = 1; x < Width; x++) { for (int y = 1; y < Height; y++) { int r, g, b; pixel = oldbitmap.GetPixel(x, y); r = 255 - pixel.R; g = 255 - pixel.G; b = 255 - pixel.B; newbitmap.SetPixel(x, y, Color.FromArgb(r, g, b)); } } this.pictureBox1.Image = newbitmap; } catch (Exception ex) { MessageBox.Show(ex.Message, "資訊提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } 二. 浮雕效果 原理: 對影象畫素點的畫素值分別與相鄰畫素點的畫素值相減後加上128, 然後將其作為新的畫素點的值. 效果圖: 程式碼實現: private void button1_Click(object sender, EventArgs e) { //以浮雕效果顯示影象 try { int Height = this.pictureBox1.Image.Height; int Width = this.pictureBox1.Image.Width; Bitmap newBitmap = new Bitmap(Width, Height); Bitmap oldBitmap = (Bitmap)this.pictureBox1.Image; Color pixel1, pixel2; for (int x = 0; x < Width - 1; x++) { for (int y = 0; y < Height - 1; y++) { int r = 0, g = 0, b = 0; pixel1 = oldBitmap.GetPixel(x, y); pixel2 = oldBitmap.GetPixel(x + 1, y + 1); r = Math.Abs(pixel1.R - pixel2.R + 128); g = Math.Abs(pixel1.G - pixel2.G + 128); b = Math.Abs(pixel1.B - pixel2.B + 128); if (r > 255) r = 255; if (r < 0) r = 0; if (g > 255) g = 255; if (g < 0) g = 0; if (b > 255) b = 255; if (b < 0) b = 0; newBitmap.SetPixel(x, y, Color.FromArgb(r, g, b)); } } this.pictureBox1.Image = newBitmap; } catch (Exception ex) { MessageBox.Show(ex.Message, "資訊提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } 三. 黑白效果 原理: 彩色影象處理成黑白效果通常有3種演算法; (1).最大值法: 使每個畫素點的 R, G, B 值等於原畫素點的 RGB (顏色值) 中最大的一個; (2).平均值法: 使用每個畫素點的 R,G,B值等於原畫素點的RGB值的平均值; (3).加權平均值法: 對每個畫素點的 R, G, B值進行加權 ---自認為第三種方法做出來的黑白效果影象最 "真實". 效果圖: 程式碼實現: private void button1_Click(object sender, EventArgs e) { //以黑白效果顯示影象 try { int Height = this.pictureBox1.Image.Height; int Width = this.pictureBox1.Image.Width; Bitmap newBitmap = new Bitmap(Width, Height); Bitmap oldBitmap = (Bitmap)this.pictureBox1.Image; Color pixel; for (int x = 0; x < Width; x++) for (int y = 0; y < Height; y++) { pixel = oldBitmap.GetPixel(x, y); int r, g, b, Result = 0; r = pixel.R; g = pixel.G; b = pixel.B; //例項程式以加權平均值法產生黑白影象 int iType =2; switch (iType) { case 0://平均值法 Result = ((r + g + b) / 3); break; case 1://最大值法 Result = r > g ? r : g; Result = Result > b ? Result : b; break; case 2://加權平均值法 Result = ((int)(0.7 * r) + (int)(0.2 * g) + (int)(0.1 * b)); break; } newBitmap.SetPixel(x, y, Color.FromArgb(Result, Result, Result)); } this.pictureBox1.Image = newBitmap; } catch (Exception ex) { MessageBox.Show(ex.Message, "資訊提示"); } } 四. 柔化效果 原理: 當前畫素點與周圍畫素點的顏色差距較大時取其平均值. 效果圖: 程式碼實現: private void button1_Click(object sender, EventArgs e) { //以柔化效果顯示影象 try { int Height = this.pictureBox1.Image.Height; int Width = this.pictureBox1.Image.Width; Bitmap bitmap = new Bitmap(Width, Height); Bitmap MyBitmap = (Bitmap)this.pictureBox1.Image; Color pixel; //高斯模板 int[] Gauss ={ 1, 2, 1, 2, 4, 2, 1, 2, 1 }; for (int x = 1; x < Width - 1; x++) for (int y = 1; y < Height - 1; y++) { int r = 0, g = 0, b = 0; int Index = 0; for (int col = -1; col <= 1; col++) for (int row = -1; row <= 1; row++) { pixel = MyBitmap.GetPixel(x + row, y + col); r += pixel.R * Gauss[Index]; g += pixel.G * Gauss[Index]; b += pixel.B * Gauss[Index]; Index++; } r /= 16; g /= 16; b /= 16; //處理顏色值溢位 r = r > 255 ? 255 : r; r = r < 0 ? 0 : r; g = g > 255 ? 255 : g; g = g < 0 ? 0 : g; b = b > 255 ? 255 : b; b = b < 0 ? 0 : b; bitmap.SetPixel(x - 1, y - 1, Color.FromArgb(r, g, b)); } this.pictureBox1.Image = bitmap; } catch (Exception ex) { MessageBox.Show(ex.Message, "資訊提示"); } } 五.銳化效果 原理:突出顯示顏色值大(即形成形體邊緣)的畫素點. 效果圖: 實現程式碼: private void button1_Click(object sender, EventArgs e) { //以銳化效果顯示影象 try { int Height = this.pictureBox1.Image.Height; int Width = this.pictureBox1.Image.Width; Bitmap newBitmap = new Bitmap(Width, Height); Bitmap oldBitmap = (Bitmap)this.pictureBox1.Image; Color pixel; //拉普拉斯模板 int[] Laplacian ={ -1, -1, -1, -1, 9, -1, -1, -1, -1 }; for (int x = 1; x < Width - 1; x++) for (int y = 1; y < Height - 1; y++) { int r = 0, g = 0, b = 0; int Index = 0; for (int col = -1; col <= 1; col++) for (int row = -1; row <= 1; row++) { pixel = oldBitmap.GetPixel(x + row, y + col); r += pixel.R * Laplacian[Index]; g += pixel.G * Laplacian[Index]; b += pixel.B * Laplacian[Index]; Index++; } //處理顏色值溢位 r = r > 255 ? 255 : r; r = r < 0 ? 0 : r; g = g > 255 ? 255 : g; g = g < 0 ? 0 : g; b = b > 255 ? 255 : b; b = b < 0 ? 0 : b; newBitmap.SetPixel(x - 1, y - 1, Color.FromArgb(r, g, b)); } this.pictureBox1.Image = newBitmap; } catch (Exception ex) { MessageBox.Show(ex.Message, "資訊提示"); } } 六. 霧化效果 原理: 在影象中引入一定的隨機值, 打亂影象中的畫素值 效果圖: 實現程式碼: private void button1_Click(object sender, EventArgs e) { //以霧化效果顯示影象 try { int Height = this.pictureBox1.Image.Height; int Width = this.pictureBox1.Image.Width; Bitmap newBitmap = new Bitmap(Width, Height); Bitmap oldBitmap = (Bitmap)this.pictureBox1.Image; Color pixel; for (int x = 1; x < Width - 1; x++) for (int y = 1; y < Height - 1; y++) { System.Random MyRandom = new Random(); int k = MyRandom.Next(123456); //畫素塊大小 int dx = x + k % 19; int dy = y + k % 19; if (dx >= Width) dx = Width - 1; if (dy >= Height) dy = Height - 1; pixel = oldBitmap.GetPixel(dx, dy); newBitmap.SetPixel(x, y, pixel); } this.pictureBox1.Image = newBitmap; } catch (Exception ex) { MessageBox.Show(ex.Message, "資訊提示"); } } 淺談Visual C#進行影象處理 作者:彭軍 http://pengjun.org.cn 這裡之所以說“淺談”是因為我這裡只是簡單的介紹如何使用Visual C#進行影象的讀入、儲存以及對畫素的訪問。而不涉及太多的演算法。 一、讀入影象 在Visual C#中我們可以使用一個Picture Box控制元件來顯示圖片,如下: private void btnOpenImage_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "BMP Files(*.bmp)|*.bmp|JPG Files(*.jpg;*.jpeg)|*.jpg;*.jpeg|All Files(*.*)|*.*"; ofd.CheckFileExists = true; ofd.CheckPathExists = true; if (ofd.ShowDialog() == DialogResult.OK) { //pbxShowImage.ImageLocation = ofd.FileName; bmp = new Bitmap(ofd.FileName); if (bmp==null) { MessageBox.Show("載入圖片失敗!", "錯誤"); return; } pbxShowImage.Image = bmp; ofd.Dispose(); } } 其中bmp為類的一個物件:private Bitmap bmp=null; 在使用Bitmap類和BitmapData類之前,需要使用using System.Drawing.Imaging; 二、儲存影象 private void btnSaveImage_Click(object sender, EventArgs e) { if (bmp == null) return; SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "BMP Files(*.bmp)|*.bmp|JPG Files(*.jpg;*.jpeg)|*.jpg;*.jpeg|All Files(*.*)|*.*"; if (sfd.ShowDialog() == DialogResult.OK) { pbxShowImage.Image.Save(sfd.FileName); MessageBox.Show("儲存成功!","提示"); sfd.Dispose(); } } 三、對畫素的訪問 我們可以來建立一個GrayBitmapData類來做相關的處理。整個類的程式如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; namespace ImageElf { class GrayBitmapData { public byte[,] Data;//儲存畫素矩陣 public int Width;//影象的寬度 public int Height;//影象的高度 public GrayBitmapData() { this.Width = 0; this.Height = 0; this.Data = null; } public GrayBitmapData(Bitmap bmp) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); this.Width = bmpData.Width; this.Height = bmpData.Height; Data = new byte[Height, Width]; unsafe { byte* ptr = (byte*)bmpData.Scan0.ToPointer(); for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { //將24位的RGB彩色圖轉換為灰度圖 int temp = (int)(0.114 * (*ptr++)) + (int)(0.587 * (*ptr++))+(int)(0.299 * (*ptr++)); Data[i, j] = (byte)temp; } ptr += bmpData.Stride - Width * 3;//指標加上填充的空白空間 } } bmp.UnlockBits(bmpData); } public GrayBitmapData(string path) : this(new Bitmap(path)) { } public Bitmap ToBitmap() { Bitmap bmp=new Bitmap(Width,Height,PixelFormat.Format24bppRgb); BitmapData bmpData=bmp.LockBits(new Rectangle(0,0,Width,Height),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb); unsafe { byte* ptr=(byte*)bmpData.Scan0.ToPointer(); for(int i=0;i<Height;i++) { for(int j=0;j<Width;j++) { *(ptr++)=Data[i,j]; *(ptr++)=Data[i,j]; *(ptr++)=Data[i,j]; } ptr+=bmpData.Stride-Width*3; } } bmp.UnlockBits(bmpData); return bmp; } public void ShowImage(PictureBox pbx) { Bitmap b = this.ToBitmap(); pbx.Image = b; //b.Dispose(); } public void SaveImage(string path) { Bitmap b=ToBitmap(); b.Save(path); //b.Dispose(); } //均值濾波 public void AverageFilter(int windowSize) { if (windowSize % 2 == 0) { return; } for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { int sum = 0; for (int g = -(windowSize - 1) / 2; g <= (windowSize - 1) / 2; g++) { for (int k = -(windowSize - 1) / 2; k <= (windowSize - 1) / 2; k++) { int a = i + g, b = j + k; if (a < 0) a = 0; if (a > Height - 1) a = Height - 1; if (b < 0) b = 0; if (b > Width - 1) b = Width - 1; sum += Data[a, b]; } } Data[i,j]=(byte)(sum/(windowSize*windowSize)); } } } //中值濾波 public void MidFilter(int windowSize) { if (windowSize % 2 == 0) { return; } int[] temp = new int[windowSize * windowSize]; byte[,] newdata = new byte[Height, Width]; for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { int n = 0; for (int g = -(windowSize - 1) / 2; g <= (windowSize - 1) / 2; g++) { for (int k = -(windowSize - 1) / 2; k <= (windowSize - 1) / 2; k++) { int a = i + g, b = j + k; if (a < 0) a = 0; if (a > Height - 1) a = Height - 1; if (b < 0) b = 0; if (b > Width - 1) b = Width - 1; temp[n++]= Data[a, b]; } } newdata[i, j] = GetMidValue(temp,windowSize*windowSize); } } for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { Data[i, j] = newdata[i, j]; } } } //獲得一個向量的中值 private byte GetMidValue(int[] t, int length) { int temp = 0; for (int i = 0; i < length - 2; i++) { for (int j = i + 1; j < length - 1; j++) { if (t[i] > t[j]) { temp = t[i]; t[i] = t[j]; t[j] = temp; } } } return (byte)t[(length - 1) / 2]; } //一種新的濾波方法,是亮的更亮、暗的更暗 public void NewFilter(int windowSize) { if (windowSize % 2 == 0) { return; } for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { int sum = 0; for (int g = -(windowSize - 1) / 2; g <= (windowSize - 1) / 2; g++) { for (int k = -(windowSize - 1) / 2; k <= (windowSize - 1) / 2; k++) { int a = i + g, b = j + k; if (a < 0) a = 0; if (a > Height - 1) a = Height - 1; if (b < 0) b = 0; if (b > Width - 1) b = Width - 1; sum += Data[a, b]; } } double avg = (sum+0.0) / (windowSize * windowSize); if (avg / 255 < 0.5) { Data[i, j] = (byte)(2 * avg / 255 * Data[i, j]); } else { Data[i,j]=(byte)((1-2*(1-avg/255.0)*(1-Data[i,j]/255.0))*255); } } } } //直方圖均衡 public void HistEqual() { double[] num = new double[256] ; for(int i=0;i<256;i++) num[i]=0; for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { num[Data[i, j]]++; } } double[] newGray = new double[256]; double n = 0; for (int i = 0; i < 256; i++) { n += num[i]; newGray[i] = n * 255 / (Height * Width); } for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { Data[i,j]=(byte)newGray[Data[i,j]]; } } } } } 在GrayBitmapData類中,只要我們對一個二維陣列Data進行一系列的操作就是對圖片的操作處理。在視窗上,我們可以使用 一個按鈕來做各種呼叫: //均值濾波 private void btnAvgFilter_Click(object sender, EventArgs e) { if (bmp == null) return; GrayBitmapData gbmp = new GrayBitmapData(bmp); gbmp.AverageFilter(3); gbmp.ShowImage(pbxShowImage); } //轉換為灰度圖 private void btnToGray_Click(object sender, EventArgs e) { if (bmp == null) return; GrayBitmapData gbmp = new GrayBitmapData(bmp); gbmp.ShowImage(pbxShowImage); } 四、總結 在Visual c#中對影象進行處理或訪問,需要先建立一個Bitmap物件,然後通過其LockBits方法來獲得一個BitmapData類的物件,然後通過獲得其畫素資料的首地址來對Bitmap物件的畫素資料進行操作。當然,一種簡單但是速度慢的方法是用Bitmap類的GetPixel和SetPixel方法。其中BitmapData類的Stride屬性為每行畫素所佔的位元組。 C# colorMatrix 對圖片的處理 : 亮度調整 抓屏 翻轉 隨滑鼠畫矩形 1.圖片亮度處理 private void btn_Grap_Click(object sender, EventArgs e) { //亮度百分比 int percent = 50; Single v = 0.006F * percent; Single[][] matrix = { new Single[] { 1, 0, 0, 0, 0 }, new Single[] { 0, 1, 0, 0, 0 }, new Single[] { 0, 0, 1, 0, 0 }, new Single[] { 0, 0, 0, 1, 0 }, new Single[] { v, v, v, 0, 1 } }; System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix(matrix); System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes(); attr.SetColorMatrix(cm); //Image tmp Image tmp = Image.FromFile("1.png"); this.pictureBox_Src.Image = Image.FromFile("1.png"); Graphics g = Graphics.FromImage(tmp); try { Rectangle destRect = new Rectangle(0, 0, tmp.Width, tmp.Height); g.DrawImage(tmp, destRect, 0, 0, tmp.Width, tmp.Height, GraphicsUnit.Pixel, attr); } finally { g.Dispose(); } this.pictureBox_Dest.Image = (Image)tmp.Clone(); } 2.抓屏將生成的圖片顯示在pictureBox private void btn_Screen_Click(object sender, EventArgs e) { Image myImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); Graphics g = Graphics.FromImage(myImage); g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)); //IntPtr dc1 = g.GetHdc(); //此處這兩句多餘,具體看最後GetHdc()定義 //g.ReleaseHdc(dc1); g.Dispose(); this.pictureBox_Src.SizeMode = PictureBoxSizeMode.StretchImage; this.pictureBox_Src.Image = myImage; myImage.Save("Screen", ImageFormat.Png); } 3.翻轉 private void btn_RotateFlip_Click(object sender, EventArgs e) { this.pictureBox_Src.Image = Image.FromFile("1.png"); Image tmp = Image.FromFile("1.png"); tmp.RotateFlip(RotateFlipType.Rotate90FlipNone); this.pictureBox_Dest.Image = tmp; } 4.跟隨滑鼠在 pictureBox的圖片上畫矩形 private int intStartX = 0; private int intStartY = 0; private bool isMouseDraw = false; private void pictureBox_Src_MouseDown(object sender, MouseEventArgs e) { isMouseDraw = true; intStartX = e.X; intStartY = e.Y; } private void pictureBox_Src_MouseMove(object sender, MouseEventArgs e) { if (isMouseDraw) { try { //Image tmp = Image.FromFile("1.png"); Graphics g = this.pictureBox_Src.CreateGraphics(); //清空上次畫下的痕跡 g.Clear(this.pictureBox_Src.BackColor); Brush brush = new SolidBrush(Color.Red); Pen pen = new Pen(brush, 1); pen.DashStyle = DashStyle.Solid; g.DrawRectangle(pen, new Rectangle(intStartX > e.X ? e.X : intStartX, intStartY > e.Y ? e.Y : intStartY, Math.Abs(e.X - intStartX), Math.Abs(e.Y - intStartY))); g.Dispose(); //this.pictureBox_Src.Image = tmp; } catch (Exception ex) { ex.ToString(); } } } private void pictureBox_Src_MouseUp(object sender, MouseEventArgs e) { isMouseDraw = false; intStartX = 0; intStartY = 0; } 5.取灰度 private void btn_GetGray_Click(object sender, EventArgs e) { this.pictureBox_Src.Image = Image.FromFile("1.png"); Bitmap currentBitmap = new Bitmap(this.pictureBox_Src.Image); Graphics g = Graphics.FromImage(currentBitmap); ImageAttributes ia = new ImageAttributes(); float[][] colorMatrix = { new float[] {0.299f, 0.299f, 0.299f, 0, 0}, new float[] {0.587f, 0.587f, 0.587f, 0, 0}, new float[] {0.114f, 0.114f, 0.114f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }; ColorMatrix cm = new ColorMatrix(colorMatrix); ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(currentBitmap, new Rectangle(0, 0, currentBitmap.Width, currentBitmap.Height), 0, 0, currentBitmap.Width, currentBitmap.Height, GraphicsUnit.Pixel, ia); this.pictureBox_Dest.Image = (Image)(currentBitmap.Clone()); g.Dispose(); } Graphics.GetHdc 方法 .NET Framework 4 獲取與此 Graphics 關聯的裝置上下文的控制程式碼。 名稱空間: System.Drawing 程式集: System.Drawing(在 System.Drawing.dll 中) 語法 [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public IntPtr GetHdc() 返回值 型別:System.IntPtr 與此 Graphics 關聯的裝置上下文的控制程式碼。 實現 IDeviceContext.GetHdc() 備註 裝置上下文是一個基於 GDI 的 Windows 結構,它定義一組圖形物件及其關聯的特性,以及影響輸出的圖形模式。 此方法返回該裝置上下文(字型除外)。由於未選擇字型,使用 GetHdc 方法返回的控制程式碼對 FromHdc 方法進行呼叫將會失敗。 GetHdc 方法呼叫和 ReleaseHdc 方法呼叫必須成對出現。 在 GetHdc 和 ReleaseHdc 方法對的範圍內,通常僅呼叫 GDI 函式。 在該範圍內對 Graphics(它產生 hdc 引數)的 GDI+ 方法的呼叫因 ObjectBusy 錯誤而失敗。 此外,GDI+ 忽略後續操作中對 hdc 引數的 Graphics 所做的所有狀態更改。 示例 下面的程式碼示例設計為與 Windows 窗體一起使用,它需要 PaintEventArgse,即 Paint 事件處理程式的一個引數。 該示例演示如何呼叫 Windows GDI 函式以執行與 GDI+ Graphics 方法相同的任務。 程式碼執行下列操作: 為 Windows DLL 檔案 gdi32.dll 定義互操作性 DllImportAttribute 特性。 此 DLL 包含所需的 GDI 函式。 將該 DLL 中的 Rectangle 函式定義為外部函式。 建立一支紅色鋼筆。 利用該鋼筆,使用 GDI+ DrawRectangle 方法將矩形繪製到螢幕。 定義內部指標型別變數 hdc 並將它的值設定為窗體的裝置上下文控制程式碼。 使用 GDI Rectangle 函式將矩形繪製到螢幕。 釋放由 hdc 參數列示的裝置上下文。 public class GDI { [System.Runtime.InteropServices.DllImport("gdi32.dll")] internal static extern bool Rectangle( IntPtr hdc, int ulCornerX, int ulCornerY, int lrCornerX, int lrCornerY); } [System.Security.Permissions.SecurityPermission( System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] private void GetHdcForGDI1(PaintEventArgs e) { // Create pen. Pen redPen = new Pen(Color.Red, 1); // Draw rectangle with GDI+. e.Graphics.DrawRectangle(redPen, 10, 10, 100, 50); // Get handle to device context. IntPtr hdc = e.Graphics.GetHdc(); // Draw rectangle with GDI using default pen. GDI.Rectangle(hdc, 10, 70, 110, 120); // Release handle to device context. e.Graphics.ReleaseHdc(hdc); }