c#影象處理入門(-bitmap類和影象畫素值獲取方法)

pamxy發表於2013-11-12

轉自:http://blog.csdn.net/jiangxinyu/article/details/6222302

一.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);

             }

      }

}

(2) 記憶體法

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);

}

(3) 指標法

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);

         }

在以上3種方法中,提取畫素法能直觀的展示影象處理過程,可讀性很好,但效率最低,並不適合做影象處理方面的工程應用;記憶體法把影象直接複製到記憶體中,直接對記憶體中的資料進行處理,速度明顯提高,程式難度也不大;指標法直接應用指標來對影象進行處理,所以速度最快。

 

 

簡單圖片處理函式程式碼(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+完成數字影象處理的基本方法,通過例項,我們不難發現合理運用新技術不僅可以大大簡化我們的程式設計工作,還可以提高程式設計的效率。不過我們在運用新技術的同時也得明白掌握基本的程式設計思想才是最主要的,不同的語言、不同的機制只是實現的具體方式不同而已,其內在的思想還是相通的。對於上面的例子,掌握了編寫影象處理函式的演算法,用其他的方式實現也應該是可行的。同時,在上面的基礎上,讀者不妨試著舉一反三,編寫出更多的影象處理的函式來,以充實並完善這個簡單的例項。

image與byte陣列的轉換

image to byte[]

  MemoryStream ms=new MemoryStream();
   byte[] imagedata=null;
   pictureBox1.Image.Save(ms,System.Drawing.Imaging.ImageFormat.Gif );
   imagedata=ms.GetBuffer ();

byte[] to image

  ms = New IO.MemoryStream(by)
  img = Drawing.Image.FromStream(ms)

 


相關文章