将OpenCV Mat转换为Texture2D?

元语境:

我正在开发一款利用opencv替代普通输入(键盘,鼠标等)的游戏。 我通过DllImports在C ++中使用Unity3D的C#脚本和opencv。 我的目标是在我的游戏中创建一个来自opencv的图像。

代码背景:

正如通常在OpenCV中所做的那样,我使用Mat来表示我的图像。 这是我导出图像字节的方式:

cv::Mat _currentFrame; ... extern "C" byte * EXPORT GetRawImage() { return _currentFrame.data; } 

这就是我从C#导入的方式:

 [DllImport ("ImageInputInterface")] private static extern IntPtr GetRawImage (); ... public static void GetRawImageBytes (ref byte[] result, int arrayLength) { IntPtr a = GetRawImage (); Marshal.Copy(a, result, 0, arrayLength); FreeBuffer(a); } 

从我理解OpenCV的方式来看,我希望在uchar指针中序列化时,以这种方式构造字节数组:

 b1, g1, r1, b2, g2, r2, ... 

我正在使用以下方法将此BGR数组转换为RGB数组:

 public static void BGR2RGB(ref byte[] buffer) { byte swap; for (int i = 0; i < buffer.Length; i = i + 3) { swap = buffer[i]; buffer[i] = buffer[i + 2]; buffer[i + 2] = swap; } } 

最后,我使用Unity的LoadRawTextureData将字节加载到纹理:

 this.tex = new Texture2D( ImageInputInterface.GetImageWidth(), ImageInputInterface.GetImageHeight(), TextureFormat.RGB24, false ); ... ImageInputInterface.GetRawImageBytes(ref ret, ret.Length); ImageInputInterface.BGR2RGB(ref ret); tex.LoadRawTextureData(ret); tex.Apply(); 

结果:

最终的图像似乎在某种程度上分散,它类似于某些形状,但它似乎也是形状的三倍。 这是我把手放在镜头前:

[我,我的手和相机]

做了一些测试,我得出结论,我正确地解码了通道,因为,使用我的手机发出RGB光,我可以重现现实世界的颜色:

[红色测试]

[蓝色测试]

[绿色测试]

图像中还有一些奇怪的线条:

[怪异的线条]

我还可以将这些图像与以下内容进行比较:

[我在镜头前的脸]

问题:

由于我能够正确解码颜色通道,我在解码OpenCVarrays时遇到了什么错误? 这是我不知道Unity的LoadRawTextureData是如何工作的,还是我以错误的方式解码了什么?

OpenCV Mat.data数组是如何构建的?

UPDATE

感谢@Programmer,他的解决方案就像魔术一样。

[我开心]

我改变了他的剧本,没有必要做一些事情。 在我的情况下,我需要使用BGR2RGBA,而不是RGB2RGBA:

 extern "C" void EXPORT GetRawImage( byte *data, int width, int height ) { cv::Mat resizedMat( height, width, _currentFrame.type() ); cv::resize( _currentFrame, resizedMat, resizedMat.size(), cv::INTER_CUBIC ); cv::Mat argbImg; cv::cvtColor( resizedMat, argbImg, CV_BGR2RGBA ); std::memcpy( data, argbImg.data, argbImg.total() * argbImg.elemSize() ); } 

使用SetPixels32而不是LoadRawTextureData 。 而不是从C ++返回数组数据,而是从C#中执行。 创建Color32数组并使用GCHandle.Alloc将其固定在c#中,将固定Color32数组的地址发送到C ++,使用cv::resize 调整cv::Mat的大小以匹配从C#发送的像素大小。 您必须执行此步骤或预计会出现一些错误或问题。

最后,将cv::MatRGB转换为ARGB,然后使用std::memcpy从C ++更新数组。 然后可以使用SetPixels32函数将更新的Color32数组加载到Texture2D 。 这就是我的方式,它一直在为我工作,没有任何问题。 可能还有其他更好的方法,但我从未找到过。

C ++:

 cv::Mat _currentFrame; void GetRawImageBytes(unsigned char* data, int width, int height) { //Resize Mat to match the array passed to it from C# cv::Mat resizedMat(height, width, _currentFrame.type()); cv::resize(_currentFrame, resizedMat, resizedMat.size(), cv::INTER_CUBIC); //You may not need this line. Depends on what you are doing cv::imshow("Nicolas", resizedMat); //Convert from RGB to ARGB cv::Mat argb_img; cv::cvtColor(resizedMat, argb_img, CV_RGB2BGRA); std::vector bgra; cv::split(argb_img, bgra); std::swap(bgra[0], bgra[3]); std::swap(bgra[1], bgra[2]); std::memcpy(data, argb_img.data, argb_img.total() * argb_img.elemSize()); } 

C#:

使用Renderer附加到任何GameObject,您应该看到每帧都在该对象上显示和更新cv::Mat 。 如果混淆,代码会被评论:

 using System; using System.Runtime.InteropServices; using UnityEngine; public class Test : MonoBehaviour { [DllImport("ImageInputInterface")] private static extern void GetRawImageBytes(IntPtr data, int width, int height); private Texture2D tex; private Color32[] pixel32; private GCHandle pixelHandle; private IntPtr pixelPtr; void Start() { InitTexture(); gameObject.GetComponent().material.mainTexture = tex; } void Update() { MatToTexture2D(); } void InitTexture() { tex = new Texture2D(512, 512, TextureFormat.ARGB32, false); pixel32 = tex.GetPixels32(); //Pin pixel32 array pixelHandle = GCHandle.Alloc(pixel32, GCHandleType.Pinned); //Get the pinned address pixelPtr = pixelHandle.AddrOfPinnedObject(); } void MatToTexture2D() { //Convert Mat to Texture2D GetRawImageBytes(pixelPtr, tex.width, tex.height); //Update the Texture2D with array updated in C++ tex.SetPixels32(pixel32); tex.Apply(); } void OnApplicationQuit() { //Free handle pixelHandle.Free(); } }