Связь через сокет TCP для потоковой передачи изображений и ошибки Texture2D - PullRequest
0 голосов
/ 07 марта 2020

Я хочу спросить о том, почему возникает эта ошибка. Во-первых, позвольте мне показать вам симптом моего проекта <- .gif файл загружен по этой ссылке. </p>

Я реализовал этот проект с Python сервером TCP и клиентом Unity. Когда вы смотрите этот файл .gif, Unity отображает изображение глубины потоковой передачи с сервера Python. Хотя Unity, кажется, показывает поток изображений в режиме реального времени, он также часто показывает «метку запроса».

Я слышал, что этот знак запроса отображается всякий раз, когда единице Texture2D не удается получить данные, но даже я добавил флаг, чтобы проверить, загружается ли изображение true или false, оно всегда true. Кроме того, когда вы видите консоль Unity, она также показывает, что есть некоторые потерянные байты по сравнению с консолью python (черная консоль для python).

Я уже тестировал этот TCP-сервер, который хорошо работает с python клиентом (этот код также загружен выше по ссылке). Итак, я предположил, что это проблема Unity, но я не знаю подробно и хочу знать, как это исправить.

Это мой Python TCP-сервер:

mport pyrealsense2 as d435
import socketserver
import socket
import cv2 
import numpy as np 
from queue import Queue
from _thread import *


# _Set queue 
enclosure_queue = Queue() 


# _Configures of depth and color streams 
pipeline = d435.pipeline()
config = d435.config()
config.enable_stream(d435.stream.depth, 640, 480, d435.format.z16, 30)
config.enable_stream(d435.stream.color, 640, 480, d435.format.bgr8, 30)



# _ D435 process 
def D435(queue):


    print("D435 processing", end="\n ")
    pipeline.start(config) # _Start streaming

    try:
        while True: 
            # _Wait for a coherent pair of frames: depth and color 
            frames = pipeline.wait_for_frames()            
            depth_frame, color_frame = (frames.get_depth_frame(), frames.get_color_frame())

            if not (depth_frame and color_frame): 
                print("Missing frame...", end="\n")
                continue

            # _Convert <pyrealsense2 frame> to <ndarray>
            depth_image = np.asanyarray(depth_frame.get_data()) # convert any array to <ndarray>
            color_image = np.asanyarray(color_frame.get_data())


            # _Apply colormap on depth image 
            #  (image must be converted to 8-bit per pixel first)



            depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.05), cv2.COLORMAP_BONE)
            #depth_colormap  = cv2.bitwise_not(depth_colormap ) # reverse image

            #print("Depth map shape = ", depth_colormap.shape)   


            # _Encoding 
            target_frame = depth_colormap

            #print(target_frame.shape)


            encode_param = [int(cv2.IMWRITE_JPEG_QUALITY),95]  # 0 ~ 100 quality 
            #encode_param = [cv2.IMWRITE_PNG_COMPRESSION,0] # 0 ~ 9 Compressiong rate 
            #encode_param = [int(cv2.IMWRITE_WEBP_QUALITY),95]  # 0 ~ 100 quality 



            result, imgencode = cv2.imencode('.jpg', target_frame, encode_param)  # Encode numpy into '.jpg'
            data = np.array(imgencode)

            stringData = data.tostring()   # Convert numpy to string
            print("byte Length: ", len(stringData))
            queue.put(stringData)          # Put the encode in the queue stack


            # __ Image show             
            images = np.hstack((color_image, depth_colormap)) # stack both images horizontally             

            cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
            cv2.imshow('RealSense', images)
            cv2.waitKey(1)        



    finally: 
        cv2.destroyAllWindows()

        # _Stop streaming 
        pipeline.stop()







class MyTCPHandler(socketserver.BaseRequestHandler):

    queue  = enclosure_queue 
    stringData = str()

    def handle(self):

        # 'self.request' is the TCP socket connected to the client     
        print("A client connected by: ", self.client_address[0], ":", self.client_address[1] )


        while True:
            try:
                # _server <- client 
                self.data = self.request.recv(1024).strip()   # 1024 byte for header 

                if not self.data: 
                    print("The client disconnected by: ", self.client_address[0], ":", self.client_address[1] )     
                    break                

                # _Get data from Queue stack 
                MyTCPHandler.stringData = MyTCPHandler.queue.get()     

                # _server -> client 
                #print(str(len(MyTCPHandler.stringData)).ljust(16).encode())  # <str>.ljust(16) and encode <str> to <bytearray>
                self.request.sendall(str(len(MyTCPHandler.stringData)).ljust(16).encode())
                self.request.sendall(MyTCPHandler.stringData)  


                #self.request.sendall(len(MyTCPHandler.stringData).to_bytes(1024, byteorder= "big"))
                #self.request.sendall(MyTCPHandler.stringData)             


            except ConnectionResetError as e: 
                print("The client disconnected by: ", self.client_address[0], ":", self.client_address[1] )     
                break


if __name__ == "__main__":

    # _Webcam process is loaded onto subthread
    start_new_thread(D435, (enclosure_queue,))  

    # _Server on
    HOST, PORT = socket.gethostname(), 8080 
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:    

        print("****** Server started ****** ", end="\n \n")     

        try: 
            server.serve_forever()

        except KeyboardInterrupt as e:
            print("******  Server closed ****** ", end="\n \n" )  

и скрипт Unity:

Client_unity.cs

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;



namespace imageStream_client
{ 

public class Client_unity : MonoBehaviour
{
    IPHostEntry ipHostInfo;
    IPAddress ipAddress; // IPv4 
    TcpClient client_sock;
    const int PORT = 8080;


