개발 관련/프로젝트

Unity에서 사물인식을 위한 참고사항 검토

by 소서리스25 2023. 9. 23.
반응형

역시나 맨땅에 헤딩하면서 뭔가를 하려고 하니 쉽지 않다. 선호하는 툴의 버전이 낮은 것도 한몫하는 것 같다. 

그러면 왜 상위버전을 사용하지 않는지에 대한 궁금증이 있을 것인데...

기존의 것을 상위 버전으로 업데이트하면 원인을 알 수 없거나 찾기가  너무 힘든 오류가 발생한다.

한두개가 아니다. 대부분의 정상적인 실행이 한 번에 되지 않으며 되더라도 매우 느린 속도로 플레이되는 경우도 있다. 그나마 현재 사용하는 버전에서 만큼은 작동이 잘 된다.

그리고 달라진 인터페이스에 적응이 쉽지 않다. 좋은건 아는데 말이다.

 

어쨌든 참고자료를 검색하고, 검토하고 진행중인데, 사물인식에 대한 설명을 한 분들 중에서 접근하는 방식이 생각과 비슷(?)하여 구글 번역본으로 옮겨본다.(출처 있음)


 

Barracuda를 사용한 Unity 3D의 이미지 인식

 

이 Unity 자습서에서는 Barracuda를 사용하여 Unity3D에서 기계 학습 모델을 실행하는 방법을 다룹니다. 이를 통해 Android 또는 iOS에서 크로스 플랫폼 지원을 위해 Unity 비디오 게임 엔진에서 대부분의 머신 러닝 모델을 onnx 형식으로 실행할 수 있습니다. 이 튜토리얼은 학습 과정에서 저의 학습 공유이며, 기계 학습의 내용과 초보자에게 학습 참고 자료로 매우 적합한 Unity3D 통합의 깊은 내용을 포함하지 않습니다.

 

머리말

 

최근에 저는 인디 타이틀에 새로운 것을 포함하고 싶기 때문에 독립적으로 작업하고 있는 게임을 보류했습니다. Unity3D에서 리듬 인식을 위해 딥 러닝 네트워크를 통합하려고 시도했으며 몇 가지 솔루션을 시도했지만 그중 어느 것도 Conda와 같은 환경 지원에 도달하지 못했습니다. 물론 개발 환경에서 딥 러닝을 실행하는 것은 새로운 것이 아니지만, 런타임에 딥 러닝 네트워크를 실행하고 플레이어가 플레이할 때 다양한 머신 러닝 모델을 사용하는 것이 제가 추구하는 것입니다.

 

솔루션 비교

 

