天天看點

[轉]幾種圖像處理類庫的比較

作者:王先榮

前言

近期需要做一些圖像處理方面的學習和研究,首要任務就是選擇一套合适的圖像處理類庫。目前較知名且功能完善的圖像處理類庫有OpenCv、EmguCv、AForge.net等等。本文将從許可協定、下載下傳、安裝、文檔資料、易用性、性能等方面對這些類庫進行比較,然後給出選擇建議,當然也包括我自己的選擇。

許可協定

類庫

許可協定網址

大緻介紹

OpenCv

BSD

www.opensource.org/licenses/bsd-license.html

在保留原來BSD協定聲明的前提下,随便怎麼用都行

EmguCv

GPL v3

http://www.gnu.org/licenses/gpl-3.0.txt

你的産品必須也使用GPL協定,開源且免費

商業授權

http://www.emgu.com/wiki/files/CommercialLicense.txt

給錢之後可以用于閉源的商業産品

AForge.net

LGPL v3

http://www.gnu.org/licenses/lgpl.html

如果不修改類庫源代碼,引用該類庫的産品可以閉源和(或)收費

以上三種類庫都可以用于開發商業産品,但是EmguCv需要付費;因為我隻是用來學習和研究,是以這些許可協定對我無所謂。不過鑒于我們身在中國,如果臉皮厚點,去他丫的許可協定。

下載下傳

可以很友善的下載下傳到這些類庫,下載下傳位址分别為:

下載下傳位址

http://sourceforge.net/projects/opencvlibrary/files/

http://www.emgu.com/wiki/index.php/Download_And_Installation

http://www.aforgenet.com/framework/downloads.html

安裝

安裝難易度

備注

比較容易

VC下使用需要重新編譯

容易

相信看這篇文章的人都不會被安裝困擾。

文檔資料 

總體評價

書籍

網站

文檔

示例

社群

中等

中英文

較多

中文論壇

有中文資料但不完整

英文

英文論壇

論壇人氣很差

OpenCv有一些中文資料,另外兩種的資料全是英文的;不過EmguCv建立在OpenCv的基礎上,大部分OpenCv的資料可以用于EmguCv;而AForge.net是原生的.net類庫,對GDI+有很多擴充,一些MSDN的資料可以借鑒。如果在查詞典的基礎上還看不懂英文文檔,基本上可以放棄使用這些類庫了。

易用性

易用性這玩意,主觀意志和個人能力對它影響很大,下面是我的看法:

比較差

OpenCv大多數功能都以C風格函數形式提供,少部分功能以C++類提供。注意:2.0版将更多的功能封裝成類了。

比較好

将OpenCv的絕大部分功能都包裝成了.net類、結構或者枚舉。不過文檔不全,還是得對照OpenCv的文檔去看才行。

純.net類庫,用起來很友善。

最近幾年一直用的是C# ,把C和C++忘記得差不多了,況且本來C/C++我就不太熟,是以對OpenCv的看法恐怕有偏見。

性能

這些類庫能做的事情很多,我選了最基礎的部分來進行性能測試,那就是将一幅彩色圖像轉換成灰階圖,然後再将灰階圖轉換成二值圖像。因為圖像處理大部分時間都用于記憶體讀寫及運算(特别是矩陣運算),是以這兩種操作有一定的代表性。

我分别用以下方式實作了圖像的灰階化及二值化:(1)C語言調用OpenCv庫;(2)C#調用AForge.net庫;(3)C#調用EmguCv庫;(4)C#中用P/INVOKE的形式調用OpenCv函數;(5)C#調用自己寫的灰階和二值化方法。

[轉]幾種圖像處理類庫的比較
[轉]幾種圖像處理類庫的比較

C語言調用OpenCv

#include "stdafx.h"

#include "cv.h"

#include "cxcore.h"

#include "highgui.h"

#include "winbase.h"

int _tmain(int argc, _TCHAR* argv[])

