unity显示相机画面并在opencv处理

打开相机

显示方法

首先需要明确,目前看到的显示图片有两种方法,一种是渲染到实际的gameobject上面去,另一种是draw到GUI上面去。关于gameobject在上一个显示图片里面有了大致的了解。这里主要说的内容包括

  • 相机显示的必备步骤
  • 关于GUI
  • 关于renderer

相机内容显示

  • 在unity中,有一个专门的类叫做WebCamTexture,我们需要为读取进来的相机texture创建一个新的对象。一般来说,需要三个部分,相机纹理,相机名字(string)以及相机是否打开(bool)

start部分

  • 在这部分,我们首先需要把创建的cameraTexture实例化
  • 其次,我们需要调用StartCoroutine函数,在其中调用测试函数来确定相机是否打开

StartCoroutine

  • 在一般的执行里面,unity是逐帧运行的,所以当操作花费时间的时候,帧率下降,就会发生卡顿
  • 这部分开始了一个协程(Coroutine),使用yield,可以在任何部分暂停这个Coroutine的执行。如果被成功暂停,它会在下一帧恢复正常。所以这个方法在多帧运行之中非常好用。
    • unity会假装开辟一个新线程来执行,但是不会影响主线程的持续效果
    • 参考
  • 从这里的功能来说,yield会检查用户有没有授权,如果没有授权的话,运行被终止,跳到下一帧。如果授权成功了的话,运行成功,相机打开。

IEnumerator

  • 在StartCoroutine调用的是一个IEnumerator函数,他通过yield一个bool来决定是不是继续运行这个函数

授权

  • 在test里面需要考虑有没有用户的授权
  • 有授权的情况下,需要把目前的WebCamTexture(注意这里不是建立的实例)的device的数据传递给WebCamDevice,包括类型,名字等等。
  • 然后需要把包括这个相机名字,size和fps的信息传给之前cameraTexture的实例
  • 以上都设定好之后,WebCamTexture.Play会激活这个相机,让他开始工作
  • 然后再讲相机的texture渲染到object的表面上
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
    if(Application.HasUserAuthorization(UserAuthorization.WebCam))
    {
    WebCamDevice[] devices = WebCamTexture.devices;
    cameraName = devices[0].name;
    cameraTexture = new WebCamTexture(cameraName, 1024, 768, 15);
    cameraTexture.Play();
    isOpen = true;
    renderer.material.mainTexture = cameraTexture;
    }

GUI

  • GUI主要是提供了图形化的窗口,实际上显示的元素是直接显示在game画面上的,也就是说这部分是和实际相机拍摄到的画面独立的。无论相机如何移动,物体如何改变,最终GUI的画面都会显示到同样的地方

MonoBehaviour.OnGUI()

  • 注意OnGUI函数并不需要我们自己去调用,不需要再update里面调用!
  • OnGUI是API里面自带的函数,我们需要在这个函数中渲染和处理GUI的event
  • 在实际应用中,OnGUI可能每一个frame被call很多次,每次event(例如鼠标操作,键盘等等)发生的时候都会call这个函数
  • 例如下面官方的例子,每次鼠标点击的时候,就会print出相应的话来
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    using UnityEngine;
    using System.Collections;

    public class ExampleClass : MonoBehaviour
    {
    void OnGUI()
    {
    if (GUI.Button(new Rect(10, 10, 150, 100), "I am a button"))
    {
    print("You clicked the button!");
    }
    }
    }

GUI.DrawTexture

  • 在GUI的实现中,我们需要将相机读取到的部分draw的GUI的上面,所以调用了这个函数
  • 需要确定的参数包括:位置,需要渲染的texture,缩放比例等等