공식 Unity 3D 및 기타 솔루션 중 일부를 사용해 보았지만 여기에 몇 가지 장단점이 있습니다.

 

  • 유니티를 위한 파이썬

  - 사용이 간편하고 공무원은 직접 설치할 수 있는 완전한 설치 패키지를 제공합니다.

  - Python 타사 라이브러리 지원, 프로젝트 경로에 사이트 패키지 참조 또는 추가 런타임 및 프로덕션 패키징은 지원되지 않으며 코드는 UnityEditor 확장의 일부입니다. 다양한 Python 패키지의 버전을 변경할 수 없으며 다양한 문제가 발생합니다.

 

  • 파이썬넷

  - Python for unity의 외부 소스에 속하며, Python for unity는 이 라이브러리를 사용하여 개발되었으며,

  - 네이티브 Python for unity에 속하는 Unity3D의 Github에서 동일한 포크 소스 코드를 찾을 수 있습니다.

  - 장점과 단점은 Python의 통합과 유사합니다.

 

  • IronPython 크랙

  - IronPython은 런타임 통합을 지원하는 .NET 플랫폼의 Python 구현입니다.

  - 또한 IronPython은 어셈블리와 DLL을 도입해야 하며 IronPython의 내부 구현에 대해 잘 알고 있어야 합니다.

  - 주니어 개발자의 경우 작은 Python 스크립트에 더 적합합니다.

  - 딥 러닝에 적합하지 않은 프레임워크.

 

  • ML-에이전트

  - Unity3D의 공식 머신러닝 솔루션으로, 설치가 간편하고, 수많은 튜토리얼 문서가 포함되어 있습니다.

  - 지원 및 Anaconda 협업 환경(개발 환경만 해당, 런타임은 지원되지 않음) Python의 머신 러닝 프레임워크에서 출발한  Unity는 딥 러닝을 하지 않는 개발자도 더 쉽게 사용할 수 있도록 자체 패키징을 만들고 자체 로직 세트를 사용했으며, 이는 TensorflowSharp의 인터페이스로 사용되어야 하고 C#으로 작성된 로직으로 사용되어야 합니다.

  - 이름은 ML-Agent이지만 대부분의 머신 러닝 콘텐츠를 지원하지 않으며, 특정 강화 학습 모델과 비슷하며 강력한 사용자 정의 간격이 없으며 Unity 자체를 제외하고 다른 개발자는 이 세트를 사용하여 개발하는 경우가 거의 없습니다.

  - Python 딥러닝 모델의 통합에는 적합하지 않지만 Conda 환경을 사용할 수 있지만 Conda 환경에서 학습한 모델을 직접 통합하는 것은 지원되지 않습니다.

 

  • 바라쿠다

  - Barracuda는 대부분의 기계 학습 통합에 가장 가까운 집합입니다. 사용이 간편하고 공무원은 직접 설치할 수 있는 완전한 설치 패키지를 제공합니다.

  - 더 많은 모델 지원: 완전 컨볼루션 신경망, 완전 고밀도 신경망, Tiny YOLO 등, 여기에서는 모든 ML-Agent 네트워크도 지원합니다(Unity는 여전히 자체 툴을 지원해야 함).

  - 많은 ONNX 네트워크에서 작동

  - C# 및 모델을 입력 및 출력 인터페이스로 사용하여 런타임 통합 지원

  - 단점은 지원하는 네트워크가 ONNX 모델, 완전 컨볼 루션 신경망, 완전 고밀도 네트워크 등에 따라 달라지며 수행할 수 있는 작업이 더 집중되어 이미지 인식, 이미지 스타일 변환, 표현 인식과 같은 가장 고전적인 모델이 더 적용 가능하며 안정적인 확산 및 기타 확산 모델 및 기타 모델과 같은 다른 신흥 네트워크를 지원하지 않습니다.

 

  • 자동 래핑 DLL

  - 이 부분은 보기에 좋고, 독립 개발자가 사용하는 작은 DLL은 괜찮고, 특정 네트워크에 대해 자신의 DLL을 수행하는 것은 비교적 쉽고, DLL 부분을 매우 명확하게 알아야 합니다.

  - 더 많은 유형의 신경망을 통합하고 싶다면 그것에 대해 생각하지 말고 Unity는 몇 년 동안 Barracuda를 사용해 왔으며 ML-Agent는 이를 해결할 수 없었고 독립 개발자는 여전히 시도하지 않습니다. 

 


 

1. 딥러닝 모델 준비

 

여기서는 많은 기계 학습에 대해 이야기하지 않겠습니다. 여기서는 딥 러닝 모델을 ONNX 형식으로 변환해야 하며, 이는 PyTorch 또는 ONNX의 자습서에서 볼 수 있습니다.

 

모델 다운로드 링크 : https://github.com/yangmingxian/Unity-Image-Classification/blob/a6e874b5684537f54cea46187b6ff2f5bca174fe/Assets/Models/ImageClass.onnx

 

라벨 다운로드 링크: https://github.com/yangmingxian/Unity-Image-Classification/blob/a6e874b5684537f54cea46187b6ff2f5bca174fe/Assets/Models/labels_map.txt

 

포함 내용: ImageClass.onnx(딥 러닝 모델) labels_map.txt(레이블 식별)

 

필자는 매우 간단한 onnx 모델을 사용하는데, 이는 모바일의 컴퓨팅 성능이 PC나 클러스터 서버보다 훨씬 낮기 때문에 매개변수가 적은 CNN 모델입니다. 이 모델은 또한 적은 양의 컴퓨팅 리소스로 30ms 이내에 인식 결과를 유지할 수 있습니다.

 

이 모델에 사용된 데이터 세트는 100,080개의 개체에 대한 레이블이 포함된 ImageNet 데이터 세트이며, 이 모델을 컴퓨터를 통해 실행하고 잘라낸 이미지를 사용하면 정확도가 약 <>%입니다. 휴대폰 픽셀 및 기타 요인으로 인해 카메라에서 얻은 이미지 인식률이 감소하고 인식 정확도가 휴대폰 카메라로 촬영한 사진의 품질과 밀접한 관련이 있다는 점은 주목할 가치가 있습니다.

 

우리가 사용해야 하는 것은 이 모델의 입력과 출력입니다.

 

입력: float32 [1,224,224,3](batch_size 1이 있는 224*224 RGB 이미지)

출력: float32[1,1000] 인덱스를 레이블에 매핑하는 점수이며, 점수가 클수록 태그에 해당할 가능성이 높습니다.

 

 

2. Unity의 씬 설정

 