{

    //初始化圖像

    IplImage * pIplSource=cvLoadImage("E:\\xrwang\\ImageProcessLearn\\Debug\\wky_tms_2272x1704.jpg");

    IplImage * pIplGrayscale=cvCreateImage(cvSize(pIplSource->width,pIplSource->height),IPL_DEPTH_8U,1);

    IplImage * pIplThreshold=cvCreateImage(cvSize(pIplSource->width,pIplSource->height),IPL_DEPTH_8U,1);

    //執行灰階化和二值化,并輸出所用時間

    LARGE_INTEGER frequency,count1,count2,count3;

    double time1,time2;

    QueryPerformanceFrequency(&frequency);

    for(int i=0;i<10;i++)

    {

        QueryPerformanceCounter(&count1);

        cvCvtColor(pIplSource,pIplGrayscale,CV_BGR2GRAY);

        QueryPerformanceCounter(&count2);

        cvThreshold(pIplGrayscale,pIplThreshold,128,255,CV_THRESH_BINARY);

        QueryPerformanceCounter(&count3);

        time1=(double)1000.0*(count2.QuadPart-count1.QuadPart)/frequency.QuadPart;

        time2=(double)1000.0*(count3.QuadPart-count2.QuadPart)/frequency.QuadPart;

        printf("灰階:%g毫秒,二值化:%g毫秒\r\n",time1,time2);

    }

    //顯示圖像

    cvNamedWindow("grayscale",0);

    cvNamedWindow("threshold",0);

    cvResizeWindow("grayscale",600,480);

    cvResizeWindow("threshold",600,480);

    cvShowImage("grayscale",pIplGrayscale);

    cvShowImage("threshold",pIplThreshold);

    cvWaitKey(0);

    //銷毀對象

    cvDestroyAllWindows();

    cvReleaseImage(&pIplThreshold);

    cvReleaseImage(&pIplGrayscale);

    cvReleaseImage(&pIplSource);

    return 0;

}

[轉]幾種圖像處理類庫的比較
[轉]幾種圖像處理類庫的比較
[轉]幾種圖像處理類庫的比較

C#調用各種類庫處理圖像

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Drawing.Imaging;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.Diagnostics;

using System.Runtime.InteropServices;

using AForge.Imaging.Filters;

using Emgu.CV;

using Emgu.CV.Structure;

using Emgu.CV.CvEnum;

namespace ImageProcessLearn

    public partial class FormMain : Form

        public FormMain()

        {

            InitializeComponent();

        }

        //窗體加載時

        private void FormMain_Load(object sender, EventArgs e)

            //顯示原始圖像

            pbSource.Image = Image.FromFile("wky_tms_2272x1704.jpg");

        //使用標明的類庫處理圖像

        private void btnProcess_Click(object sender, EventArgs e)

            if (rbAForge.Checked)

            {

                ProcessImageWithAforge();

            }

            else if (rbEmgucv.Checked)

                ProcessImageWithEmgucv();

            else if (rbOpencv.Checked)

                ProcessImageWithOpencv();

            else if (rbOwnMethod.Checked)

                ProcessImageWithOwnMethod();

        /// <summary>

        /// 使用AForge.net處理圖像

        /// </summary>

        private void ProcessImageWithAforge()

            Stopwatch sw = new Stopwatch(); //計時器

            //灰階

            sw.Start();

            Grayscale grayscaleFilter = new Grayscale(0.299, 0.587, 0.114);

            Bitmap bitmapGrayscale = grayscaleFilter.Apply((Bitmap)pbSource.Image);

            sw.Stop();

            double timeGrayscale = sw.Elapsed.TotalMilliseconds;

            if (pbGrayscale.Image != null)

                pbGrayscale.Image.Dispose();

                pbGrayscale.Image = null;

            pbGrayscale.Image = bitmapGrayscale;

            //二值化

            sw.Reset();

            Threshold thresholdFilter = new Threshold(128);

            Bitmap bitmapThreshold = thresholdFilter.Apply(bitmapGrayscale);

            double timeThreshold = sw.Elapsed.TotalMilliseconds;

            if (pbThreshold.Image != null)

                pbThreshold.Image.Dispose();

                pbThreshold.Image = null;

            pbThreshold.Image = bitmapThreshold;

            //輸出所用時間

            txtResult.Text += string.Format("類庫:AForge.net,灰階:{0:F05}毫秒,二值化:{1:F05}毫秒\r\n", timeGrayscale, timeThreshold);

        /// 使用EmguCv處理圖像

        private void ProcessImageWithEmgucv()

            Image<Bgr, Byte> imageSource = new Image<Bgr, byte>((Bitmap)pbSource.Image);

            Image<Gray, Byte> imageGrayscale = imageSource.Convert<Gray, Byte>();

            pbGrayscale.Image = imageGrayscale.ToBitmap();

            Image<Gray, Byte> imageThreshold = imageGrayscale.ThresholdBinary(new Gray(128), new Gray(255));

            pbThreshold.Image = imageThreshold.ToBitmap();

            txtResult.Text += string.Format("類庫:EmguCv,灰階:{0:F05}毫秒,二值化:{1:F05}毫秒\r\n", timeGrayscale, timeThreshold);

        /// 使用Open Cv P/Invoke處理圖像

        unsafe private void ProcessImageWithOpencv()

            IntPtr ptrSource = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MIplImage)));

            Marshal.StructureToPtr(imageSource.MIplImage, ptrSource, true);

            IntPtr ptrGrayscale = CvInvoke.cvCreateImage(imageSource.Size, IPL_DEPTH.IPL_DEPTH_8U, 1);

            CvInvoke.cvCvtColor(ptrSource, ptrGrayscale, COLOR_CONVERSION.CV_BGR2GRAY);

            pbGrayscale.Image = ImageConverter.IplImagePointerToBitmap(ptrGrayscale);

            IntPtr ptrThreshold = CvInvoke.cvCreateImage(imageSource.Size, IPL_DEPTH.IPL_DEPTH_8U, 1);

            CvInvoke.cvThreshold(ptrGrayscale, ptrThreshold, 128d, 255d, THRESH.CV_THRESH_BINARY);

            pbThreshold.Image = ImageConverter.IplImagePointerToBitmap(ptrThreshold);

            //釋放資源

            //CvInvoke.cvReleaseImage(ref ptrThreshold);

            //CvInvoke.cvReleaseImage(ref ptrGrayscale);

            Marshal.FreeHGlobal(ptrSource);

            txtResult.Text += string.Format("類庫:OpenCv P/Invoke,灰階:{0:F05}毫秒,二值化:{1:F05}毫秒\r\n", timeGrayscale, timeThreshold);

        /// 使用自定義的方法處理圖像

        private void ProcessImageWithOwnMethod()

            Bitmap bitmapGrayscale = Grayscale((Bitmap)pbSource.Image);

            Bitmap bitmapThreshold = Threshold(bitmapGrayscale, 128);

            txtResult.Text += string.Format("類庫:自定義方法,灰階:{0:F05}毫秒,二值化:{1:F05}毫秒\r\n", timeGrayscale, timeThreshold);

        /// 将指定圖像轉換成灰階圖

        /// <param name="bitmapSource">源圖像支援3通道或者4通道圖像,支援Format24bppRgb、Format32bppRgb和Format32bppArgb這3種像素格式</param>

        /// <returns>傳回灰階圖,如果轉化失敗,傳回null。</returns>

        private Bitmap Grayscale(Bitmap bitmapSource)

            Bitmap bitmapGrayscale = null;

            if (bitmapSource != null && (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb || bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb))

                int width = bitmapSource.Width;

                int height = bitmapSource.Height;

                Rectangle rect = new Rectangle(0, 0, width, height);

                bitmapGrayscale = new Bitmap(width, height, PixelFormat.Format8bppIndexed);

                //設定調色闆

                ColorPalette palette = bitmapGrayscale.Palette;

                for (int i = 0; i < palette.Entries.Length; i++)

                    palette.Entries[i] = Color.FromArgb(255, i, i, i);

                bitmapGrayscale.Palette = palette;

                BitmapData dataSource = bitmapSource.LockBits(rect, ImageLockMode.ReadOnly, bitmapSource.PixelFormat);

                BitmapData dataGrayscale = bitmapGrayscale.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

                byte b, g, r;

                int strideSource = dataSource.Stride;

                int strideGrayscale = dataGrayscale.Stride;

                unsafe

                {

                    byte* ptrSource = (byte*)dataSource.Scan0.ToPointer();

                    byte* ptr1;

                    byte* ptrGrayscale = (byte*)dataGrayscale.Scan0.ToPointer();

                    byte* ptr2;

                    if (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb)

                    {

                        for (int row = 0; row < height; row++)

                        {

                            ptr1 = ptrSource + strideSource * row;

                            ptr2 = ptrGrayscale + strideGrayscale * row;

                            for (int col = 0; col < width; col++)

                            {

                                b = *ptr1;

                                ptr1++;

                                g = *ptr1;

                                r = *ptr1;

                                *ptr2 = (byte)(0.114 * b + 0.587 * g + 0.299 * r);

                                ptr2++;

                            }

                        }

                    }

                    else    //bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb

                            ptr1 = ptrSource + strideGrayscale * row;

                                ptr1 += 2;

                }

                bitmapGrayscale.UnlockBits(dataGrayscale);

                bitmapSource.UnlockBits(dataSource);

            return bitmapGrayscale;

        /// 将指定的灰階圖像轉換成二值圖像。如果某個像素的值大于等于閥值,該像素置為白色;否則置為黑色。

        /// 目前支援8bpp和16bpp兩種灰階圖像的轉換,對于8bpp,閥值介于0~255之間;對于16bpp,閥值介于0~65535之間。

        /// <param name="bitmapGrayscale">灰階圖像</param>

        /// <param name="thresholdValue">閥值</param>

        /// <returns>傳回轉換之後的二值圖像;如果轉換失敗,傳回null。</returns>

        private Bitmap Threshold(Bitmap bitmapGrayscale,int thresholdValue)

            Bitmap bitmapThreshold = null;

            if (bitmapGrayscale != null)

                int width = bitmapGrayscale.Width;

                int height = bitmapGrayscale.Height;

                PixelFormat pixelFormat = bitmapGrayscale.PixelFormat;

                if (pixelFormat == PixelFormat.Format8bppIndexed)

                    if (thresholdValue >= 0 && thresholdValue <= 255)

                        bitmapThreshold = (Bitmap)bitmapGrayscale.Clone();

                        byte white = 255;

                        byte black = 0;

                        BitmapData data = bitmapThreshold.LockBits(rect, ImageLockMode.ReadWrite, pixelFormat);

                        unsafe

                            byte* ptrStart = (byte*)data.Scan0.ToPointer();

                            byte* ptr1;

                            for (int row = 0; row < height; row++)

                                ptr1 = ptrStart + data.Stride * row;

                                for (int col = 0; col < width; col++)

                                {

                                    *ptr1 = (*ptr1 < thresholdValue) ? black : white;

                                    ptr1++;

                                }

                        bitmapThreshold.UnlockBits(data);

                else if (pixelFormat == PixelFormat.Format16bppGrayScale)

                    bitmapThreshold = (Bitmap)bitmapGrayscale.Clone();

                    UInt16 white = 65535;

                    UInt16 black = 0;

                    BitmapData data = bitmapThreshold.LockBits(rect, ImageLockMode.ReadWrite, pixelFormat);

                    unsafe

                        byte* ptrStart = (byte*)data.Scan0.ToPointer();

                        UInt16* ptr1;

                            ptr1 = (UInt16*)(ptrStart + data.Stride * row);

                                *ptr1 = (*ptr1 < thresholdValue) ? black : white;

                    bitmapThreshold.UnlockBits(data);

            return bitmapThreshold;

