将 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*
。我尝试了几种方法来进行这种转换:
- 使用简单的转换(如
(char*)my_mat.data
)
- 使用 reinterpret_cast(比如
reinterpret_cast<char*>(my_mat.data)
),有人说这对 C++ 来说更好
- 像这样使用 memcpy:
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 过程中。无论如何,我在 DllImport
和 Encoding.GetBytes
中尝试了不同的字符集,但这似乎没有帮助。
所以我认为我对char
和unsigned 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 System
和 using 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
或类似的东西进行编译)。所以我想我会坚持使用这种方法,只要我以后不会遇到更多问题。
所以我是 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*
。我尝试了几种方法来进行这种转换:
- 使用简单的转换(如
(char*)my_mat.data
) - 使用 reinterpret_cast(比如
reinterpret_cast<char*>(my_mat.data)
),有人说这对 C++ 来说更好 - 像这样使用 memcpy:
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 过程中。无论如何,我在 DllImport
和 Encoding.GetBytes
中尝试了不同的字符集,但这似乎没有帮助。
所以我认为我对char
和unsigned 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 System
和 using 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
或类似的东西进行编译)。所以我想我会坚持使用这种方法,只要我以后不会遇到更多问题。