OpenCV

OpenCV+Unity 실시간 마커 검출 (카메라 사용)

Dean83 2022. 4. 15. 23:34

아래 마커검출 예제를 통해 정지이미지에서 마커가 검출되는것을 확인하였다. 

 

https://dean83.tistory.com/73

 

OpenCV+Unity 마커 검출

Aruco 에서 미리 정의한 마커들을 검출하는 기능 1. 간략 설명 - 검출할 마커의 미리 정의한 데이터 획득 - 원본 이미지를 grayscale로 변환 - 미리 정의한 데이터와 기본 DetectorParameter 클래스 값을 이

dean83.tistory.com

 

간단히 해보려 하는것은, 아래 카메라 띄우기와 접목시켜 실시간으로 마커를 검출하는 앱을 만들고자 한다. 

결론부터 말하자면, 동작을 하는데 끊김이 너무심하다. 퍼포먼스 문제가 있다.

내 폰이 예전꺼라 그런건진 모르겠다.  어쩌면 네이티브 OpenCV가 아니기에 퍼포먼스 저하가 발생하는걸 수 도 있고, 원본 c++ 라이브러리 구현 알고리즘을 고쳐야 할지도 모르겠다. 속도 저하의 원인은 Mat <->Texture 변환이다

아마 지난번 얼굴인식 때처럼 인식된 마커의 좌표값만 얻고, 유니티에 미리 구현된 좌표 이미지를 해당 좌표로 이동
하게끔 구현한다면 렉없이 빠르게 동작할 것이다. 화면에 마커를 초록색 테두리로 그리진 못하겠지만 말이다. 

 

쨌든, 카메라 사용법은 여기를 참고 한다. https://dean83.tistory.com/69?category=1088148 

 

카메라 띄우기

사실, ios도 되는 코드라고 본다. 단, 맥북이 없는 관계로 ios 빌드를 해서 돌려볼 수가 없다. 처음에는 OpenCV 에서 제공하는 기능이라 생각했지만, 유니티에서 기본으로 제공하는 코드였고, OpenCV도

dean83.tistory.com

 

1. 간략 설명

    - 메인 동작 스크립트에서 카메라 실행 및 WebcamTexture를 화면에 뿌려주는 작업을 한다

    - 메인 동작 스크립트에서 스레드를 실행, 무한루프로 마커 인식 스크립트 함수를 계속 호출한다

    - 메인 동작 스크립트에서 마커 인식동작 완료시 받은 texture를 화면에 뿌려준다. 
    - 마커 인식 스크립트에선, grayscale 변환, DetectorParameters와 Dictionary 데이터를 통해 마커를 인식하고

       결과를 메인 동작 스크립트에 delegate를 이용해 전달한다. 

* 사실 delegate 안써도 된다. 이것저것 테스트하다 남은 잔재라고 보면 된다. 최적화 되어 있는 스크립트도 아니다. 
* 스레드 대신 Coroutine을 사용해도 되나, 아래와 같은 차이점이 존재한다. 
   - Coroutine 사용시, Coroutine을 쓰지 않는것과 동일한 결과가 나온다. 그냥 계속 느리다.

   - 스레드 사용시, Update 문에서 raw 이미지를 계속 화면에 보여주고, 드문드문 마커를 인식한 이미지를 보여준다. 

     카메라 영상 재생이 Coroutine 보단 부드러운 경우가 있으나 화면이 흔들리는 듯한 느낌이 난다
     (마커 인식 처리속도 문제)

 

2. 메인 동작 스크립트 전체

using OpenCvSharp;
using OpenCvSharp.Aruco;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;

public class docutest : MonoBehaviour
{
    private MarkerDetect markerDetect;
    private WebCamTexture camTexture;
    private Texture2D markerResult;

    [SerializeField]
    private RawImage rawImage;

    // Start is called before the first frame update
    void Start()
    {
        markerDetect = new MarkerDetect(MarkerDetected, ref markerResult);
        SetCamera();
    }

