C++/CLR托管模式下接收C#端传递的图像进行处理和显示及不同变量类型转换和值的互传

很多时候做工业视觉的都喜欢用C#+halcon,纯粹只想做做应用的话,采用该模式搞下去也没啥问题,但是如果想提升一下自己对图像算法的底层开发能力,一般都还是会选择使用C++搭配Opencv进行开发,对于喜欢用C#开发上位机,结合C++做算法的话,那么C++的托管模式是一个不错的选择。

首先说下我为什么要用C++托管模式:

1、托管C++可以使用非托管内容;

2、托管模式下可以断点调试,非托管模式本人暂未找到可以断点调试的方法(如果喜欢非托管可以使用DebugView);

3、在调用c++的dll的时候,就像使用C#导出的dll一样去使用,不需要写dllexport和dllimport之类的玩意;

缺点:变量类型转换会稍微麻烦点


本次C#/C++混合模式测试思路为:

1、C#端作为图像源的获取端,便于测试直接使用halcon进行图像读取和传递;

2、C++端作为图像的接收端,接收C#传过来的图像;

3、图像显示:将C#端的panel控件的句柄传递到C++端,C++端通过halcon进行处理和显示;

下面具体介绍托管模式下C#端和C++端的详细配置和代码测试例程:

C++端配置

1、新建空白工程,添加一个需要供C#端进行调用的类,将配置类型设置为“动态库(.dll)”

2、在配置属性里,将公共语言运行时支持设置为clr模式,见下图

3、在配置属性里,选择C/C++下语言,找到“符合模式”一项,设置为“否(permissive)”

如果设置为“是(permissive-)”的话,编译的时候会出现以下错误

 4、添加类库mscorlib和System,添加方式同C#一样

 5、如果需要在断点的时候查看变量值的变化,需要进行一下操作

C++端设置:将调试器类型设置为混合模式

 C#端设置:勾选“启用本地代码调试”

 6、C++端测试代码

C++端采用opencv和halcon的混用模式,可根据具体情况去选择,halcon和Opencv的配置可自行配置

6.1 在类的前面加上ref

 6.2 .h文件

static HalconCpp::HWindow g_hwin;
public ref class MyAlgorithmPro
	{
	public:
		MyAlgorithmPro();
		~MyAlgorithmPro(void);

	public:
		void initWinHandle(long handle, int width, int height);//初始化窗口句柄
		void getImageByHalcon(unsigned char* R, unsigned char* G, unsigned char* B, int width, int height, int channel);//halcon取图
		void getImageByOpencv(unsigned char* R, unsigned char* G, unsigned char* B, int width, int height, int channel);//opencv取图
	};

6.3 .cpp文件

#include "MyAlgorithmPro.h"


MyAlgorithmPro::MyAlgorithmPro()
{
}


MyAlgorithmPro::~MyAlgorithmPro(void)
{
}


void MyAlgorithmPro::initWinHandle(long handle, int width, int height)
{
    g_hwin = HalconCpp::HWindow(0, 0, width, height, handle, "visible", "");
}

void MyAlgorithmPro::getImageByHalcon(unsigned char* R, unsigned char* G, unsigned char* B, 
    int width, int height, int channel)
{
    HObject img;

    switch (channel)
    {
    case 1:
        GenImage1(&img, "byte", width, height, (Hlong)R);
        break;
    case 3:
        GenImage3(&img, "byte", width, height, (Hlong)R, (Hlong)G, (Hlong)B);
        break;
    default:
        break;
    }
    

    g_hwin.SetPart(0, 0, height, width);
    g_hwin.DispObj(img);
    g_hwin.SetColor("red");
    g_hwin.DispCircle( height*0.5, width * 0.5, 50);
}

void MyAlgorithmPro::getImageByOpencv(unsigned char* R, unsigned char* G, unsigned char* B, 
    int width, int height, int channel)
{
    //cv::merge()
    cv::Mat img, rimg, gimg, bimg;
    std::vector<cv::Mat> vc;
    switch (channel)
    {
    case 1:
        img = cv::Mat(height, width, CV_8UC1, R);
        cv::circle(img, cv::Point(width*0.5, height*0.5), 200, cv::Scalar(100), -1);
        break;
    case 3:
        img = cv::Mat(height, width, CV_8UC3);
        rimg = cv::Mat(height, width, CV_8UC1, R);
        gimg = cv::Mat(height, width, CV_8UC1, G);
        bimg = cv::Mat(height, width, CV_8UC1, B);
        //halcon是RGB模式,opencv是BGR模式
        vc.push_back(bimg);
        vc.push_back(gimg);
        vc.push_back(rimg);
        cv::merge(vc, img);
        break;
    default:
        break;
    }

    HObject dstimg = MyClass::Mat2Hobject(img);
    g_hwin.SetPart(0, 0, height, width);
    g_hwin.DispObj(dstimg);
    g_hwin.SetColor("red");
    g_hwin.DispCircle(height * 0.5, width * 0.5, 50);
}

7、编写完代码后,会生成一个dll,记得将C++端和C#的生成目录都设置为同一个目录,否则是无法进行断点调试的

C#端配置

1、新建一个窗体应用程序,添加一个panel控件和按钮控件

2、在应用处添加C++端导出得dll

3、具体代码见下

 public partial class Form1 : Form
    {
        MyAlgorithmPro alg = new MyAlgorithmPro();

        public Form1()
        {
            InitializeComponent();
            initialWinHandle();
        }


        private void initialWinHandle()
        {
            alg.initWinHandle((int)panel1.Handle, panel1.Width, panel1.Height);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string curFilename = "";
            OpenFileDialog opndlg = new OpenFileDialog();
            opndlg.Filter = "所有图像文件|*.bmp;*.pcx;*.png;*.jpg;*.gif";
            opndlg.Title = "打开图片文件";
            if(opndlg.ShowDialog()==DialogResult.OK)
            {
                curFilename = opndlg.FileName;
            }


            HObject img = null;
            HTuple pointers = new HTuple(), type = new HTuple(), width = new HTuple(), height = new HTuple();
            HTuple redPointers = new HTuple(), greenPointers = new HTuple(), bluePointers = new HTuple();
            HTuple channels = new HTuple();
            HOperatorSet.GenEmptyObj(out img);
            HOperatorSet.ReadImage(out img, curFilename);
            HOperatorSet.CountChannels(img, out channels);
            if(channels.I==1)
            {
                HOperatorSet.GetImagePointer1(img, out pointers, out type, out width, out height);
                
                unsafe
                {
                    byte* ptr = (byte*)pointers.L;
                    alg.getImageByHalcon(ptr, ptr, ptr, width, height, channels);
                    //alg.getImageByOpencv(ptr, ptr, ptr, width, height, channels);
                }
            }
            else if(channels.I==3)
            {
                HOperatorSet.GetImagePointer3(img, out redPointers, out greenPointers, out bluePointers, out type, out width, out height);

                unsafe
                {
                    byte* bptr = (byte*)(redPointers.L);
                    byte* R = (byte*)redPointers.L;
                    byte* G = (byte*)greenPointers.L;
                    byte* B = (byte*)bluePointers.L;
                    alg.getImageByHalcon(R, G, B, width, height, channels);
                    //alg.getImageByOpencv(R, G, B, width, height, channels);
                }
            }
        }

    }

4、显示处理后的灰度图像

 5、显示处理后的彩色图像

结束语:以上混编模式适合喜欢折腾的人,其实用QT就没上面的事了!

当然除了传图片,肯定也少不了要进行变量的互传,下面再介绍下托管模式下各类型变量的互传:


C++/clr托管模式下同C#端进行各类型变量的转换和互传

托管模式和非托管模式的变量类型的传递还是有很大区别的,本文暂不介绍非托管的内容,有兴趣的可以自行百度。

C#传递各类型变量到C++端

除了字符串之外,其它类型的变量按照正常的变量类型进行传递即可,但C++端需要在引用处添加System.dll

比如传递int、double、float、bool类的时候,C++端的测试代码如下:

.h文件:

int getMyInt(int n);//接收和返回int变量
double getMyDouble(double n);//接收和返回double变量
float getMyFloat(float n);//接收和返回float变量
bool getMyBool(bool n);//接收和返回bool变量

.cpp文件:

int MyAlgorithmPro::getMyInt(int n){    return n;}

double MyAlgorithmPro::getMyDouble(double n){    return n;}

float MyAlgorithmPro::getMyFloat(float n){    return n;}

bool MyAlgorithmPro::getMyBool(bool n){    return n;}

C#端测试代码:

 double t = alg.getMyDouble(12.3);
 this.textBox1.Text = t.ToString();

 int t = alg.getMyInt(123);
 this.textBox1.Text = t.ToString();

 float t = alg.getMyFloat(12.2f);
 this.textBox1.Text = t.ToString();

 bool b = alg.getMyBool(false);
 this.textBox1.Text = b.ToString();

但是在传字符串的时候需要稍作修改

C++端:

