最近要做一个程序,程序主体用C#写的,要使用摄像头。
最先考虑的是用C++调用openCV写成dll,给C#调用。后来感觉有点复杂就转而使用EmguCV了。
但是使用中发现两个bug,其一是无法得到摄像头的名字(这个可以通过C#本身枚举系统设备实现,但是这之后还是遇到一个bug,摄像头名字跟编号不对应,有两个摄像头的话,用VidioCapture打开后是另外一个),其二是通过使用SetCaptureProperty方法修改摄像头参数时无效,网上搜了半天竟然没有人遇到类似问题,也无无从解决,真是莫名其妙。
又考虑到后续还有很多图像处理工作要用OpenCV,在C#里通过EmguCV调用效率恐怕也难以保证。
于是下定决心,回过头来再研究一下C++与C#混合编程的方法。
基本思路是,在C++中调用openCV读取摄像头的数据,传回给C#显示在PictureBox中。具体实现方法是通过将C#中的IntrPtr类型传入C++的函数,在其中赋值。
测试如下,在C++中
extern "C" __declspec(dllexport) void Test2(unsigned char* x) {
for (int i = 0; i < 10; i++)
x[i] = i;
}
在C#中,声明外部函数
[DllImport("C:\\Users\\boron\\source\\repos\\CVUtilityDll\\debug\\CVUtilityDll.dll"
, CallingConvention = CallingConvention.Cdecl)]
static extern int Test2(IntPtr x);
在函数调用,
byte[] data = new byte[10];
IntPtr x = Marshal.AllocHGlobal(10);
Test2(x);
Marshal.Copy(x, data, 0, 10);
IntPtr x = Marshal.AllocHGlobal(10); 此句中的参数是10的话程序运行一切正常,而用更小的数,比如1,则程序运行有时正常,有时会出错,应该是因为内存使用上的冲突。
回到正题,调用摄像头时,在C++中创建三个函数,分别是启用相机,释放相机和获取一桢图像。
#define EXTERN_DLL extern "C" __declspec(dllexport)
// 全局变量
VideoCapture capture;
Mat I;
/*
打开相机,设置分辨率
*/
EXTERN_DLL void InitializeCamera() {
capture.open(0, CAP_DSHOW);
capture.set(CAP_PROP_FRAME_WIDTH, 640);
capture.set(CAP_PROP_FRAME_HEIGHT, 480);
}
/*
释放相机和图像矩阵
*/
EXTERN_DLL void ReleaseCamera() {
capture.release();
I.release();
}
/*
从相机中获取一桢图像,把数组传给外部的指针变量
*/
EXTERN_DLL void GetOneFrame(unsigned char* x) {
capture.read(I);
if (I.empty()) return;
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
uchar* p;
for (int i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for (int j = 0; j < nCols; ++j)
{
x[i * nCols + j] = p[j];
}
}
}
在C#中声明外部函数
[DllImport("C:\\Users\\boron\\source\\repos\\CVUtilityDll\\debug\\CVUtilityDll.dll"
, CallingConvention = CallingConvention.Cdecl)]
static extern void InitializeCamera();
[DllImport("C:\\Users\\boron\\source\\repos\\CVUtilityDll\\debug\\CVUtilityDll.dll"
, CallingConvention = CallingConvention.Cdecl)]
static extern void ReleaseCamera();
[DllImport("C:\\Users\\boron\\source\\repos\\CVUtilityDll\\debug\\CVUtilityDll.dll"
, CallingConvention = CallingConvention.Cdecl)]
static extern void GetOneFrame(IntPtr a);
创建一个PictureBox,用来显示传回的图片,在一个函数中创建BitMap对象用来接收数据,不断调用GetOneFrame来获取图像,并显示在PictureBox上
InitializeCamera();
Bitmap bmp = new Bitmap(640, 480, PixelFormat.Format24bppRgb);
while (Visible)
{
BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, 640, 480),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
GetOneFrame(bmd.Scan0);
bmp.UnlockBits(bmd);
pictureBox1.Image = bmp;
Application.DoEvents();
}
ReleaseCamera();
至于如何设置环境不再赘述,可直接参考我前面写的文章。相关代码下载链接如下
https://download.csdn.net/download/boron1987/15120306