基本思想:使用c#调用c++的动态包,进行图像帧传入和处理,然后返回结果;这样就可以独立的写c++算法,使用c#进行前端界面和布局开发了
一、创建C#工程
然后简单测试一下工程
二、在C#中先把OpenCV调用起来
(1)在C#中打开搜索NuGet(库程序包管理器)
安装最新的OpenCVSharp4和OpenCVSharp4。runtime.win两个包
修改一下工程类型,选择X64,若没有在“配置管理器”中,新建一个x64平台即可
然后测试一下,C#可以正常读取图片和显示图片
测试图片代码
using System;
using OpenCvSharp;
namespace ConsoleAppDemo
{
class Program
{
static void Main(string[] args)
{
Mat source = new Mat(@"G:\\1.jpg", ImreadModes.Color);
Cv2.Resize(source, source, new OpenCvSharp.Size(480, 480), 0);
Cv2.ImShow("Demo", source);
Console.WriteLine("show image");
Cv2.WaitKey(0);
}
}
}
测试视频
using System;
using OpenCvSharp;
namespace ConsoleAppDemo
{
class Program
{
static void Main(string[] args)
{
VideoCapture cap = new VideoCapture("G:\\1.avi");
VideoWriter writer = new VideoWriter("G:\\save.avi", FourCC.DIVX, 20, new Size(cap.FrameWidth, cap.FrameHeight));
if (!cap.IsOpened())
{
Console.WriteLine("Open video failed!");
return;
}
Mat frame=new Mat();
while(true)
{
cap.Read(frame);
if (frame.Empty() )
break;
writer.Write(frame);
Cv2.ImShow("Demo", frame);
Cv2.WaitKey(1);
}
Console.WriteLine("show image");
}
}
}
三、打包一个c++静态包,对C#传过来的帧进行灰度处理并返回给C#保存
(1) 使用Visual Studio2019 配置opencv工程在这,就不具体叙述了,只贴一下创建工程的属性"空项目",没必要创建动态库类型的工程
main.cpp
#include "processImage.h"
int main()
{
cv::Mat img = cv::imread("G:\\1.jpg");
cv::Mat result;
cv::resize(img, img, cv::Size(480, 720));
int ok= bitwiseImage(img,result);
cv::imshow("demo",result);
cv::waitKey(0);
return 0;
}
processImage.h
#include
#include
#include
int bitwiseImage(cv::Mat img, cv::Mat& result);
processImage.cpp
#include "processImage.h"
int bitwiseImage(cv::Mat img, cv::Mat &result)
{
cv::Mat mask(img.rows, img.cols, CV_8UC3, cv::Scalar(255, 255, 255));
cv::bitwise_xor(img, mask, result);
return 0;
}
测试一下, 没问题
(2) 生成动态库lib文件 ,需要将 "生成-->属性--->配置类型 .exe"修改为.lib类型 ;然后点击左侧的工程名-->重新生成
然后在输出目录R中生成Bitwise.lib文件
(3) 然后在将"配置类型" 修改回原来的.exe;在"vc++目录"--->库目录---->添加 生成的.lib的目录(上述目录为--输出目录R) 同时链接器中填入Bitwise.lib
静态库包含
同时将本地的processImage.cpp代码全部注释掉,调用Bitwise.lib测试一下
调用成功,然后使用C#调用一下 (查询微软手册发现C#是不能调用lib的,所以只能改一下生成dll,方法是一样的,修改配置类型即可,如果现实中遇到第三方的lib 可以写个函数调用第三方的lib,然后在生成dll)
(4)、因为C#调用c++的动态包需要以unsigned char * 的数据格式传入,所以需要提前修改一下数据格式 逐在原基础上重新修改写一个例子
main.cpp
#include "processImage.h"
int main()
{
cv::Mat img = cv::imread("G:\\1.jpg");
cv::resize(img, img, cv::Size(480, 720));
unsigned char* src = img.data;
cv::Mat result = cv::Mat(img.rows, img.cols, CV_8UC3, src);
unsigned char* dest = result.data;
int ok = bitwiseImage(src, dest,img.rows,img.cols);
cv::imshow("demo", result);
cv::waitKey(0);
img.release();
result.release();
return 0;
}
processImage.h
#include
#include
#include
extern "C" _declspec(dllexport) int bitwiseImage(unsigned char* ImageBuffer, unsigned char* ImageResult, int imageWedth, int imageHeight);
//extern "C" _declspec(dllexport) 为了生成动态库之后,函数名字不要发生变化
processImage.cpp
#include "processImage.h"
int bitwiseImage(unsigned char* ImageBuffer, unsigned char* ImageResult, int imageWedth, int imageHeight)
{
cv::Mat result;
cv::Mat img = cv::Mat(imageHeight, imageWedth, CV_8UC3, ImageBuffer);
cv::Mat mask(img.rows, img.cols, CV_8UC3, cv::Scalar(255, 255, 255));
cv::bitwise_xor(img, mask, result);
int length = (int)(result.total() * result.elemSize());
unsigned char* buffer = new unsigned char[length];
memcpy(ImageResult, result.data, length * sizeof(unsigned char));
return 0;
}
重新生成一下demo.dll动态库
四、使用C#调用c++的静态包 【代码中含有mat--->bitmap--->byte 】【byte---->bitmap--->mat】
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using System.Runtime.InteropServices;
using System.Drawing;
using OpenCvSharp.Extensions;
using System.IO;
using System.IO.Compression;
using System.Drawing.Imaging;
namespace ConsoleApp1
{
class Program
{
[DllImport(@"C:\Users\Administrator\source\repos\demo\x64\Release\demo.dll")]
public static extern int bitwiseImage(byte[] ImageBuffer, byte[] ImageResult, int imageWedth, int imageHeight);
static void Main(string[] args)
{
VideoCapture cap = new VideoCapture("E:\\test.mp4");
VideoWriter writer = new VideoWriter("E:\\save.avi", FourCC.DIVX, 20, new OpenCvSharp.Size(cap.FrameWidth, cap.FrameHeight));
if (!cap.IsOpened())
{
Console.WriteLine("Open video failed!");
return;
}
Mat frame = new Mat();
while (true)
{
cap.Read(frame);
if (frame.Empty())
break;
Bitmap bmp = frame.ToBitmap();
byte[] source = GetBGRValues(bmp);
byte[] dest = source;
bitwiseImage(source, dest, bmp.Width, bmp.Height);
Bitmap bmpConvert = Byte2Bitmap(dest, bmp.Width, bmp.Height);
Mat mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmpConvert);
writer.Write(mat);
Cv2.ImShow("Demo", mat);
Cv2.WaitKey(1);
}
Console.WriteLine("show image");
}
public static byte[] GetBGRValues(Bitmap bmp)
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
var imgBytes = bmp.Height * rowBytes;
byte[] rgbValues = new byte[imgBytes];
IntPtr ptr = bmpData.Scan0;
for (var i = 0; i < bmp.Height; i++)
{
Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
return rgbValues;
}
public static Bitmap Byte2Bitmap(Byte[] data, int width, int height)
{
Bitmap image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
BitmapData bmData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);
IntPtr ptr = bmData.Scan0;
for (int i = 0; i < image.Height; i++)
{
Marshal.Copy(data, i * image.Width * 3, ptr, image.Width * 3);
ptr = (IntPtr)(ptr.ToInt64() + bmData.Stride);
}
image.UnlockBits(bmData);
return image;
}
}
}
结果图