    // Update is called once per frame
    void Update()
    {
        if (IsCamTextureReady() == false)
            return;

        rawImage.texture = camTexture;
        rawImage.GetComponent<RectTransform>().localEulerAngles = new Vector3(0, 0, -camTexture.videoRotationAngle);
        rawImage.GetComponent<RectTransform>().sizeDelta = new Vector2(camTexture.width, camTexture.height);
    }

    private void SetCamera()
    {
        string cameraName = "";
        if (WebCamTexture.devices.Length > 0)
            cameraName = WebCamTexture.devices[0].name;

        camTexture = new WebCamTexture(cameraName);
        camTexture.requestedHeight = Screen.height;
        camTexture.requestedWidth = Screen.width;
        camTexture.filterMode = FilterMode.Trilinear;

        camTexture.Play();
        ThreadPool.UnsafeQueueUserWorkItem(MarkerDetect, null);
    }

    private bool IsCamTextureReady()
    {
        if (camTexture == null || camTexture.isPlaying == false)
            return false;

        return true;
    }

    private void MarkerDetect(object item)
    {
        while (true)
        {
            if (IsCamTextureReady() == false)
            {
                continue;
            }

            if (camTexture == null)
            {
                Debug.Log("texture is null");
                return;
            }

            markerDetect.DetectMarker(camTexture);
        }
    }

    private void MarkerDetected(Texture2D resultTexture)
    {
        if (resultTexture == null)
            return;

        isWorking = false;
        rawImage.texture = resultTexture;

        rawImage.GetComponent<RectTransform>().localEulerAngles = new Vector3(0, 0, -camTexture.videoRotationAngle);
        rawImage.GetComponent<RectTransform>().sizeDelta = new Vector2(resultTexture.width, resultTexture.height);
    }
}

3. 마커 인식 스크립트

using OpenCvSharp;
using OpenCvSharp.Aruco;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

public delegate void DetectionFinished(Texture2D resultTexture);

public class MarkerDetect
{
    private DetectionFinished detectionFinished;
    private Texture2D resultTexture;
    SynchronizationContext m_Context;

    public MarkerDetect(DetectionFinished finished, ref Texture2D resultTextureItem)
    {
        m_Context = SynchronizationContext.Current;
        detectionFinished = finished;
        resultTexture = resultTextureItem;
    }

    public void DetectMarker(WebCamTexture texture)
    {
        DetectProcess(texture);
    }

    private void DetectProcess(object item)
    {
        WebCamTexture texture = (WebCamTexture)item;

        if (texture == null)
        {
            Debug.Log("texture is null");
            return;
        }

        Mat source = OpenCvSharp.Unity.TextureToMat(texture);
        if (source == null)
        {
            Debug.Log("source is null");
            return;
        }

        Mat workingMat = new Mat();
        Cv2.CvtColor(source, workingMat, ColorConversionCodes.BGR2GRAY);

        DetectorParameters parameters = DetectorParameters.Create();
        Dictionary dictionary = CvAruco.GetPredefinedDictionary(PredefinedDictionaryName.Dict6X6_1000);

        Point2f[][] corners;
        int[] ids;
        Point2f[][] rejected;

        CvAruco.DetectMarkers(workingMat, dictionary, out corners, out ids, parameters, out rejected);
        if (corners != null && corners.Length > 0 && ids != null && ids.Length > 0)
            CvAruco.DrawDetectedMarkers(source, corners, ids);
       
        m_Context.Post(delegate
        {
            if (detectionFinished != null)
            {
                resultTexture = OpenCvSharp.Unity.MatToTexture(source, resultTexture);
                detectionFinished(resultTexture);
            }
        },null);    
    }
}

 

4. 인식 결과

    - 모니터에 마커를 띄우고 휴대폰 카메라로 인식해봤다. 마커 border에 초록색 1픽셀 녹색 테두리가 생기고, 중앙부

      에 아이디가 표시된다. 단, 카메라 이미지가 90도 꺾여있어서 돌리다보니 id 표기가 90도 뒤집혀 있다