【问题标题】:How can I load an Image using Opencv and pass it to Unity (through a C++ dll)?如何使用 Opencv 加载图像并将其传递给 Unity(通过 C++ dll)?
【发布时间】:2021-03-19 13:55:51
【问题描述】:

上下文

我目前正在使用opencv 来检测视频源中的某些模式并将该信息发送到 Unity 游戏。 我找到了Aruco,一个opencv 模块以及 ArucoUnity ArucoUnityPlugin。但它不符合我的要求,所以我目前正在重新制作一个更简单的版本(即使用 opencv 作为库制作一个 dll 并在 Unity 中导入它的函数)。

问题

虽然使用简单的参数(int、string)并设法取回值,但我正在努力将图像加载到 Mat(通过 imread 函数)并在 C# 中取回 Mat。我可以使用 new Mat() 创建一个空的 Mat() 并将其放回 C#,我可以将我的 imagetest 加载到 Mat 中,但是当我尝试将它放入我返回的 Mat 中时,应用程序崩溃了。

代码

C++

mat.hpp(部分)

#ifdef PATTERNRECOGNITIONTOCLONEDLL_EXPORTS
#define PATTERNRECOGNITION_API __declspec(dllexport)
#else
#define PATTERNRECOGNITION_API __declspec(dllimport)
#endif
extern "C" {
    const string pluginFolderPath = "./Assets/Plugin/";

    PATTERNRECOGNITION_API cv::Mat* mat_new();
    PATTERNRECOGNITION_API cv::Mat* mat_loadPath(const char* path);
}

mat.cpp(部分)

cv::Mat* mat_new() // This works
{
    return new cv::Mat();
}

cv::Mat* mat_loadPath(const char* path) // This doesn't
{
    Mat* matPtr = new cv::Mat(); // Create an empty Mat
    *matPtr = cv::imread(pluginFolderPath + string(path), cv::IMREAD_COLOR); // read the image into the Mat
    return matPtr;
}

C#

Mat.cs(部分)

class Mat : HandleCppPtr
{
    public Mat(string path) : base(mat_loadPath(path))
    {
    }
    public Mat() : base(mat_new())
    {
    }
}

HandleCppPtr.cs(部分)

public enum DeleteResponsibility
{
    True,
    False
}

public abstract class  HandleCppPtr
{

    public HandleCppPtr(IntPtr cppPtr, DeleteResponsibility deleteResponsibility = DeleteResponsibility.True)
    {
        CppPtr = cppPtr;
        this.deleteResponsibility = deleteResponsibility;
    }

    ~HandleCppPtr()
    {
        if(this.deleteResponsibility == DeleteResponsibility.True)
        {
            Debug.Log("[C# ]Deleting HandleCppPtr object -> " + this.ToString());
            DeleteCppPtr();
        }
        else
        {
            Debug.Log("[C# ] NOT Deleting HandleCppPtr object because deleteResp is False -> " + this.ToString());
        }
    }

    public IntPtr CppPtr
    {
        get { return handle.Handle; }
        set { handle = new HandleRef(this, value); }
    }
    public DeleteResponsibility deleteResponsibility;
    HandleRef handle;
    protected abstract void DeleteCppPtr();
}

错误

当我尝试使用以下 C# 代码创建 Mat 时:
void Update()
{
    string testImagePath = "TestMatImage.png";
    if (Input.GetKeyDown(KeyCode.P))
    {
        Debug.Log("[C# ] Loading Image by Path = " + testImagePath);
        Mat mat = new Mat(testImagePath);
        Debug.Log(mat.ToString());
    }
}

它崩溃了,我收到以下错误: Microsoft Visual C++ 运行时库

运行时错误! 程序:

此应用程序已请求运行时以异常方式终止。 请联系应用程序的支持团队了解更多信息。

