1. Введение
Среда разработки: Hololens2 + unity2020.3 +vs2019+python3.8.
Недавно я изучал, как обеспечить связь гололенов с компьютером.Изучив информацию, я обнаружил, что связь может быть достигнута с помощью Socket .
Разрешение хололенам взаимодействовать с компьютером на самом деле означает, что Unity может взаимодействовать с компьютером, а затем разрабатывать пользовательский интерфейс на стороне хололенов в соответствии с потребностями.
Процесс проекта примерно заключается в написании сценария C# на Unity для захвата и отправки изображений и использовании Python на компьютере для получения и сохранения изображений.
Что касается пользовательского интерфейса, создайте две кнопки Hololens для подключения двух методов: один управляет захватом фотографий, а другой — отправкой захваченных фотографий на сервер Python. Последующая работа заключается в использовании полученных изображений для обработки изображений и так далее.
Ниже приведено изображение проекта Unity.
2.Код проекта
Код клиента:
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using System;
using System.Net.Sockets;
using System.Net;
using System.Collections.Generic;
using UnityEngine.Windows.WebCam;
using Microsoft.MixedReality.Toolkit.UI;
public class CaptureAndSend : MonoBehaviour
{
// Hololens摄像头对象
PhotoCapture photoCaptureObject = null;
// 捕获的图片对象
Texture2D targetTexture = null;
// 图片的宽和高
int width = 1280;
int height = 720;
// 用于控制图片捕获和发送的按钮
public Interactable CaptureButton;
public Interactable SendButton;
// 服务器IP地址和端口号
string serverIP = "172.22.90.152";
int serverPort = 8888;
private TcpClient client;
// 图片数据的字节数组
byte[] imageBytes = null;
void Start()
{
client = new TcpClient(serverIP, serverPort);
// 获取按钮组件并添加点击事件
CaptureButton.OnClick.AddListener(CaptureImage);
SendButton.OnClick.AddListener(SendImage);
}
void OnDestroy()
{
// 释放摄像头资源
if (photoCaptureObject != null)
{
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
}
void CaptureImage()
{
// 初始化PhotoCapture对象并设置参数
PhotoCapture.CreateAsync(true, OnPhotoCaptureCreated);
}
void SendImage()
{
if (imageBytes != null)
{
try
{
// 创建TcpClient连接服务器
// 获取网络流并发送图片数据
NetworkStream stream = client.GetStream();
stream.Write(imageBytes, 0, imageBytes.Length);
Debug.Log("Image sent to server.");
// 关闭网络流和TcpClient连接
stream.Close();
client.Close();
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
}
else
{
Debug.Log("imageBytes变量为空");
}
}
void OnPhotoCaptureCreated(PhotoCapture captureObject)
{
// 将PhotoCapture对象赋值给成员变量
photoCaptureObject = captureObject;
// 创建CameraParameters对象
CameraParameters camParameters = new CameraParameters();
camParameters.hologramOpacity = 0.0f;
camParameters.cameraResolutionWidth = width;
camParameters.cameraResolutionHeight = height;
camParameters.pixelFormat = CapturePixelFormat.PNG;
// 捕获图片并保存到Texture2D对象中
captureObject.StartPhotoModeAsync(camParameters, OnPhotoModeStarted);
}
void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result)
{
if (result.success)
{
targetTexture = new Texture2D(width, height, TextureFormat.RGB24, false);
// 拍摄图像并保存到Texture2D对象中
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
}
else
{
Debug.LogError("Unable to start photo mode!");
}
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
if (result.success)
{
// 将图片数据转换为字节数组
imageBytes = ConvertToByteArray(photoCaptureFrame);
// 将图片数据显示到屏幕上
targetTexture.LoadImage(imageBytes);
//GetComponent<Renderer>().material.mainTexture = targetTexture;
Debug.Log("Image captured.");
}
else
{
Debug.LogError("Failed to capture photo.");
}
// 停止PhotoCapture模式并释放资源
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
// 释放PhotoCapture资源
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
byte[] ConvertToByteArray(PhotoCaptureFrame photoCaptureFrame)
{
// 获取图像数据缓冲区
List<byte> buffer = new List<byte>();
photoCaptureFrame.CopyRawImageDataIntoBuffer(buffer);
// 将List<byte>转换为byte[]
byte[] bytes = buffer.ToArray();
return bytes;
}
}
Код на стороне сервера:
import socket
import cv2
import numpy as np
import os
from datetime import datetime
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = '172.22.90.152'
# 设置一个端口
port = 8888
# 绑定端口号
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5)
print('等待客户端连接...')
# 接受客户端连接
client_socket, address = server_socket.accept()
print('连接地址:', address)
# 接收图片数据
data = b''
while True:
packet = client_socket.recv(4096)
if not packet:
break
data += packet
# 将接收到的字节数据转换为OpenCV格式的图片
img_data = np.frombuffer(data, dtype=np.uint8)
img = cv2.imdecode(img_data, cv2.IMREAD_COLOR)
print(img_data)
print('Received image size:', img.shape)
# 在窗口中显示接收到的图片
cv2.imshow('Received Image', img)
print("开始保存文件")
# 将接收到的图片保存到指定文件夹中
save_folder = './received_images'
if not os.path.exists(save_folder):
os.makedirs(save_folder)
print("创建文件夹")
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
filename = os.path.join(save_folder, f'received_image_{current_time}.jpg')
cv2.imwrite(filename, img)
print(f'Saved received image to {filename}')
cv2.waitKey()
# 关闭连接
client_socket.close()
server_socket.close()
3. Процесс разработки
3.1 Клиент
Создайте проект Unity, преобразуйте его в платформу UWP , импортируйте в проект набор инструментов MRTK и добавьте сцену MR по умолчанию.
Импортируйте XR Plug-in Management , создайте файл сценария C# в соответствии с приведенными ниже настройками
и скопируйте в него приведенный выше клиентский код. Строка serverIP в коде может быть установлена на IP-адрес компьютера в той же локальной сети . При отладке в Unity сначала можно установить значение 127.0.0.1.
Создайте пустой узел с именем HololensCamera, чтобы смонтировать только что созданный скрипт.
Затем свяжите со сценарием две кнопки.
Откройте панель инструментов. В ней много кнопок Hololens. Я выбрал первую в разделе NearMenus. Вы
можете переименовать строку меню кнопок в Menu. Под узлом ButtonCollection находятся три кнопки. Поскольку используются только две кнопки, я удалил одну. Вы можете переименовать эти два узла кнопок в «Захват» и «Отправить», чтобы облегчить различие.
Значок можно установить в инспекторе кнопок, а отображаемый текст можно установить в IconAndText.
Перетащите две кнопки на две необходимые ссылки на кнопки сценария установки пустого узла.
Таким образом, будет создан единый проект.
3.2 Серверная часть
Создайте проект в pycharm, скопируйте в него код Python и импортируйте необходимые пакеты. IP-адрес также должен быть установлен на IP-адрес вашего собственного компьютера. Если вы отлаживаете этот компьютер, вы можете сначала установить номер порта 127.0.0.1 и синхронизировать его с C#.
4. Отладка
Сначала запустите код на стороне сервера, а затем запустите unity, чтобы запустить игровой режим. В это время в рабочем окне на стороне сервера будет выводиться информация о подсказке для подключения клиента. В Unity сначала нажмите кнопку захвата, а затем кнопку отправки. В это время компьютер получит изображение и сохранит его в папке полученное_изображение в той же папке проекта Python.
Если при отладке в Unity проблем не возникло, разверните его в Hololens.
Запустите сервер, откройте программу голо, нажмите кнопку захвата, а затем отправьте его, чтобы посмотреть, сохранилась ли картинка в папке.