C# 處理圖像三種方法對比


C#本身自帶有一定的圖像處理能力,即使在不依賴Emgu CV的情況下,也是有很大的潛質的。

不過,最近在處理大量圖片時,發現圖片數量較少時,處理本身所帶來的延時不會讓人敏感,但是數量較大時,程序花費大量時間在預處理圖片上,導致程序很容易誤報線程時延過大,導致誤判程序異常。對於這個問題苦惱很久,畢竟不是CS專業出身,對於圖像處理以及一些算法和庫的運用上感到畢竟吃力。

今天在閱讀了一些數字圖像處理的書之后感到收益很大,現在來做點試驗對照一下之前自己的錯誤在哪里。

之前看MS的C#API時候發現有一個Graphics挺適合我現在使用,於是便二話不說開干,今天才發現越來封裝的太好,反而會帶來許多拖累,不如操起指針暢快的運行。

不多說,試驗對比的是Graphics、操作內存、操作指針三者在性能上差距。

試驗內容:對同一張照片進行灰度處理,直接看三者之間延時有多大

 

1.試驗基本場景,比較丑陋,就湊合看着。。。

2.提取像素Button中,調用Graphics灰度化處理

private void button2_Click(object sender, EventArgs e)
        {
            if (curBitMap1 != null)
            {
                Color curColor;
                int ret;
                //個人運行時間判斷類
                GetRunTime getRunTime = new GetRunTime();
                //stopWatch啟動
                getRunTime.Start();
                //Graphics//灰度化
                for (int i = (int)(0); i < (int)(curBitMap1.Width ); i++)
                {
                    for (int j = 0; j < curBitMap1.Height; j++)
                    {
                        //獲取像素
                        curColor = curBitMap1.GetPixel(i, j);
                        //獲取RGB
                        ret = (int)(curColor.R * 0.299 + curColor.G * 0.587 + curColor.B * 0.114);
                        //設置像素
                        curBitMap1.SetPixel(i, j, Color.FromArgb(ret, ret, ret));
                    }
                }
                Invalidate();

                labelGetPixel.Text = "提取像素花費時間:" + getRunTime.getRunTime() + "ms";

            }
        }

  

3.內存法Button中,直接復制數據到內存,直接操作

private void button5_Click(object sender, EventArgs e)
        {
            if (curBitMap2 != null)
            {
                GetRunTime getRunTime = new GetRunTime();
                getRunTime.Start();
                Rectangle rect = new Rectangle(0, 0, curBitMap2.Width, curBitMap2.Height);
                //以讀寫方式鎖定位圖
                System.Drawing.Imaging.BitmapData bmpData =
                    curBitMap2.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, curBitMap2.PixelFormat);
                //首地址
                IntPtr ptr = bmpData.Scan0;
                //24位bmp的總字節數
                int bytes = curBitMap2.Width * curBitMap2.Height * 3;
                byte[] rgbValues = new byte[bytes];
                //復制被鎖定的圖像到rgbValues
                System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
                double colorTemp = 0;
                //灰度化
                for (int i = 0; i < bmpData.Height; i++)
                {
                    for (int j = 0; j < bmpData.Width * 3; j+=3)
                    {
                        //跳過空白塊
                        colorTemp = rgbValues[i * bmpData.Stride + j + 2] * 0.299 
                            + rgbValues[i * bmpData.Stride + j + 1] * 0.587 
                            + rgbValues[i * bmpData.Stride + j] * 0.114;
                        rgbValues[i * bmpData.Stride + j + 2] 
                            = rgbValues[i * bmpData.Stride + j + 1] 
                            = rgbValues[i * bmpData.Stride + j] = (byte)colorTemp;
                    }
                }
                //復制回位圖
                System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
                curBitMap2.UnlockBits(bmpData);
                Invalidate();
                labelCaChe.Text = "提取像素內存法花費時間:" + getRunTime.getRunTime() + "ms";
            }
        }

  

4.指針法Button中,直接復制數據到內存,指針操作 

private void button6_Click(object sender, EventArgs e)
        {
            GetRunTime getRunTime = new GetRunTime();
            getRunTime.Start();
            Rectangle rect = new Rectangle(0, 0, curBitMap3.Width, curBitMap3.Height);
            //以讀寫方式鎖定位圖
            System.Drawing.Imaging.BitmapData bmpData =
                    curBitMap3.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, curBitMap3.PixelFormat);
            byte temp = 0;
            unsafe
            {
                //首地址
                byte* ptr = (byte*)(bmpData.Scan0);
                //灰度化
                for (int i = 0; i < bmpData.Height; i++)
                {
                    for (int j = 0; j < bmpData.Width;j++ )
                    {
                        temp = (byte)(0.299 * ptr[2] + 0.587 * ptr[1] + 0.114 * ptr[0]);
                        ptr[0] = ptr[1] = ptr[2] = temp;
                        ptr += 3;
                    }
                    //跳過空白塊
                    ptr += bmpData.Stride - bmpData.Width * 3;
                }
            }
            //解鎖位圖
            curBitMap3.UnlockBits(bmpData);

            Invalidate();
            labelPoint.Text = "提取像素內存法花費時間:" + getRunTime.getRunTime() + "ms";
        }

5,簡單完善一下就開始打開圖片看看運行時間之間的對比吧

總結:雖然電腦爛,但還是看得出三者之間的差距吧。復制進內存直接操作和用指針操作都是極大的提高效率,如果處理一張圖片節約60ms,那處理大量的圖片時候將會節約多少時間?所以今后處理圖像的時候,如果不用Emgu,盡量也別用Graphics,還是自己動手操作內存。(肯定會有人說,你為毛不去用C++,好吧,我其實就是在黑Graphics,你咬我呀~)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM