简体   繁体   English

从C ++更新Texture2D像素

[英]Update Texture2D pixels from C++

In the project, I'm using Unity3D's C# scripts and C++ via DllImports. 在项目中,我正在通过DllImports使用Unity3D的C#脚本和C ++。 My goal is that the game scene has 2 cubes (Cube & Cube2) which one texture of cube show the live video through my laptop camera and Unity's webCamTexture.Play() and the other one texture of cube show the processed video by external C++ function ProcessImage() . 我的目标是游戏场景具有2个多维数据集(Cube & Cube2) ,其中一个多维数据集纹理通过我的便携式摄像机和Unity的webCamTexture.Play()展示实时视频,另一个多维数据集纹理通过外部C ++函数显示处理后的视频ProcessImage()

Code Context: 代码上下文:

In c++, I define that 在C ++中,我定义为

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

and the function is 而功能是

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#: C#:

Declare and importing 声明和导入

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);

Get camera situation and set cube1, cube2 Texture 获取摄像头情况并设置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();
    }
}

Send data to external C++ function by DllImports and receive processed data using Color32[] a . 通过DllImports将数据发送到外部C ++函数,并使用Color32[] a接收处理后的数据。 Finally, I'm using Unity's SetPixels32 to setup tx2D (Cube2) texture: 最后,我使用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();
}

Results: 结果:

The result is just the texture of cube 1 show the live video and fail to show processed data using the texture of cube 2. 结果只是多维数据集1的纹理显示了实时视频,而无法显示使用多维数据集2的纹理处理的数据。

Error: 错误:

SetPixels32 called with invalid number of pixels in the array UnityEngine.Texture2D:SetPixels32(Color32[]) Webcam:Update() (at Assets/Scripts/Webcam.cs:45) 在数组UnityEngine.Texture2D:SetPixels32(Color32 [])Webcam:Update()中调用了无效像素数的SetPixels32(位于Assets / Scripts / Webcam.cs:45)

I don't understand that why the invalid number of pixels in the array when I input array a to SetPixels32 我不明白为什么在将数组a输入到SetPixels32时为什么数组中的像素数无效

Any ideas? 有任何想法吗?


UPDATE (10 Oct. 2018) 更新 (2018年10月10日)

Thanks to @Programmer, now it can work by pin memory. 感谢@Programmer,现在它可以通过引脚存储工作。

Btw, I find some little problem which is about Unity Engine. 顺便说一句,我发现一些关于Unity Engine的小问题 When the Unity Camera run between 0 to 1 second, webCamTexture.width or webCamTexture.height always return 16x16 size even requested bigger image such as 1280x720 and then it will return correct size after 1 second. 当Unity Camera在0到1秒之间运行时,即使要求更大的图像(例如1280x720), webCamTexture.widthwebCamTexture.height始终返回16x16大小,然后它将在1秒后返回正确的大小。 (Possibly several frames) So, I reference this post and delay 2 seconds to run Process() in Update() function and reset the Texture2D size in Process() function. (可能是几帧)因此,我引用了这篇文章,并延迟了2秒钟,以便在Update()函数中运行Process()并在Process()函数中重置Texture2D大小。 It will work fine: 它将正常工作:

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");
    }
    ...
}

C# doesn't return understand the Color32 object you returned from C++. C#不会返回您从C ++返回的Color32对象。 This would have worked if you made the return type to be unsigned char* then use Marshal.Copy on the C# side to copy the returned data into byte array then use Texture2D.LoadRawTextureData to load the array into your Texture2D . 如果您将返回类型设置为unsigned char*然后在C#端使用Marshal.Copy将返回的数据复制到字节数组中,然后使用Texture2D.LoadRawTextureData将数组加载到Texture2D.LoadRawTextureData ,这将是Texture2D Here is an example that returns byte array from C#. 是一个从C#返回字节数组的示例。


I wouldn't suggest you return data as that's costly. 我不建议您返回数据,因为这很昂贵。 Fill the array you are passing to the function then simply re-assign that array to your Texture2D . 填充要传递给函数的数组,然后只需将该数组重新分配给Texture2D

C++: 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#: 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();
}

To use: 使用方法:

public Texture2D tex;

void Update()
{
    ProcessImage(tex);
}

If you don't want to use the unsafe and fixed keywords, you can also use GCHandle.Alloc to pin the array before sending it to the C++ side with GCHandle.AddrOfPinnedObject . 如果你不想使用unsafefixed关键字,你也可以使用GCHandle.Alloc它发送到C ++侧前针阵列GCHandle.AddrOfPinnedObject See this post for how to do that. 请参阅这篇文章,了解如何执行此操作。


If you are getting the following exception: 如果您遇到以下异常:

SetPixels32 called with invalid number of pixels in the array UnityEngine.Texture2D:SetPixels32(Color32[]) Webcam:Update() (at Assets/Scripts/Webcam.cs:45) 在数组UnityEngine.Texture2D:SetPixels32(Color32 [])Webcam:Update()中调用了无效像素数的SetPixels32(位于Assets / Scripts / Webcam.cs:45)

This means that the Texture2D you're calling SetPixels32 on has different size or texture dimension with the WebCamTexture texture. 这意味着您要调用SetPixels32Texture2DWebCamTexture纹理具有不同的大小或纹理尺寸。 To fix this, before calling SetPixels32 , check if the texture dimension changed then resize your target texture to match with the WebCamTexture . 要解决此问题,请在调用SetPixels32之前,检查纹理尺寸是否已更改,然后调整目标纹理的大小以与WebCamTexture匹配。

Replace 更换

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

with

//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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM