Hololens2 Node.JS 소켓 통신

윤재희9108·2021년 9월 24일
0

Hololens2는 Unity로 개발이 가능한데 MRTK 라는 특별한 툴킷을 사용한다.

그런데 생각보다 MRTK - unity 툴킷을 사용하면 제약이 너무 많다.

ex) Azure Nuget pkg 를 설치할 수 없음(가장 이상함),
기타 여러가지 pkg를 설치할 수 없었다.
만약 Nuget pkg로 설치한다면 에러가 나거나, namespace가 추가되지 않았다는 에러가 뜸

나는 Hololens2를 활용하여 Azure의 cognitive - OCR 기술을 unity에 추가하고 있었는데, C#으로 개발을 해야될 뿐더러 Azure에서 제공하는 pkg를 사용할 수 없으니 원시적으로 접근해 보았다.

https://westus.dev.cognitive.microsoft.com/docs/services/computer-vision-v3-2/operations/56f91f2e778daf14a499f20d

를 참고하여 API를 직접 호출하는 작업을 진행하였는데, API는 Url Image는 쉽게 읽어올 수 있었으나, 실제 Image 파일을 Binary Image Data로 API를 호출하면 (C#이 익숙하지 않아 내가 실수 했을 가능성이 크다) 제대로 동작하지 않았다. -> 이 과정에서 오랜시간 소요

그렇다면 Hololens2에서 OCR데이터를 읽어오는 것이 아닌, Hololens2의 사진만 서버로 가지고와서 서버에서 Javascript로 Azure cognitive - OCR 기술을 사용하여 이미지를 분석한 후, 결과를 소켓 통신을 통해 다시 Hololens2로 전송하는 방법을 진행하였다.

다행히 Hololens2는 사진을 찍으면 자동으로 OneDrive에 저장이 됐고, OneDrive 연동이 된 PC에서 Node.JS 서버를 열어 localhost 소켓 통신으로 Hololens2와 데이터를 주고 받는데 성공 했다.

아래는 내 서버 Node.JS 코드

const async = require("async");
const fs = require("fs");
const https = require("https");
const path = require("path");
const createReadStream = require("fs").createReadStream;
const sleep = require("util").promisify(setTimeout);
const ComputerVisionClient =
  require("@azure/cognitiveservices-computervision").ComputerVisionClient;
const ApiKeyCredentials = require("@azure/ms-rest-js").ApiKeyCredentials;
const WebSocket = require("ws");

const key = "{cognitive Service Privite-Key}";
const endpoint = "{cognitive Service End-point}";

// <snippet_client>
const computerVisionClient = new ComputerVisionClient(
  new ApiKeyCredentials({ inHeader: { "Ocp-Apim-Subscription-Key": key } }),
  endpoint
);

const dir = "./";

fs.readdir(dir, function (err, filelist) {
  // fs모듈의 readdir함수를 사용해
  // 첫번째 인자로 파일 목록을 읽을 폴더(dir)를 가져오고
  // 콜백함수의 두번째 인자로 폴더(dir)의 파일목록(filelist)을 가져옴

  console.log(filelist[0]);
});

const arrayOcr = [];
// <snippet_functiondef_begin>
function computerVision() {
  async.series(
    [
      async function () {
        const STATUS_SUCCEEDED = "succeeded";
        const STATUS_FAILED = "failed";

        const handwrittenImageLocalPath = "ocr.jpg";

        console.log(
          "\nRead handwritten text from local file...",
          handwrittenImageLocalPath
        );
        const handwritingResult = await readTextFromFile(
          computerVisionClient,
          handwrittenImageLocalPath
        );
        printRecText(handwritingResult);

        // Perform read and await the result from local file
        async function readTextFromFile(client, localImagePath) {
          // To recognize text in a local image, replace client.read() with readTextInStream() as shown:
          let result = await client.readInStream(() =>
            createReadStream(localImagePath)
          );
          // Operation ID is last path segment of operationLocation (a URL)
          let operation = result.operationLocation.split("/").slice(-1)[0];

          // Wait for read recognition to complete
          // result.status is initially undefined, since it's the result of read
          while (result.status !== STATUS_SUCCEEDED) {
            //await sleep(1000);
            result = await client.getReadResult(operation);
          }
          return result.analyzeResult.readResults; // Return the first page of result. Replace [0] with the desired page if this is a multi-page file such as .pdf or .tiff.
        }

        // <snippet_read_print>
        // Prints all text from Read result
        function printRecText(readResults) {
          console.log("Recognized text:");
          for (const page in readResults) {
            if (readResults.length > 1) {
              console.log(`==== Page: ${page}`);
            }
            const result = readResults[page];

            if (result.lines.length) {
              for (const line of result.lines) {
                //console.log(line.words.map((w) => w.text).join(" "));
                arrayOcr.push(line.words.map((w) => w.text));
              }
            } else {
              console.log("No recognized text.");
            }
          }
        }
      },
      function () {
        return new Promise((resolve) => {
          resolve();
        });
      },
    ],
    (err) => {
      throw err;
    }
  );
}

function regExp(str) {
  var reg = /[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]/gi;
  //특수문자 검증
  if (reg.test(str)) {
    //특수문자 제거후 리턴
    return str.replace(reg, " ");
  } else {
    //특수문자가 없으므로 본래 문자 리턴
    return str;
  }
}

//WebSocket 서버 오픈
const wss = new WebSocket.Server({ port: 8080 }, () => {
  console.log("server start");
});

// Client에게 메세지를 받으면 data를 통해 OCR 결과값을 전송한다.
wss.on("connection", (ws) => {
  ws.on("message", (data) => {
    console.log("data received : " + data);

    console.log(regExp(arrayOcr.toString()));
    ws.send(regExp(arrayOcr.toString()));
  });
});

wss.on("listening", () => {
  console.log("server is listening on port 8080");
});

computerVision();

아래는 Unity 부분 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using WebSocketSharp;
public class OCR_TEST_Debug : MonoBehaviour
{
    // Start is called before the first frame update
	// e.Data를 통해 데이터를 수신
    WebSocket ws;
    void Start()
    {
        ws = new WebSocket("ws://localhost:8080");
        ws.OnMessage += (sender, e) =>
        {
            Debug.Log("Message received from " + ((WebSocket)sender).Url + ", Data : " + e.Data);

        };
        ws.Connect();
    }

    // Update is called once per frame
    void Update()
    {
        if (ws == null)
        {
            return;
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ws.Send("Hello");
        }
    }

    //왼쪽버튼이 눌렸을때 활성화
    public void Popup_ON()
    {
        Debug.Log("팝업 ON");
    }

    //오른쪽버튼이 눌렸을때 활성화
    public void Popup_OFF()
    {
        Debug.Log("팝업 OFF");
    }
}

Hololens2는 아직 출시된지 얼마 안된 제품이다보니 개발에 제한이 많이 걸린 것 같아보인다.

Azure의 기술을 사용하는것도 Unity로 바로 연결하는것이 아닌, 특별한 방식이 존재하는걸로 보인다. (Hololens2 공식 기술문서 기재)

시간이 부족하여 급하게 진행돼서 아쉬운 부분이 많아서 추후 조금씩 고칠 예정.

profile
개발의 ㄱ자도 모르네!

0개의 댓글