摘要: 以opencv驅動攝像頭, 並處理圖像, 將結果傳遞給C#, 不涉及圖像數據Mat的傳遞. 工程主體爲C#, 封裝opencv爲dll, 進行調用. 開發環境用VS2019
系統簡化: 爲了能夠更具體地描述openCV C++與C#混合編程, 對系統進行簡化. 目標是用簡單的C#窗體程序, 讀取攝像頭, 並顯示原始圖像, 算法處理後, 顯示結果圖像. 不涉及圖像數據傳遞, 也就無法在窗體中重繪圖像, 顯示窗口仍用opencv的HighGUI模塊.
openCV 程序設計
全局變量
由於不傳遞圖像Mat, openCV程序應在作用域內處理圖像, 就需要用到全局變量. 主要定義有:
- VideoCapture
- frame
攝像頭初始化
void initCamera(int cameraIndex, double frameWidth, double frameHeight, double fps)
{
camera.open(cameraIndex);
if(!camera.isOpened())
{
cerr << "Couldn't open camera." << endl;
}
camera.set(cv::CAP_PROP_FRAME_WIDTH, frameWidth); // 寬度
camera.set(cv::CAP_PROP_FRAME_HEIGHT, frameHeight); // 高度
camera.set(cv::CAP_PROP_FPS, fps); // 幀率
}
攝像頭高級設置(注意備份, 謹慎更改
)
void setCameraParams(double brightness, double contrast, double saturation, double hue, double gain, double exposure)
{
//打印攝像頭參數
cout << "\n參數修改前:" << endl;
printf("brightness = %.2f\n", camera.get(cv::CAP_PROP_BRIGHTNESS));
printf("contrast = %.2f\n", camera.get(cv::CAP_PROP_CONTRAST));
printf("saturation = %.2f\n", camera.get(cv::CAP_PROP_SATURATION));
printf("hue = %.2f\n", camera.get(cv::CAP_PROP_HUE));
printf("gain = %.2f\n", camera.get(cv::CAP_PROP_GAIN));
printf("exposure = %.2f\n", camera.get(cv::CAP_PROP_EXPOSURE));
// 修改攝像頭參數
camera.set(cv::CAP_PROP_BRIGHTNESS, brightness); // 亮度
camera.set(cv::CAP_PROP_CONTRAST, contrast); // 對比度
camera.set(cv::CAP_PROP_SATURATION, saturation); // 飽和度
camera.set(cv::CAP_PROP_HUE, hue); // 色調
camera.set(cv::CAP_PROP_GAIN, gain); // 增益
camera.set(cv::CAP_PROP_EXPOSURE, exposure); // 曝光度
//打印攝像頭參數
cout << "\n參數修改後:" << endl;
printf("brightness = %.2f\n", camera.get(cv::CAP_PROP_BRIGHTNESS));
printf("contrast = %.2f\n", camera.get(cv::CAP_PROP_CONTRAST));
printf("saturation = %.2f\n", camera.get(cv::CAP_PROP_SATURATION));
printf("hue = %.2f\n", camera.get(cv::CAP_PROP_HUE));
printf("gain = %.2f\n", camera.get(cv::CAP_PROP_GAIN));
printf("exposure = %.2f\n", camera.get(cv::CAP_PROP_EXPOSURE));
}
讀取幀並顯示
void readFrame()
{
camera >> frame;
cv::imshow("camera", frame);
cv::waitKey(1);
}
圖像處理
void processingImage()
{
cv::cvtColor(frame, frame, cv::COLOR_BGR2GRAY);
cv::Canny(frame, frame, 100, 200);
cv::imshow("processor", frame);
cv::waitKey(1);
}
關閉顯示
void closeCamera(string winName)
{
if(winName == "camera")
cv::destroyWindow("camera");
else if(winName == "processor")
cv::destroyWindow("processor");
else
;// do nothing
}
頭文件聲明
由於是導出爲dll, 需要特別的語法支持
extern "C" __declspec(dllexport) void initCamera(int cameraIndex, double frameWidth, double frameHeight, double fps);
extern "C" __declspec(dllexport) void readFrame();
extern "C" __declspec(dllexport) void processingImage();
extern "C" __declspec(dllexport) void closeCamera(string winName);
生成dll
- 更改項目屬性, 設置
配置類型
爲 dll, 如圖. 然後右鍵生成
或Ctrl+Shift+B
, 即可在輸出目錄看到dll文件 - 由於該dll依賴於opencv庫, 所以還應包含opencv的dll. 如果是獨立模塊, 則需包括所有調用的; 如果是集合成了world文件, 就只需包含"opencv_world411.dll"即可. 參見博客: 封裝opencv的函數成dll,獨立調用
C#窗體程序
建立窗體項目
注意不要選錯類型, 具體步驟可參考微軟官方教程: Create a Windows Forms app in Visual Studio with C#
導入dll
如同導出聲明一樣, 需要特定的導入格式
// 初始化攝像頭
[DllImport("ImageTracker.dll", EntryPoint = "initCamera", CallingConvention = CallingConvention.Cdecl)]
public static extern void initCamera(int cameraIndex, double frameWidth, double frameHeight, double fps);
有博客提到, 用reference的方法添加dll, 未嘗試. Visual Studio 在 C# 項目添加動態鏈接庫 dll
調用dll
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Camera
{
public partial class FormCamera : Form
{
// 初始化攝像頭
[DllImport("ImageProcessing.dll", EntryPoint = "initCamera", CallingConvention = CallingConvention.Cdecl)]
public static extern void initCamera(int cameraIndex, double frameWidth, double frameHeight, double fps);
// 讀取圖像並顯示
[DllImport("ImageProcessing.dll", EntryPoint = "readFrame", CallingConvention = CallingConvention.Cdecl)]
public static extern void readFrame();
// 處理圖像並顯示
[DllImport("ImageProcessing.dll", EntryPoint = "processingImage", CallingConvention = CallingConvention.Cdecl)]
public static extern void processingImage();
// 關閉窗口
[DllImport("ImageProcessing.dll", EntryPoint = "closeCamera", CallingConvention = CallingConvention.Cdecl)]
public static extern void closeCamera(string winName);
public FormCamera()
{
InitializeComponent();
ImageProcessing();
}
private void ImageProcessing()
{
initCamera(0, -1, -1, -1);
for(int i = 0; i <= 10000; i++)
{
readFrame();
processingImage();
}
closeCamera("camera");
closeCamera("processor");
}
}
}
其中遇到的問題
- dll模塊不存在: 將兩個dll都放入C#的生成目錄. 參見
生成dll
小節的參考文獻: 封裝opencv的函數成dll,獨立調用 - dll格式無效: 將調試平臺設置爲x64, 因爲openCV只支持64位. 參見博客: 試圖加載格式不正確的程序。 (異常來自 HRESULT:0x8007000B)
- HighGUI窗體無響應: 設置爲主線程, 即在
imshow
後追加waitKey(1)
, 參見博客: OpenCV使用cv::imshow在子線程中更新圖片不刷新