    NetworkStream stream;

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("*****Unity frame started *****");

        ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
        ipAddress = ipHostInfo.AddressList[1];

        client_sock = new TcpClient(ipAddress.ToString(), PORT);
        Debug.Log("***** Client Connected to the server *****");

        stream = client_sock.GetStream();        

    }

    // Update is called once per frame
    void Update()
    {        
        //Debug.Log("**** Buffer streaming ****");

        // _Client -> Server 
        string message = "1";
        byte[] buff = Encoding.ASCII.GetBytes(message);

        stream.Write(buff, 0, buff.Length); // spend the byte stream into the Stream 


        // _Client <- Server 
        byte[] recvBuf = new byte[client_sock.ReceiveBufferSize]; // total receiveBuffer size         
        int readBytes = stream.Read(recvBuf, 0, recvBuf.Length);


        //Debug.Log($"total receiveBuffer length: {recvBuf.Length}");
        Debug.Log($"Real-read byte length: {readBytes}");


        // _Set display image 
        byte[] image = new byte[readBytes];
        Buffer.BlockCopy(recvBuf, 0, image, 0, readBytes);
        Viewer.instance.SetImageToDisplay(image);

        //Viewer.instance.SetImageToDisplay();
    }


    }
}

Viewer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace imageStream_client
{
    public class Viewer : MonoBehaviour
    {
        public RawImage rawimage;
        public AspectRatioFitter fit;

        #region Data
        public static Viewer instance;
        private byte[] imageToDisplay;
        #endregion

        #region Get Set
        public void SetImageToDisplay(byte[] image)
        {
            DisplayImage(image);
            //instance.imageToDisplay = image;
        }
        #endregion

        #region API
        internal static void DisplayImage(byte[] image)
        {
            if (image == null)
                return;
            if (image.Length > 0)
            {
                //이미지 크기 생성 이후 streaming 되는 이미지 크기에 맞게 수정 해야함
                Texture2D texture = new Texture2D(640, 480);

                //byte형식의 데이터를 texture2D 형식으로 읽음
                bool load = texture.LoadImage(image);
                if (load)
                {
                    //이미지를 화면에 입힘(Canvas 아래 RawImage)
                    instance.rawimage.texture = texture as Texture;
                    //이미지 가로 세로 비율
                    instance.fit.aspectRatio = 640 / 480;
                }
            }
        }
        #endregion
        // Start is called before the first frame update
        void Awake()
        {
            instance = this;
        }

        // Update is called once per frame
        void Update()
        {
            //DisplayImage(instance.imageToDisplay);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 11 марта 2020

Хорошо, я нашел проблему и исправил ее. Проблема заключалась в неправильной реализации TCP-сервера.

Я изменил python код сервера, как показано ниже:

class MyTCPHandler(socketserver.BaseRequestHandler):

    queue  = enclosure_queue 
    stringData = str()

    def handle(self):

        # 'self.request' is the TCP socket connected to the client     
        print("A client connected by: ", self.client_address[0], ":", self.client_address[1] )


        while True:
            try:
                # _server <- client 
                self.data = self.request.recv(1024).strip()   # 1024 byte for header 
                #print("Received from client: ", self.data)

                if not self.data: 
                    print("The client disconnected by: ", self.client_address[0], ":", self.client_address[1] )     
                    break                

                # _Get data from Queue stack 
                MyTCPHandler.stringData = MyTCPHandler.queue.get()     

                # _server -> client 
                #print(str(len(MyTCPHandler.stringData)).ljust(16).encode())  # <str>.ljust(16) and encode <str> to <bytearray>

                ###self.request.sendall(str(len(MyTCPHandler.stringData)).ljust(16).encode())  # <- Make this line ignored when you connect with C# client. 
                self.request.sendall(MyTCPHandler.stringData)  


                #self.request.sendall(len(MyTCPHandler.stringData).to_bytes(1024, byteorder= "big"))
                #self.request.sendall(MyTCPHandler.stringData)             


            except ConnectionResetError as e: 
                print("The client disconnected by: ", self.client_address[0], ":", self.client_address[1] )     
                break

Я тестировал этот код сервера с клиентом TCP python. Когда клиент подключается к серверу и получает данные о расходах, он должен знать размер буфера для передаваемых данных сервером. Итак, self.request.sendall(str(len(MyTCPHandler.stringData)).ljust(16).encode()) необходимо.

Однако класс TCPclient в C# отличается от класса python. ему не нужно получать явный размер буфера передаваемых данных с сервера (возможно, это было обработано автоматически внутри). Таким образом, вышеупомянутую строку кода python можно игнорировать.

Я перегрузил новую версию здесь .

0 голосов
/ 07 марта 2020

Проблема в том, что вы переместили свою функцию чтения в обновление, которое вызывает каждый кадр в Unity, поэтому вы просто не можете перехватить получение данных.

Решение:

void Start()
{
    Debug.Log("*****Unity frame started *****");

    ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
    ipAddress = ipHostInfo.AddressList[1];

    client_sock = new TcpClient(ipAddress.ToString(), PORT);
    Debug.Log("***** Client Connected to the server *****");

    stream = client_sock.GetStream(); 
    StartReceiveFunction();
}

void StartReceiveFunction()
{
    // Can be Application.IsRunning or whatever bool you need to receive data
    while (true)
    {
         if(stream.CanRead && client_sock.ReceiveBufferSize > 0){
         bytes = new byte[client_sock.ReceiveBufferSize];
         stream.Read(bytes, 0, client_sock.ReceiveBufferSize);             
         Viewer.instance.SetImageToDisplay(image);
    }       
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...