Hololens2는 Unity로 개발이 가능한데 MRTK 라는 특별한 툴킷을 사용한다.
그런데 생각보다 MRTK - unity 툴킷을 사용하면 제약이 너무 많다.
ex) Azure Nuget pkg 를 설치할 수 없음(가장 이상함),
기타 여러가지 pkg를 설치할 수 없었다.
만약 Nuget pkg로 설치한다면 에러가 나거나, namespace가 추가되지 않았다는 에러가 뜸
나는 Hololens2를 활용하여 Azure의 cognitive - OCR 기술을 unity에 추가하고 있었는데, C#으로 개발을 해야될 뿐더러 Azure에서 제공하는 pkg를 사용할 수 없으니 원시적으로 접근해 보았다.
를 참고하여 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 공식 기술문서 기재)
시간이 부족하여 급하게 진행돼서 아쉬운 부분이 많아서 추후 조금씩 고칠 예정.