实现

  • 代码部分参考
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class showCamera : MonoBehaviour
    {
    protected WebCamTexture cameraTexture;
    protected string cameraName = "";
    protected bool isOpen = false;

    //protected MeshRenderer renderer;

    // Start is called before the first frame update
    void Start()
    {
    //renderer = this.GetComponent<MeshRenderer>();
    cameraTexture = new WebCamTexture();
    StartCoroutine(Test());
    }

    // Update is called once per frame
    void Update()
    {
    }

    IEnumerator Test()
    {
    yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
    if(Application.HasUserAuthorization(UserAuthorization.WebCam))
    {
    WebCamDevice[] devices = WebCamTexture.devices;
    cameraName = devices[0].name;
    cameraTexture = new WebCamTexture(cameraName, 1024, 768, 15);
    cameraTexture.Play();
    isOpen = true;
    //renderer.material.mainTexture = cameraTexture;
    }
    }

    void OnGUI()
    {
    if(isOpen)
    {
    GUI.DrawTexture(new Rect(0, 0, 400, 300), cameraTexture, ScaleMode.ScaleToFit);
    }
    }
    }

渲染到object上面

  • 和之前的操作类似,需要
    • 构建MeshRenderer的object
    • 在start里面读取出物体的MeshRenderer(getcomponent)
    • 最后打开相机后,把相机的内容渲染到MeshRenderer上面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class showCamera : MonoBehaviour
{
protected WebCamTexture cameraTexture;
protected string cameraName = "";
protected bool isOpen = false;

protected MeshRenderer renderer;

// Start is called before the first frame update
void Start()
{
renderer = this.GetComponent<MeshRenderer>();
//cameraTexture = new WebCamTexture();
StartCoroutine(Test());
}


IEnumerator Test()
{
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
if(Application.HasUserAuthorization(UserAuthorization.WebCam))
{
WebCamDevice[] devices = WebCamTexture.devices;
cameraName = devices[0].name;
cameraTexture = new WebCamTexture(cameraName, 1024, 768, 15);
cameraTexture.Play();
isOpen = true;
renderer.material.mainTexture = cameraTexture;
}
}

//void OnGUI()
//{
// if(isOpen)
// {
// GUI.DrawTexture(new Rect(0, 0, 400, 300), cameraTexture, ScaleMode.ScaleToFit);
// }
//}
}

将图片放入opencv

来源

  • 得到了web的texture之后,可以直接用openCV的部分把这个玩意转换成mat,然后处理
  • 这里的问题是刚开始mat的大小莫名其妙的是16,所以需要加上一个判断条件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity.CoreModule;
using OpenCVForUnity.UnityUtils;
using OpenCVForUnity.ImgprocModule;

public class showCamera : MonoBehaviour
{
protected WebCamTexture cameraTexture;
//protected string cameraName = "";
//protected bool isOpen = false;
private Color32[] colors;
private Mat rgbaMat;
private Texture2D tex;

// Start is called before the first frame update
void Start()
{
//renderer = this.GetComponent<MeshRenderer>();
//cameraTexture = new WebCamTexture();
//StartCoroutine(Test());
cameraTexture = new WebCamTexture(WebCamTexture.devices[0].name, 640, 480);
cameraTexture.Play();

StartCoroutine(init());
}


private IEnumerator init()
{
Debug.Log(cameraTexture.width);
if (cameraTexture.width <= 16)
{
while(!cameraTexture.didUpdateThisFrame)
{
yield return new WaitForEndOfFrame();
}

cameraTexture.Pause();
colors = cameraTexture.GetPixels32();
cameraTexture.Stop();

yield return new WaitForEndOfFrame();
cameraTexture.Play();

tex = new Texture2D(cameraTexture.width, cameraTexture.height, TextureFormat.RGBA32, false);
rgbaMat = new Mat(cameraTexture.height, cameraTexture.width, CvType.CV_8UC4);

GetComponent<Renderer>().material.mainTexture = tex;

}
}

private void Update()
{
Debug.Log(cameraTexture.width);
if (cameraTexture.didUpdateThisFrame && rgbaMat != null)
{
Utils.webCamTextureToMat(cameraTexture, rgbaMat);
//Imgproc.cvtColor(rgbaMat, rgbaMat, Imgproc.COLOR_RGB2HSV);
Utils.matToTexture2D(rgbaMat, tex);
tex.Apply();
}
}

}