作者:王先榮
前言
近期需要做一些圖像處理方面的學習和研究,首要任務就是選擇一套合适的圖像處理類庫。目前較知名且功能完善的圖像處理類庫有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>