Nic*_*ous 7 c# c++ opencv unity-game-engine
元语境:
我目前正在开发一款利用opencv替代普通输入(键盘,鼠标等)的游戏.我通过DllImports在C++中使用Unity3D的C#脚本和opencv.我的目标是在我的游戏中创建一个来自opencv的图像.
代码上下文:
正如通常在OpenCV中所做的那样,我使用Mat来表示我的图像.这是我导出图像字节的方式:
cv::Mat _currentFrame;
...
extern "C" byte * EXPORT GetRawImage()
{
return _currentFrame.data;
}
Run Code Online (Sandbox Code Playgroud)
这就是我从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);
}
Run Code Online (Sandbox Code Playgroud)
从我理解OpenCV的方式来看,我希望在uchar指针中序列化时,以这种方式构造字节数组:
b1, g1, r1, b2, g2, r2, ...
Run Code Online (Sandbox Code Playgroud)
我正在使用以下方法将此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;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我使用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();
Run Code Online (Sandbox Code Playgroud)
结果:
最终的图像似乎在某种程度上分散,它类似于某些形状,但它似乎也是形状的三倍.这是我牵着我的手在镜头前:
[我,我的手和相机]
做一些测试,我得出结论,我正确地解码了通道,因为,使用我的手机发出RGB光,我可以重现现实世界的颜色:
[红色测试]
[蓝色测试]
[绿色测试]
图像中还有一些奇怪的线条:
[怪异的线条]
我还可以将这些图像与以下内容进行比较:
[我在镜头前的脸]
问题:
由于我能够正确解码颜色通道,我在解码OpenCV阵列时遇到了什么错误?我不知道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() );
}
Run Code Online (Sandbox Code Playgroud)
Pro*_*mer 10
用SetPixels32
而不是LoadRawTextureData
.而不是从C++返回数组数据,而是从C#中执行.创建Color32
阵列并与针它在c# GCHandle.Alloc
,送钉扎的地址Color32
阵列,以C++,使用cv::resize
来调整cv::Mat
,以匹配从C#发送像素的尺寸.您必须执行此步骤或预计会出现一些错误或问题.
最后,cv::Mat
从RGB转换为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<cv::Mat> 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());
}
Run Code Online (Sandbox Code Playgroud)
C#:
使用a附加到任何GameObject,Renderer
您应该看到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<Renderer>().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();
}
}
Run Code Online (Sandbox Code Playgroud)