How to Create an OpenCV C++ DLL and To be Used in C# Windows Form Project?
如果你跟我一樣:
1. 有點厭倦使用EmguCV, 理由很簡單: 因為當你google 關鍵字OpenCV找到的範例絕大部分應該是C++為大宗,
所以你得即時翻譯成EmguCV語法, 無法享受Copy-Paste的快樂;
雖然話說習慣成自然, 但是久沒有使用, 時間是會沖淡一切….
2. 當你使用 C++, 又開始懷念C#拉UI的快樂時, 你不得不考慮兩者加在一起使用….
3. 綜合1.2兩點, 希望可以在C# windows form快快樂樂拉UI, 同時簡簡單單將不同參數傳給OpenCV,
最重要的是可以快速複製貼上OpenCV的範例程式….哈哈!!!
4. 當OpenCV C++函式庫有變動時, 只需要編譯整個方案(solution)就可以全部一次更新完成
所以接下來要寫一個簡單範例,
1. 在C++ DLL專案, 載入一張圖片並顯示
2. 在C# Windows Form專案, 加入C++ DLL參考, 將檔案名稱傳給該DLL函式
===================================================================
ImageProcLib
選擇DLL
按下<完成>後如下
ImageProcLib.cpp 空空如也~
加入現有屬性工作表
接下來新增 ImageProcLib.hpp
名稱: imageProcLib.h
先宣告ImageProcLib.h
#include <wchar.h>#pragma once
namespace CVision{public ref class ImageProc
{ public: ImageProc(){};bool imread(wchar_t *filename);
bool showImages();};
}
回到ImageProcLib.cpp, 實作ImageProcLib.h
// ImageProcLib.cpp : 定義 DLL 應用程式的匯出函式。//#include "stdafx.h"
#include "ImageProcLib.h"
using namespace std;
using namespace cv;
namespace CVision{string filename;
Mat img;
bool ImageProc::imread(wchar_t* txt)
{ wstring ws(txt);
filename = WstringToString(ws);
img = cv::imread( filename );
if( !img.data) return false; else return true;}
std::string ImageProc::WstringToString(const std::wstring str) { unsigned len = str.size() * 4; setlocale(LC_CTYPE, "cht");char *p = new char[len];
wcstombs(p,str.c_str(),len);
std::string str1(p);
delete[] p; return str1;}
void ImageProc::showImage() {namedWindow( filename, CV_WINDOW_NORMAL );
imshow(filename, img );
}
}
組態管理員->選擇x64平台
下拉選擇支援/clr, 讓C#可以呼叫C++ DLL
若成功編譯的話, 會在x64\Release資料夾輸出ImageProcLib.dll
==========================================================
準備撰寫C# windows form, 首先在原來ImageProLib Solution加入新專案(C# windows form project)
在C# GUI加入一個menuStrip元件
準備加入<參考>
選擇剛才C++製作的DLL元件
在C# windows form加入
using CVision;
記得勾選: 允許Unsafe程式碼
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using CVision;namespace ImageProcLibClient{public partial class Form1 : Form
{ ImageProc imgProc = new ImageProc(); public Form1() {InitializeComponent();
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{using (OpenFileDialog ofd = new OpenFileDialog())
{ if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { unsafe {fixed (char* filename = ofd.FileName)
{imgProc.imread(filename);
imgProc.showImage();
}
}
}
}
}
}
}
選擇ImageProcLibClient為起始專案, 並編譯執行
ImageProcLibClient c# Window Form專案也必須是x64平台, 才能呼叫c++ x64 imageProcLib函式庫
測試載入一張圖來試試看囉
=================================================================
更新 imageProcLib.h
#include <wchar.h>#include <opencv2\highgui\highgui.hpp>#pragma once
using namespace std;
namespace CVision{public ref class ImageProc
{ public: ImageProc(){}; bool imread(string filename);bool imread(wchar_t *filename);
std::string WstringToString(const std::wstring str); void showImage();};
}
更新 imageProcLib.cpp
// ImageProcLib.cpp : 定義 DLL 應用程式的匯出函式。//#include "stdafx.h"
#include "ImageProcLib.h"
using namespace std;
using namespace cv;
namespace CVision{string filename;
Mat img;
bool ImageProc::imread(string filename) {const char * file = filename.c_str();
img = cv::imread( file );
if( !img.data) return false; else return true;}
bool ImageProc::imread(wchar_t* txt)
{ wstring ws(txt);
filename = WstringToString(ws);
img = cv::imread( filename );
if( !img.data) return false; else return true;}
std::string ImageProc::WstringToString(const std::wstring str) { unsigned len = str.size() * 4; setlocale(LC_CTYPE, "cht");char *p = new char[len];
wcstombs(p,str.c_str(),len);
std::string str1(p);
delete[] p; return str1;}
void ImageProc::showImage() {namedWindow( filename, CV_WINDOW_NORMAL );
imshow(filename, img );
}
}
=================================================================
接下來, 加入一個簡單的形態學運算
將結果的視窗名稱命名 std::string filenameDilate = s + " Dilate"
How to concatenate two strings in C++?
接下來測試Copy-Paste的功能
google “opencv dilate” 輕鬆找到一個範例[4]來測試
先更新 imageProcLib.h
只是多宣告一行 ImageDilate();
#include <wchar.h>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>#pragma once
using namespace std;
namespace CVision{public ref class ImageProc
{ public: ImageProc(){}; bool imread(string filename);bool imread(wchar_t *filename);
std::string WstringToString(const std::wstring str); void ImageProc::ImageDilate(); void showImage();};
}
更新 imageProcLib.cpp
幾乎是Copy Paste改幾個字就打完收工
// ImageProcLib.cpp : 定義 DLL 應用程式的匯出函式。//#include "stdafx.h"
#include "ImageProcLib.h"
using namespace std;
using namespace cv;
namespace CVision{string filename;
Mat img;
Mat imgDst;
bool ImageProc::imread(string filename) {const char * file = filename.c_str();
img = cv::imread( file );
if( !img.data) return false; else return true;}
bool ImageProc::imread(wchar_t* txt)
{ wstring ws(txt);
filename = WstringToString(ws);
img = cv::imread( filename );
if( !img.data) return false; else return true;}
std::string ImageProc::WstringToString(const std::wstring str) { unsigned len = str.size() * 4; setlocale(LC_CTYPE, "cht");char *p = new char[len];
wcstombs(p,str.c_str(),len);
std::string str1(p);
delete[] p; return str1;}
void ImageProc::ImageDilate() { // Create a structuring element int erosion_size = 6; Mat element = getStructuringElement(cv::MORPH_CROSS ,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size) );
// Apply erosion or dilation on the image erode(img,imgDst,element); // dilate(image,dst,element); }
void ImageProc::showImage() {namedWindow( filename, CV_WINDOW_NORMAL );
imshow(filename, img );
std::string filenameDilate = filename + " Dilate"; //concatenation easy!
namedWindow( filenameDilate, CV_WINDOW_NORMAL );
imshow( filenameDilate, imgDst );
}
}
更新完C++ DLL, C# Windows Form會自動偵測到ImageProcLib.dll異動
C# 額外呼叫 imgProc.ImageDilate(), 其他不變(夠簡單吧!)
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{using (OpenFileDialog ofd = new OpenFileDialog())
{ if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { unsafe {fixed (char* filename = ofd.FileName)
{imgProc.imread(filename);
imgProc.ImageDilate();
imgProc.showImage();
}
}
}
}
}
接著按一下編譯, 將C#專案再次編譯就可以
測試結果如下:
每開一張圖, 就會以filename開新的視窗, 所以意外地可以同時看多張跑出來的結果, 真是太棒了!!!
===============================================================
如果要C#對erosion size參數, 該如何做到?
更新 imageProcLib.h
void ImageProc::ImageDilate(int erosion_size);
更新 imageProcLib.cpp
void ImageProc::ImageDilate(int erosion_size)
{ // Create a structuring elementMat element = getStructuringElement(cv::MORPH_CROSS ,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size) );
// Apply erosion or dilation on the image erode(img,imgDst,element); // dilate(image,dst,element); }
c# windows form更新如下
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using CVision;namespace ImageProcLibClient{public partial class Form1 : Form
{ ImageProc imgProc = new ImageProc(); public Form1() {InitializeComponent();
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{using (OpenFileDialog ofd = new OpenFileDialog())
{ if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { unsafe {fixed (char* filename = ofd.FileName)
{imgProc.imread(filename);
int erosionSize = Convert.ToInt16(numericUpDown1.Value);imgProc.ImageDilate(erosionSize);
imgProc.showImage();
}
}
}
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{ int erosionSize = Convert.ToInt16(numericUpDown1.Value);imgProc.ImageDilate(erosionSize);
imgProc.showImage();
}
}
}
測試結果如下
很明顯這樣可以輕輕鬆鬆不同Erosion Size參數大小,
而不用像以前在c++ Console模式下,
寫一堆有的沒有程式碼或是每次更動參數後重新Compile
參考資料:
1. Calling OpenCV C++ Code in C# Windows Form Applications
2. char* pointer from string in C#

讚讚讚
哈哈! 很高興有幫助到你~ 我自己順便複習一次~~~~
不好意思 你一開始在建立的時後 沒有 include相關 oipencv 函式庫的樣子 .h裡面定義bool showImages(); .cpp裡面怎麼變成void ImageProc::showImage() ??
.cpp 中ImageProc::showImage() 是指在ImageProc類別下的showImage實作內容, .h因為已經寫在ImageProc類別中,所以不需要特別強調ImageProc,只需要列出需要定義的方法清單
VS2010OpenCV249X64Release ˋˋ找不到怎麼辦~QQ
VS2010OpenCV249X64Release是屬性管理設定檔案,管理OpenCV include和library等設定,如果你會設定編譯環境就可以忽略這個設定檔案。
我可以用VS 2015+ OpenCV 3.0來做上面的工作嗎?
沒問題, 可以的
請問要如何使用C++將cuda版本的opencv包成DLL呢? 我按照您文章方法做,可以建立出DLL,但是C#無法呼叫。 註:我使用CPU版本的opencv(非cuda版)所包成的DLL,確定可以在C#上使用了,但cuda版的都失敗。 謝謝。
Error message?
版主 謝謝回覆。 Error Message :DLL 找不到指定的模組 (異常來自HRESULT:0x8007007E)。 thanks
找不到對應的DLL, 應該是系統變數沒有設定正確 或是你可以複製X64資料夾至你的應用程式根目錄下