将 OpenCV 图像数据获取到 C# 的问题

Problem with getting OpenCV image data to C#

所以我是 C++ 的新手,我尝试使用它来使用 OpenCV 库读取图像。我的想法是将 C++ 代码放在 DLL 中,然后通过从 C# 脚本调用 DLL 来获取解码图像(我需要 C# 中的图像)。

我环顾了一下将字节发送到 C# 的最佳方式,发现大多数人都在使用 char* 来存储字节。然后可以由 C++ 函数返回并存储为 C# string,如下所示:

char* GetSomeBytes()
{
    // Got this method of allocating memory from the internet, sadly can't remember from were
    // (I get memory errors if I pass my_bytes directly)

    size_t stSize = strlen(my_bytes) + sizeof(char);
    char* pszReturn = NULL;

    pszReturn = (char*)::CoTaskMemAlloc(stSize);
    strcpy_s(pszReturn, stSize, my_bytes);
    return pszReturn;
}

然后在 C# 中:

[DllImport(path_to_my_dll, EntryPoint = "GetSomeBytes", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern string GetSomeBytes();

static void Main(string[] args)
{
    string raw_bytes = GetSomeBytes();

    byte[] bytes = Encoding.ASCII.GetBytes(raw);
}

这甚至奏效了,但现在我必须将原始数据从 Mat 获取到 char*。我很确定我必须使用 Mat.data 字段,但它包含一个 unsinged char*。我尝试了几种方法来进行这种转换:

int size = my_mat.total() * my_mat.elemSize();
char* bytes = new char[size];
memcpy(bytes, my_mat.data, size);

所以这是我的问题:当我在全高清图像上使用 my_mat.total() * my_mat.elemSize() 时,它返回 6220804,这对我来说有意义(因为 1920 * 1080 = 2073600,所以图像有2.073.600 像素,2073600 * 3 = 6220804,图像有 3 个颜色通道,因此总共需要 6.220.804 个字节来存储它。

然而,在转换为 char* 之后,对于上述三种方法中的每一种,我尝试的每张图像 strlen(the_converted_bytes) 都是完全不同的,从大约 2.000 一直到到大约 11.000.000。 bytes.Length 在 C# 中返回了相同的值,所以我认为错误不在 C++-char* 到 C#-bytes 过程中。无论如何,我在 DllImportEncoding.GetBytes 中尝试了不同的字符集,但这似乎没有帮助。

所以我认为我对charunsigned char的理解或者对指针的理解有问题。或两者。无论如何,在我看来,应该可以将 C++ Mat 的数据转换为 C# byte[] 的数据。 unsinged char*char* 之间的转换难道不像我想象的那么容易,还是我忽略了一些完全不同的东西(也许我对 my_mat.data 的使用有问题)?

如果有任何帮助,我将不胜感激

好吧,没关系,事实证明,从 C++ 函数中 return unsigned char* 然后在 C# 中转换它实际上更有意义。如果其他人对此有疑问,here's the thread I found this answer on (Thanks to Louis.fr)。

以下是我最终使用的方法:

在 C++ 中:

unsigned char* GetUnsignedBytes(char* image, int* bytes_count)
{
    Mat img = imread(image);

    int size = img.total() * img.elemSize();
    unsigned char* raw_bytes = new unsigned char[size];

    memcpy(raw_bytes, img.data, size * sizeof(std::byte));
    
    *bytes_count = size;
    return raw_bytes;
}

在 C# 中(using Systemusing System.Runtime.InteropServices):

[DllImport(path_to_my_dll)]
public static extern IntPtr GetUnsignedBytes(string image, out int bytes_count);

static void Main(string[] args)
{
    IntPtr ptr = GetUnsignedBytes(some_image, out int bytes_count);

    byte[] bytes = new byte[bytes_count];
    Marshal.Copy(ptr, bytes, 0, bytes_count);
}

很高兴毕竟这么简单。 Louis.fr 还写道,您可以只传递一个指针,但据我所知,在 C# 中使用这些指针需要取消保存代码(使用 /unsave 或类似的东西进行编译)。所以我想我会坚持使用这种方法,只要我以后不会遇到更多问题。