LoginSignup
12
10

More than 5 years have passed since last update.

TCP通信でHoloLensのMRC画像をPCへ送信

Last updated at Posted at 2018-12-21

HoloLens Advent Calendar 2018の14日目の記事です。
HoloLensのMRC(MixedRealityCapture)画像をTCP通信を使って、PCへ送信してみました。

開発環境

クライアント側

  • HoloLens RS5
  • Visual Studio 2017(15.9.2)
  • Unity 2017.4.11f1
  • HoloToolkit-Unity-2017.4.3.0.unitypackage
  • HoloToolkit-Unity-Examples-2017.4.3.0.unitypackage

サーバー側

  • Windows 10 PC
  • Python 3.7

クライアント側(HoloLens)

Connectボタンをタップすることでサーバーへ接続し、タップしたときのMRC画像をサーバーへ送信します。TcpNetworkClientManager.csはアキヒロさんのを参考にSendImage関数を追加しています。コードはこちら

hololenstcp01.PNG

● Client.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.WSA.WebCam;
using HoloToolkit.Unity.InputModule;
using System;
using System.Linq;

public class Client : MonoBehaviour, IInputClickHandler
{

    public string IP;
    public int port;
    public Text connectButtonText;
    public TextMesh debugText;

    private PhotoCapture photoCaptureObject = null;
    private Texture2D targetTexture = null;
    private Resolution cameraResolution;

    private TcpNetworkClientManager client = null;

    // Use this for initialization
    void Start () {
        cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
        debugText.text = cameraResolution.width.ToString() + " " + cameraResolution.height.ToString();
        targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
        // targetTexture = new Texture2D(480, 270);
        // InputManager.Instance.PushFallbackInputHandler(gameObject);
        InputManager.Instance.AddGlobalListener(gameObject);
    }

    void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
    {
        photoCaptureObject.Dispose();
        photoCaptureObject = null;
    }

    void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
    {
        photoCaptureFrame.UploadImageDataToTexture(targetTexture);
        //byte[] texByte = targetTexture.EncodeToJPG();
        //byte[] image = new byte[texByte.Length];
        //Array.Copy(texByte, image, texByte.Length);

        byte[] image = targetTexture.GetRawTextureData();
        client.SendImage(image);
        photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
    }

    public void OnInputClicked(InputClickedEventData eventData)
    {
        if (client != null)
        {
            PhotoCapture.CreateAsync(true, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
                CameraParameters cameraParameters = new CameraParameters();
                cameraParameters.hologramOpacity = 0.9f;
                cameraParameters.cameraResolutionWidth = cameraResolution.width;
                cameraParameters.cameraResolutionHeight = cameraResolution.height;
                cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
                photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
                });
            });
        }
    }

    public void ConnectButtonClicked()
    {
        if(client != null)
        {
            Debug.Log("Disconnected");
            connectButtonText.text = "Connect";
            client = null;
        }
        else
        {
            Debug.Log("Connected");
            client = new TcpNetworkClientManager(IP, port);
            connectButtonText.text = "Disconnect";
        }
    }
}

● TcpNetworkClientManager.cs

using System;
#if UNITY_UWP
using System.IO;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
#endif

public class TcpNetworkClientManager
{
#if UNITY_UWP
    private Stream stream = null;
    private StreamWriter writer = null;
#endif

    public TcpNetworkClientManager(string IP, int port)
    {
#if UNITY_UWP
        Task.Run(async () => {
            StreamSocket socket = new StreamSocket();
            await socket.ConnectAsync(new HostName(IP),port.ToString());
            stream = socket.OutputStream.AsStreamForWrite();
            writer = new StreamWriter(stream);
            StreamReader reader = new StreamReader(socket.InputStream.AsStreamForRead());
            try
            {
                string data = await reader.ReadToEndAsync();
            }
            catch (Exception) { }
            writer = null;
        });
#endif
    }

    public void SendMessage(string data)
    {
#if UNITY_UWP
        if (writer != null) Task.Run(async () =>
        {
            await writer.WriteAsync(data);
            await writer.FlushAsync();
        });
#endif
    }

    public void SendImage(byte[] image)
    {
#if UNITY_UWP
        if (stream != null) Task.Run(async () =>
        {
            await stream.WriteAsync(image, 0, image.Length);
            await stream.FlushAsync();
        });
#endif
    }
}

サーバー側(Python)

サーバーを起動し、接続を待ちます。
接続できたら受信を待ち、画像を受信したら表示します。

● tcpserver.py

import socket
import numpy as np
import cv2

def main():
    host = "192.168.0.31"
    port = 4000

    width = 2048
    height = 1152

    serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    serversock.bind((host,port))
    serversock.listen(10)

    print('Waiting for connections...')
    clientsock, client_address = serversock.accept()
    print("Succeeded in Connection!")

    total = 0
    buffer_size = 4*width*height
    while True:
        data = b''
        data = clientsock.recv(buffer_size)
        print(len(data))
        if len(data) == buffer_size:
            tmp = np.frombuffer(data, np.uint8, -1)
            img = tmp.reshape(height, width, 4)
            img = cv2.resize(img, (480, 270))
            img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
            img = cv2.flip(img, 0)
            cv2.imshow("img", img)
            cv2.imwrite("out.jpg", img)
            cv2.waitKey(0)
            cv2.destroyAllWindows()
    clientsock.close()

if __name__ == '__main__':
    main()

実行

1.Anaconda Promptを開き、tcpserver.pyを実行しておきます。
2.HoloLensTCPClientを起動し、Connectボタンをタップし接続します。
3.タップ時に、MRC(Mixed Reality Capture)の画像を送信します。
4.サーバー側に表示されれば、完了です。
out - rawdata.jpg

まとめ

  • タップしたときのMRC(Mixed Reality Capture)を、TCP通信を使ってHoloLensからPCへ送信できた
  • Editorで動作しないのマジ面倒くさい
  • bufferデカすぎて、安定しない

参考文献

[1] HoloLensでSharingする方法(TCP編)
[2] Python server, UWP(C#)client間でTCP通信がしたい

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10