[轉]幾種圖像處理類庫的比較

     分别用上述5種形式處理10次,記錄下運作時間,去掉每種的最大和最小資料,然後計算平均值。結果如下所示(機關是毫秒):

語言

灰階化

二值化

性能排名

C

16.89721

7.807766

1

C#

Aforge.net

48.9403

25.32473

5

18.86898

13.74628

3

OpenCv(P/Invoke)

18.68938

10.0149

2

自定義處理方法

48.33593

21.46168

4

測試環境如下:CPU-奔騰4 2.4G,記憶體-512M,作業系統-Windows XP SP2,顯示卡-nVidia GForce4 64M,程序數-49,線程數-611,句柄數-13004,可用記憶體101M。

[轉]幾種圖像處理類庫的比較

毫無疑問,用C語言調用OpenCv的性能最好,兩種純.net的方式性能最差。

C語言調用OpenCv的處理效果如下所示:

[轉]幾種圖像處理類庫的比較

C#的處理效果如下:

[轉]幾種圖像處理類庫的比較

結論

将上面的内容彙總結果如下表所示:

GPL v3或商業授權

友善

文檔資料

很好

不好

綜上所述,我的選擇是使用EmguCv作為我的圖像處理類庫,在必要的時候用P/Invoke的形式調用沒有被封裝的OpenCv函數。

<b>本文轉自feisky部落格園部落格,原文連結:http://www.cnblogs.com/feisky/archive/2010/03/08/1681103.html,如需轉載請自行聯系原作者</b>

繼續閱讀