(https://i.stack.imgur.com/LN0Xb.png)

编辑 1

我尝试打印指针以查看使用 imread 时是否发生了恶作剧,但值(在重新启动之间更改时)在前后是相同的。所以好像不是这样的。 我使用的代码仍然在这里。
cv::Mat* mat_loadPath(const char* path)
{
    string message = "[C++] Loading mat by Path -> " + pluginFolderPath + string(path);
    DebugLog(message.c_str()); // Custom function that send debug message to Unity
    // PRINT : [C++] Loading mat by Path -> ./Assets/Plugin/TestMatImage.png
    
    Mat* matPtr = new cv::Mat(); // Create an empty Mat
    message = "[C++] Mat Not Loaded -> at " + std::to_string((int)matPtr) + "  (" + std::to_string(matPtr->rows) + "," + std::to_string(matPtr->cols) + ") ";
    DebugLog(message.c_str());
    // PRINT: [C++] Mat Not Loaded -> at -1895966160  (0,0) 

    *matPtr = cv::imread(pluginFolderPath + string(path), cv::IMREAD_COLOR); // read the image into the Mat
     message = "[C++] Mat Loaded -> at "+ std::to_string((int)matPtr) +"  (" + std::to_string(matPtr->rows) + "," + std::to_string(matPtr->cols) + ") " + std::to_string(matPtr->at<Vec3b>(0, 0).val[1]);
    DebugLog(message.c_str());
    // PRINT: [C++] Mat Loaded -> at -1895966160  (10,10) 231
        
    return new cv::Mat(); // If this is replaced by matPtr, the call of the 
}

【问题讨论】:

  • 如果你需要 C# 中的 OpenCV,你可以使用 Emgu:emgu.com/wiki/index.php/Main_Page 这简化了 C++ 包装部分。
  • 感谢 Sdra,我不确定它是否有效,因为我已经在 C++ 中完成了大部分图像分析(并且只将结果发送到 C#/Unity 端)。据我了解,Emgu 允许 C# 调用 opencv 的函数,这意味着我必须将所有这些分析移植到 C# 端。 (+ 我必须说服人们购买许可证)我希望能够让 Matrix 调用工作,但我会研究 Emgu 代码,看看他们是否做了类似的事情,如果他们做了,如何做。

标签: c# c++ opencv unity3d


【解决方案1】:

我找到了崩溃的原因,后来我想获取我转换为错误类型值的值(Vecd 而不是正确的 Vec3b)。 此外,我在测试时也没有使用“new cv::Mat()”,这导致 Mat 被 C++ 丢弃(并且 C# 指向随机的东西)。

对于任何想做类似事情的人来说,这里是我的代码(即使它深受 NormandErwan 的 ArucoUnity/ArucoUnityPlugin 的启发)。

C++

在某处有以下内容(对我来说它在 PatternRecognition.hpp 中)
#ifdef PATTERNRECOGNITIONTOCLONEDLL_EXPORTS
#define PATTERNRECOGNITION_API __declspec(dllexport)
#else
#define PATTERNRECOGNITION_API __declspec(dllimport)
#endif

mat.hpp

#include <opencv2/core.hpp>
#include "PatternRecognition.hpp"


using namespace CloneToTangible;

extern "C" {

    const string pluginFolderPath = "./Assets/Plugin/";

    PATTERNRECOGNITION_API cv::Mat* mat_new();
    PATTERNRECOGNITION_API cv::Mat* mat_loadPath(const char* path);

    PATTERNRECOGNITION_API void mat_delete(cv::Mat* matPtr);

    PATTERNRECOGNITION_API Vec3b* mat_at(cv::Mat* mat, uint posX, uint posY);

    PATTERNRECOGNITION_API int mat_rows(cv::Mat* mat);
    PATTERNRECOGNITION_API int mat_cols(cv::Mat* mat);

    PATTERNRECOGNITION_API int mat_type(cv::Mat* mat);
}

mat.cpp

#include "pch.h"
#include "mat.hpp"
#include <opencv2\imgcodecs.hpp>

cv::Mat* mat_new()
{
    Mat* matPtr = new cv::Mat();
    //DebugLog(("[C++] Mat Loaded -> at " + std::to_string((int)matPtr) + "  (" + std::to_string(matPtr->rows) + "," + std::to_string(matPtr->cols) + ") " + std::to_string(matPtr->at<Vec3b>(0, 0).val[1])).c_str());
    return matPtr;
}

cv::Mat* mat_loadPath(const char* path)
{
    //DebugLog(("[C++] Loading mat by Path -> " + pluginFolderPath + string(path)).c_str());
    Mat* matPtr = new cv::Mat();
    *matPtr = cv::imread(pluginFolderPath + string(path), cv::IMREAD_COLOR);
    //DebugLog(("[C++] Mat Loaded -> at " + std::to_string((int)matPtr) + "  (" + std::to_string(matPtr->rows) + "," + std::to_string(matPtr->cols) + ") " + std::to_string(matPtr->at<Vec3b>(0, 0).val[1])).c_str());
    return matPtr;
}

void mat_delete(cv::Mat* matPtr)
{
    delete matPtr;
}

Vec3b* mat_at(cv::Mat* mat, uint posX, uint posY)
{
    return new Vec3b(mat->at<Vec3b>(posX,posY));
}

int mat_rows(cv::Mat* mat)
{
    //DebugLog(("Returning rows for " + std::to_string((int)&mat) + " which is " + std::to_string(mat->rows)).c_str());
    return mat->rows;
}

int mat_cols(cv::Mat* mat)
{
    //DebugLog(("Returning cols for " + std::to_string((int)&mat) + " which is " + std::to_string(mat->cols)).c_str());
    return mat->cols;
}

int mat_type(cv::Mat* mat)
{
    return mat->type();
}

vec3b.hpp

#include <opencv2/core.hpp>
#include "PatternRecognition.hpp"

extern "C" {

    PATTERNRECOGNITION_API cv::Vec3b* cv_Vec3b_new(unsigned char v0, unsigned char v1, unsigned char v2);

    PATTERNRECOGNITION_API void cv_Vec3b_delete(cv::Vec3b* vec3dPtr);

    PATTERNRECOGNITION_API unsigned char cv_Vec3b_get(cv::Vec3b* vec3dPtr, int i);

    PATTERNRECOGNITION_API void cv_Vec3b_set(cv::Vec3b* vec3dPtr, int i, unsigned char value);

    PATTERNRECOGNITION_API void cv_Vec3b_setAll(cv::Vec3b* vec3dPtr, unsigned char v0, unsigned char v1, unsigned char v2);;
}

vec3b.cpp

#include "pch.h"
#include "vec3b.hpp"

cv::Vec3b* cv_Vec3b_new(unsigned char v0, unsigned char v1, unsigned char v2)
{
    return new cv::Vec3b(v0, v1, v2);
}

void cv_Vec3b_delete(cv::Vec3b* vec3bPtr)
{
    int n = (*vec3bPtr)[0];
    delete vec3bPtr;
}

unsigned char cv_Vec3b_get(cv::Vec3b* vec3bPtr, int i)
{
    unsigned char value = 0;
    try {
        value = (*vec3bPtr)[i];
    }
    catch (const cv::Exception& e) {
        return value;
    }
    return value;
}

void cv_Vec3b_set(cv::Vec3b* vec3bPtr, int i, unsigned char value)
{
    try {
        (*vec3bPtr)[i] = value;
    }
    catch (const cv::Exception& e) {
    }
}

void cv_Vec3b_setAll(cv::Vec3b* vec3bPtr, unsigned char v0, unsigned char v1, unsigned char v2)
{
    try {
        (*vec3bPtr)[0] = v0;
        (*vec3bPtr)[1] = v1;
        (*vec3bPtr)[2] = v2;
    }
    catch (const cv::Exception& e) {
    }
}

C#

Mat.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

class Mat : HandleCppPtr
{
    public int cols
    {
        get { return mat_cols(CppPtr); }
    }
    public int rows
    {
        get { return mat_rows(CppPtr); }
    }
    public int type
    {
        get { return mat_type(CppPtr); }
    }

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern IntPtr mat_new();

    [DllImport("PatternRecognitionToCloneDLL")]
    public static extern IntPtr mat_loadPath(string path);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern void mat_delete(IntPtr matPtr);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern IntPtr mat_at(IntPtr matPtr, uint x, uint y);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern int mat_cols(IntPtr matPtr);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern int mat_rows(IntPtr matPtr);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern int mat_type(IntPtr matPtr);

    public Mat(string path, DeleteResponsibility deleteResponsibility = DeleteResponsibility.True) : base(mat_loadPath(path), deleteResponsibility)
    {
        Debug.Log("[C#] Mat Instantiation : Mat(string path) ");
    }

    public Mat(DeleteResponsibility deleteResponsibility = DeleteResponsibility.True) : base(mat_new(), deleteResponsibility)
    {
        Debug.Log("[C#] Mat Instantiation : Mat() ");
    }
    public Mat(IntPtr cppPtr, DeleteResponsibility deleteResponsibility = DeleteResponsibility.True) : base(cppPtr, deleteResponsibility)
    {

    }

    public Vec3b At(uint x, uint y)
    {
        if (x < rows && y < cols)
            return new Vec3b(mat_at(this.CppPtr, x, y));
        else
            return new Vec3b();
    }

    protected override void DeleteCppPtr()
    {
        //Debug.Log("[C#] Mat Destruction : DeleteCppPtr(), before mat_delete() call ");
        mat_delete(this.CppPtr);
        //Debug.Log("[C#] Mat Destruction : DeleteCppPtr(), after mat_delete() call ");
    }

    public override string ToString()
    {
        string message = "Mat->";
        int rows = mat_rows(this.CppPtr);
        Debug.Log("[C#] Mat has " + rows + " rows");
        int cols = mat_cols(this.CppPtr);
        Debug.Log("[C#] Mat has " + cols + " cols");
        for (uint x = 0; x < rows; x++)
        {
            message += "\n {";
            for (uint y = 0; y < cols; y++)
            {
                message += At(x, y).ToString();
            }
            message += "}";
        }
        return message;
    }

}

Vec3b.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

public class Vec3b : HandleCppPtr
{

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern IntPtr cv_Vec3b_new(byte v0, byte v1, byte v2);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern void cv_Vec3b_delete(IntPtr vec3bPtr);


    [DllImport("PatternRecognitionToCloneDLL")]
    static extern byte cv_Vec3b_get(IntPtr vec3bPtr, int i);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern void cv_Vec3b_set(IntPtr vec3bPtr, int i, byte value);

    [DllImport("PatternRecognitionToCloneDLL")]
    static extern void cv_Vec3b_setAll(IntPtr vec3bPtr, byte v0, byte v1, byte v2);

    public Vec3b(byte v0 = 0, byte v1 = 0, byte v2 = 0) : base(cv_Vec3b_new(v0, v1, v2))
    {
    }

    public Vec3b(IntPtr cppPtr) : base(cppPtr)
    {
    }

    protected override void DeleteCppPtr()
    {
        cv_Vec3b_delete(this.CppPtr);
    }

    public int Get(int i)
    {
        return cv_Vec3b_get(this.CppPtr, i);
    }

    public void Set(int i, byte value)
    {
        cv_Vec3b_set(this.CppPtr, i, value);
    }


    public void Set(byte v0, byte v1, byte v2)
    {
        cv_Vec3b_setAll(this.CppPtr, v0, v1, v2);
    }

    public override string ToString()
    {
        string message = "Vec3b->";
        message += "(" + Get(0) + "," + Get(1) + "," + Get(2) + ")";
        return message;
    }

}

HandleCppPtr.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;


public enum DeleteResponsibility
{
    True,
    False
}
public abstract class  HandleCppPtr
{

    public HandleCppPtr(IntPtr cppPtr, DeleteResponsibility deleteResponsibility = DeleteResponsibility.True)
    {
        CppPtr = cppPtr;
        this.deleteResponsibility = deleteResponsibility;
    }

    ~HandleCppPtr()
    {
        if(this.deleteResponsibility == DeleteResponsibility.True)
        {
            //Debug.Log("[C# ]Deleting HandleCppPtr object -> " + this.ToString());
            DeleteCppPtr();
        }
        else
        {
            //Debug.Log("[C# ] NOT Deleting HandleCppPtr object because deleteResp is False -> " + this.ToString());
        }
    }

    public IntPtr CppPtr
    {
        get { return handle.Handle; }
        set { handle = new HandleRef(this, value); }
    }
    public DeleteResponsibility deleteResponsibility;
    HandleRef handle;
    protected abstract void DeleteCppPtr();
}

【讨论】:

    猜你喜欢
    • 2021-09-10
    • 2019-02-08
    • 2021-11-15
    • 2016-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    相关资源
    最近更新 更多