#include <msclr\marshal_cppstd.h>
System::String^ MyAlgorithmPro::getMyString(System::String^ str)
{
    string ss = msclr::interop::marshal_as<std::string>(str);
    System::String^ res = gcnew String(ss.c_str());

    return res;
}

参考链接:system::String ^转std::string - 懒人福利 - 博客园


C#和C++互传各类型变量的数组(其实就是用于计算结果回传)

C++端测试代码:

.h文件:

void getMyStringArray(cli::array<System::String^, 1>^ res);//字符串数组传参
void getMyDoubleArray(cli::array<System::Double^, 1>^ res);//double数组传参
void getMyIntArray(cli::array<System::Int32^, 1>^ res);//int数组传参
void getMyFloatArray(cli::array<System::Single^, 1>^ res);//Single(单精度)数组传参

.cpp文件:

void MyAlgorithmPro::getMyDoubleArray(cli::array<System::Double^, 1>^ res)
{
    double t = 9.99;
    res[0] = t;
    res[1] = 2.2;
}

void MyAlgorithmPro::getMyIntArray(cli::array<System::Int32^, 1>^ res)
{
    res[0] = 12;
    res[1] = 13;
    res[2] = 14;
}

void MyAlgorithmPro::getMyFloatArray(cli::array<System::Single^, 1>^ res)
{
    float t1 = 11.1;
    float t2 = 11.2;
    //res[0] = t1;
    res[0] = (float)22.3;
    res[1] = t2;
}

void MyAlgorithmPro::getMyStringArray(cli::array<System::String^, 1>^ res)
{
    string ss = "123456";
    //res[0]= "111";
    res[0] = gcnew String(ss.c_str());
    res[1] = "222";
}

C#端测试代码:除了字符串之外,C#端的数组均为ValueType类型,只需要再做一个显示转换即可

 private void button2_Click(object sender, EventArgs e)   
{       
    string[] tt = { "1", "2" };
    alg.getMyStringArray(tt);
    this.textBox1.Text = tt[0].ToString();
}

private void button3_Click(object sender, EventArgs e)
{
     ValueType[] v = { 1, 2, 3 };
     alg.getMyDoubleArray(v);
     double t = (double)v[0];
     this.textBox1.Text =t.ToString();
}

private void button4_Click(object sender, EventArgs e)
{
      ValueType[] v = { 0, 0, 0 };
      alg.getMyIntArray(v);
      int v2 = (int)v[0];
      this.textBox1.Text = v2.ToString();
}

private void button5_Click(object sender, EventArgs e)
{
      ValueType[] v = { 0, 0, 0 };
      alg.getMyFloatArray(v);
      float t = (float)v[0];
      this.textBox1.Text = t.ToString();
}

具体测试效果可自行测试,不做具体展示

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
如果你想在C++/CLI中使用C# Task,并且还需要处理带界面的代码,你可以使用以下步骤: 1. 在C++/CLI项目中添加对C#项目的引用。 2. 创建一个C++/CLI的Windows窗体应用程序,并在窗体中包含一个按钮。 3. 在按钮的Click事件处理程序中,使用C# Task来执行异步操作。 以下是一个示例: C#代码: ```csharp using System.Threading.Tasks; namespace MyNamespace { public class MyClass { public static async Task<int> MyMethodAsync(int arg1, int arg2) { await Task.Delay(1000); return arg1 + arg2; } } } ``` C++/CLI代码: ```cpp #using <mscorlib.dll> #using <System.Windows.Forms.dll> #using "MyNamespace.dll" using namespace System; using namespace System::Threading::Tasks; using namespace MyNamespace; using namespace System::Windows::Forms; public ref class MyForm : public Form { public: MyForm() { Button^ button = gcnew Button(); button->Text = "Run Task"; button->Location = Point(10, 10); button->Click += gcnew EventHandler(this, &MyForm::OnButtonClick); this->Controls->Add(button); } private: void OnButtonClick(Object^ sender, EventArgs^ e) { Task<int>^ task = MyClass::MyMethodAsync(1, 2); int result = task->Result; MessageBox::Show(result.ToString()); } }; int main() { Application::Run(gcnew MyForm()); return 0; } ``` 这个示例创建了一个Windows窗体应用程序,其中包含一个按钮。当按钮被点击时,它将使用C# Task执行异步操作,并在任务完成后弹出一个消息框来显示结果。 注意:在C++/CLI中使用C#代码时,你需要在开头使用“#using”指令来引用相关的程序集。此外,你还需要使用“gcnew”来创建引用类型的实例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LoveWeeknd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值