유니티 씬 셋업은 간단하며, 캔버스만 있으면 되는데, 휴대폰 카메라에서 반환된 이미지를 표시하는 RawImage와 상호작용을 위한 Button이 있으며, 버튼의 텍스트는 인식의 결과입니다. 이 외에도 추론 및 인식 스크립트를 저장할 빈 개체를 설정했습니다.



3. 주요 스크립트 및 프로세스

 

3.1 휴대폰 카메라 디스플레이

 

첫 번째는 카메라의 이미지를 저장하고 표시하기 위해 RawImage를 선언하는 것이며, 여기서는 추가 AspectRatioFitter 컴포넌트를 설정하여 휴대폰 화면의 스케일과 일치하도록 RawImage 스케일을 설정합니다.

 

그런 다음 WebCamTexture를 사용하여 카메라의 푸티지를 읽습니다.

(여기서 추가 참고 사항은 RawImage의 Z축 회전을 -90으로 설정하여 확인할 수 있습니다. 그렇지 않으면 전화기 화면이 뒤집힙니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Start()
   {
       rawImage = GetComponent<RawImage>();
       fitter = GetComponent<AspectRatioFitter>();
       InitWebCam();
       if (webcamTexture.width > 100)
       {
           fitter.aspectRatio = (float)webcamTexture.width / (float)webcamTexture.height;
       }
   }

void InitWebCam()
   {
       string camName = WebCamTexture.devices[0].name;
       webcamTexture = new WebCamTexture(camName, Screen.width, Screen.height, 30);
       rawImage.texture = webcamTexture;
       webcamTexture.Play();
   }
cs

 

 

3.2 휴대폰 카메라 디스플레이

 

원본 카메라 이미지가 있으면 첫 번째 단계는 자르고 다시 샘플링하는 것입니다. 모델에 필요한 입력은 float32[1,224,224,3]이기 때문입니다.

 

  • 우선, 첫 번째 매개 변수는 batch_size가 1임을 나타내며 한 번에 하나의 그림만 읽습니다.
  • 제 2 및 제 3 파라미터들(224,224)은 이미지의 크기를 나타낸다.
  • 네 번째 매개 변수 3은 RGB 채널을 나타냅니다.

 

따라서 이미지를 정사각형으로 잘라야 하고 입력에 224*224 픽셀이 필요하기 때문에 이미지를 다운샘플링해야 하므로 데이터 소스의 픽셀이 높은지 확인하고 콘텐츠를 잃지 않고 필요한 모양으로 이미지를 다운샘플링해야 합니다.

 

먼저 결과를 저장할 새 renderTexture를 만들고 여기서는 ARGB32 형식을 사용합니다. 그런 다음 Graphics.Blit 함수를 사용하여 셰이더를 사용하여 이미지와 서브샘플을 자르고 배치합니다. 그런 다음 AsyncGPUReadback을 사용하여 GPU에서 처리한 renderTexture 데이터를 CPU로 전송하여 계산합니다.

1
2
3
4
5
6
7
8
9
10
11
12
public void ScaleAndCropImage(WebCamTexture webCamTexture, int desiredSize, UnityAction<byte[]> callback)
    {
        this.callback = callback;

        if (renderTexture == null)
        {
            renderTexture = new RenderTexture(desiredSize, desiredSize, 0, RenderTextureFormat.ARGB32);
        }

        scale.x = (float)webCamTexture.height / (float)webCamTexture.width;
        offset.x = (1 - scale.x) / 2f;
        Graphics.Blit(webCamTexture, renderTexture, scale, offset);
        AsyncGPUReadback.Request(renderTexture, 0, TextureFormat.RGB24, OnCompleteReadback);
    } 
cs

 

 

3.3 모델 추론

 

먼저 Barracuda가 제공하는 간단한 응용 프로그램인 필요한 모델 파일을 참조합니다.

ModelLoader를 사용하면 모델을 참조할 수 있고 WorkerFactory를 사용하면 추론 엔진을 구축할 수 있습니다. 여기서 WorkerFactory.Type.ComputePrecompiled는 GPU 추론을 사용하며, 물론 플랫폼과 모델에 따라 가장 적합한 엔진을 결정해야 하며, CPU 기반 엔진 등이 있습니다.

1
2
var model = ModelLoader.Load(modelFile);
worker = WorkerFactory.CreateWorker(WorkerFactory.Type.ComputePrecompiled, model);
cs

 

그런 다음 핵심 처리 흐름이 있습니다.

 

1. TransformInput을 호출하여 입력 텐서를 생성합니다.

2. 그런 다음 작업자에게 전화하십시오. Execute는 추론을 위해 신경망을 사용할 수 있습니다.

3. 그런 다음 노동자. PeekOutput은 가장 큰 출력을 얻습니다.

4. 가장 큰 인덱스를 얻기 위한 출력에 따라 LoadLabels 함수를 사용자 정의하여 인덱스에 해당하는 레이블을 가져오면 이 태그가 원하는 결과입니다.

5. 마지막으로, Unity의 GC는 이러한 리소스를 인수할 수 없으므로 tensor를 수동으로 릴리스해야 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
IEnumerator RunModelRoutine(byte[] pixels)
   {
       Tensor tensor = TransformInput(pixels);
       var inputs = new Dictionary<string, Tensor> {
           { INPUT_NAME, tensor }
       };
       worker.Execute(inputs);
       Tensor outputTensor = worker.PeekOutput(OUTPUT_NAME);
       //get largest output
       List<float> temp = outputTensor.ToReadOnlyArray().ToList();
       float max = temp.Max();
       int index = temp.IndexOf(max);
 
 
       //显示结果输出到UI Text
       uiText.text = labels[index];
       //dispose tensors
       tensor.Dispose();
       outputTensor.Dispose();
       yield return null;
   }
cs

 

3.4 인덱스에 따라 레이블 결과 가져오기

 

처음에는 결과 태그를 저장하기 위해 string[]을 설정하고 index를 사용하여 테이블을 조회하여 결과를 얻을 수 있습니다. 이 프로세스는 이전 섹션에서 UI 텍스트로 출력된 결과를 표시하는 것으로 시작됩니다.

1
2
3
4
5
6
void LoadLabels()
{
    // 得到index对应的标签
    var stringArray = labelAsset.text.Split('"').Where((item, index) => index % 2 != 0);
    labels = stringArray.Where((x, i) => i % 2 != 0).ToArray();
cs

 

리소스를 절약하기 위해 Button을 사용하여 함수를 호출하므로 매 프레임마다 모델 추론을 수행할 필요가 없으며 실제로 실시간 인식은 가능하지만 휴대전화의 프레임 속도는 급격히 떨어지므로 추론 인식을 수행할 때를 결정하기 위해 버튼을 사용하는 것이 좋습니다.

 

 

4. 결과 & 아우트로

 

테스트 후 내 휴대폰은 스트레스 없이 좋은 결과를 얻었습니다. 참고 : 리소스를 절약하기 위해 코드에서 30 프레임을 강제했습니다. 인정 결과는 다음과 같습니다.

 

4.1 향후 방향

 

이것은 매우 간단한 예일 뿐이며 실제 가치는 게임 엔진에서 올바른 기계 학습 모델을 사용하면 게임을 더 흥미롭게 만들 수 있다는 것입니다. Unity3D에서 객체 인식을 실현할 수 있으며 실시간 객체 추적 및 이미지 분할을 단기간에 달성할 수 있습니다. 장기적인 목표는 머신 러닝 모델에 의해 완전히 생성된 NPC와 같은 더 스마트한 카드 게임 AI 또는 스토리의 텍스트가 전적으로 모델에 의해 생성되는 스토리텔링 NPC와 같은 딥 러닝을 사용하는 모델로 게임 콘텐츠를 구축하는 것입니다. 이런 식으로 게임의 무작위성은 질적으로 도약할 것입니다.

 

저의 당장 목표는 리듬 감지 신경망을 독립형 데모에 통합하는 것이며, 플레이어들이 완전히 커스터마이징 가능한 배음 게임에서 자신만의 재미를 찾을 수 있기를 바랍니다. 그러나 conda 환경 및 신경망의 다양한 종속성을 Unity\Unreal 또는 다른 엔진에 원활하게 통합하려면 아직 갈 길이 멉니다.

 

이 간단한 이미지 인식 데모의 경우 모든 코드가 오픈 소스이므로 다음 주소에서 프로젝트 파일과 패키지 된 apk 파일을 다운로드하십시오.

(내 Android 10 Oneplus 6에서만 테스트했으며 파일은 IOS를 지원하지 않습니다.)

프로젝트 파일 주소: https://github.com/yangmingxian/Unity-Image-Classification

 

 

5. 기타

 

  • 블로그

 http://yangmingxian.com/2022/10/29/%E4%BD%BF%E7%94%A8Barracuda%E5%9C%A8Unity3D%E4%B8%AD%E5%AE%9E%E7%8E%B0%E5%9B%BE%E5%83%8F%E8%AF%86%E5%88%AB/

 

 

作者:CyberStreamer https://www.bilibili.com/read/cv19440824/ 出处:bilibili


 

위의 마지막 줄의 출처로부터 원본을 확인할 수 있다.

추가적인 사항으로는 위의 내용을 다른 외국인이 유튜브 동영상을 통해서 설명한 사항이 있다.

 

 

 

반응형

댓글