【问题标题】:Update Texture2D pixels from C++从 C++ 更新 Texture2D 像素
【发布时间】:2018-10-07 07:48:29
【问题描述】:

在项目中,我通过 DllImports 使用 Unity3D 的 C# 脚本和 C++。我的目标是游戏场景有2个立方体(Cube & Cube2),其中一个立方体纹理通过我的笔记本电脑摄像头和Unity的webCamTexture.Play()显示实时视频,另一个立方体纹理显示通过外部C++函数ProcessImage()处理的视频。

代码上下文:

在 c++ 中,我定义了它

struct Color32
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
};

功能是

extern "C"
{
    Color32* ProcessImage(Color32* raw, int width, int height);
}
...

Color32* ProcessImage(Color32* raw, int width, int height)
{
    for(int i=0; i<width*height ;i++)
    {
       raw[i].r = raw[i].r-2;
       raw[i].g = raw[i].g-2;
       raw[i].b = raw[i].b-2;
       raw[i].a = raw[i].a-2;
    }
    return raw;
}

C#:

声明和导入

public GameObject cube; 
public GameObject cube2;
private Texture2D tx2D;
private WebCamTexture webCamTexture;

[DllImport("test22")]   /*the name of Plugin is test22*/
private static extern Color32[] ProcessImage(Color32[] rawImg, 
                                                 int width, int height);

获取相机情况并设置cube1、cube2纹理

void Start()
{
    WebCamDevice[] wcd = WebCamTexture.devices;

    if(wcd.Length==0)
    {
        print("Cannot find a camera");
        Application.Quit();
    }
    else
    {
        webCamTexture = new WebCamTexture(wcd[0].name);
        cube.GetComponent<Renderer>().material.mainTexture = webCamTexture;

        tx2D = new Texture2D(webCamTexture.width, webCamTexture.height);
        cube2.GetComponent<Renderer>().material.mainTexture = tx2D;

        webCamTexture.Play();
    }
}

通过DllImports 向外部C++ 函数发送数据,并使用Color32[] a 接收处理后的数据。最后,我使用 Unity 的 SetPixels32 设置 tx2D(Cube2) 纹理:

void Update()
{
    Color32[] rawImg = webCamTexture.GetPixels32();
    System.Array.Reverse(rawImg);

    Debug.Log("Test1");
    Color32[] a = ProcessImage(rawImg, webCamTexture.width, webCamTexture.height);
    Debug.Log("Test2");

    tx2D.SetPixels32(a);
    tx2D.Apply();
}

结果:

结果只是立方体 1 的纹理显示了实时视频,而无法显示使用立方体 2 的纹理处理的数据。

错误:

SetPixels32 调用时数组中的像素数无效 UnityEngine.Texture2D:SetPixels32(Color32[]) 网络摄像头:Update() (在 资产/脚本/Webcam.cs:45)

我不明白为什么当我将数组 a 输入到 SetPixels32 时数组中的像素数无效

有什么想法吗?


更新(2018 年 10 月 10 日)

感谢@Programmer,现在它可以通过引脚内存工作。

顺便说一句,我找到了一些关于 Unity 引擎的小 problem。当 Unity Camera 在 0 到 1 秒之间运行时,webCamTexture.widthwebCamTexture.height 总是返回 16x16 大小,甚至请求更大的图像,例如 1280x720,然后它会在 1 秒后返回正确的大小。 (Possibly several frames) 所以,我引用了这个 post 并延迟 2 秒以在 Update() 函数中运行 Process() 并在 Process() 函数中重置 Texture2D 大小。它会正常工作:

delaytime = 0;
void Update()
{
    delaytime = delaytime + Time.deltaTime;
    Debug.Log(webCamTexture.width);
    Debug.Log(webCamTexture.height);

    if (delaytime >= 2f)
        Process();
}
unsafe void Process()
{
    ...

    if ((Test.width != webCamTexture.width) || Test.height != webCamTexture.height)
    {
       Test = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.ARGB32, false, false);
       cube2.GetComponent<Renderer>().material.mainTexture = Test;
       Debug.Log("Fixed Texture dimension");
    }
    ...
}

【问题讨论】:

  • 你能分享你定义和初始化Texture2D tx2D变量的代码吗?
  • Texture2D tx2D 初始化 new Texture2D(webCamTexture.width, webCamTexture.height)
    代码已经上传,您可以在本站观看[链接:@987654324 @
  • 我遇到了同样的问题,你能分享一下你做了什么让它工作吗?我无法将处理后图像的 cv::Mat 数组从 c++ 移动到 C#

标签: c# c++ unity3d pinvoke


【解决方案1】:

C# 不返回了解您从 C++ 返回的 Color32 对象。如果您将返回类型设置为 unsigned char* 然后在 C# 端使用 Marshal.Copy 将返回的数据复制到字节数组中,然后使用 Texture2D.LoadRawTextureData 将数组加载到您的 Texture2D 中,这将起作用。 Here 是一个从 C# 返回字节数组的示例。


我不建议您返回数据,因为这很昂贵。填充您传递给函数的数组,然后将该数组重新分配给您的Texture2D

C++:

extern "C"
{
    void ProcessImage(unsigned char* raw, int width, int height);
}
...

void ProcessImage(unsigned char* raw, int width, int height)
{
    for(int y=0; y < height; y++)
    {
        for(int x=0; x < width; x++)
        {
            unsigned char* pixel = raw + (y * width * 4 + x * 4);
            pixel[0] = pixel[0]-(unsigned char)2; //R
            pixel[1] = pixel[1]-(unsigned char)2; //G
            pixel[2] = pixel[2]-(unsigned char)2; //B
            pixel[3] = pixel[3]-(unsigned char)2; //Alpha
        }
    }
}

C#:

[DllImport("test22")]
private static extern void ProcessImage(IntPtr texData, int width, int height);

unsafe void ProcessImage(Texture2D texData)
{
    Color32[] texDataColor = texData.GetPixels32();
    System.Array.Reverse(texDataColor);

    //Pin Memory
    fixed (Color32* p = texDataColor)
    {
        ProcessImage((IntPtr)p, texData.width, texData.height);
    }
    //Update the Texture2D with array updated in C++
    texData.SetPixels32(texDataColor);
    texData.Apply();
}

使用方法:

public Texture2D tex;

void Update()
{
    ProcessImage(tex);
}

如果您不想使用unsafefixed 关键字,您还可以使用GCHandle.Alloc 固定数组,然后再使用GCHandle.AddrOfPinnedObject 将其发送到C++ 端。请参阅this 帖子了解如何执行此操作。


如果您遇到以下异常:

SetPixels32 调用时数组中的像素数无效 UnityEngine.Texture2D:SetPixels32(Color32[]) 网络摄像头:Update() (在 资产/脚本/Webcam.cs:45)

这意味着您调用SetPixels32Texture2D 具有与WebCamTexture 纹理不同的大小或纹理维度。要解决此问题,请在调用 SetPixels32 之前检查纹理尺寸是否发生变化,然后调整目标纹理的大小以匹配 WebCamTexture

替换

tx2D.SetPixels32(rawImg);
tx2D.Apply();

//Fix texture dimension if it doesn't match with the web cam texture size
if ((tx2D.width != webCamTexture.width) || tx2D.height != webCamTexture.height)
{
    tx2D = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32, false, false);
    Debug.Log("Fixed Texture dimension");
}

tx2D.SetPixels32(rawImg);
tx2D.Apply();

【讨论】:

  • 感谢您的回复。不幸的是,我参考了您的建议,思考并修改了代码。它也出现相同的错误消息。想看代码详情,我已经上传到这个网站了:codeshare.io/G6gkjz
  • 对答案进行了编辑以解决该错误。看看这个。如果还有问题,请告诉我。
  • 如果您的问题通过单击我的答案上的绿色复选标记得到解决,请不要忘记接受答案。
  • 感谢@Programmer,现在它可以通过引脚内存工作。但是,我有一个小问题,如果我使用 OpenCV 库处理图像。更多详细信息,在我的帖子“更新”部分。
  • 对不起,这不是这里的工作方式。这篇文章不是关于 OpenCV 的,原来的文章是关于从 C++ 更新 Texture2D 像素的。我认为问题现在已经解决了。当您提出一个问题并且它已解决但您想做不同的事情并遇到问题时,您接受当前答案然后创建新问题。您应该使用您的 OpenCV 帖子创建新问题。确保在新问题中也标记 OpenCV。您可能想要删除您对此问题所做的 OpenCV 编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-25
  • 1970-01-01
  • 2016-11-21
  • 2021-10-06
  • 2021-05-23
  • 1970-01-01
  • 2012-09-01
相关资源
最近更新 更多