Изображения, передаваемые через tcp, делятся на 3 с отсутствующим средним - PullRequest
0 голосов
/ 30 сентября 2018

Example of result

Цель проекта - потоковая передача видео, снятого с хоста python, на клиент ac # через сокеты tcp.

Relavent скрипт сервера python2:

import cv2
import numpy as np
import socket
from threading import Thread

_continue = True
def imageStreamer4():
    global _continue
    cam = cv2.VideoCapture(0)
    camSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    camSocket.bind(("",8081))
    camSocket.listen(1)
    # set flip image to false if you don't want the image to be flipped
    flipImage = True

    while _continue:
        try:
            client,address = camSocket.accept()
            print("client connected")
            ret,camImage = cam.read()
            if flipImage:
                camImage = cv2.flip(camImage,1)

            #uncomment the below code to view the webcam stream locally
            """
            cv2.imshow('image',camImage)
            if cv2.waitKey(1) == 27: 
                break  # esc to quit
            """
            byteString = bytes(cv2.imencode('.jpg', camImage)[1].tostring())
            fileSize = len(byteString)
            totalSent = 0
            client.send(str(fileSize).encode())

            sizeConfirmation = client.recv(1024)

            totalSent = 0
            while totalSent < fileSize:
                totalSent += client.send(byteString[totalSent:])

            print(str(fileSize), str(totalSent),sizeConfirmation.decode('utf-8'))

        except Exception as e:
            print(e)
            print("shutting down video stream")
            _continue = False

    print("video stream exited.")

Соответствующий код клиента c #:

using System.Collections;
using UnityEngine;
using System;
using System.Net;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using UnityEngine.UI;
using System.IO;

void getVideoStream()
    {
        byte[] header; 
        int recieved;
        int fileSize;
        NetworkStream dataStream;
        MemoryStream ms;
        while (connectCam)
        {
            fileSize = 0;
            recieved = 0;
            camClient = new TcpClient(camIP, camPort);

            //get header
            dataStream = camClient.GetStream();
            while (!dataStream.DataAvailable)
            {
                //waste time
            }
            header = new byte[1024];
            dataStream.Read(header, 0, header.Length);
            fileSize = Int32.Parse(Encoding.Default.GetString(bytesReducer(header)));
            byte[] result = Encoding.ASCII.GetBytes(fileSize.ToString());

            //send response
            dataStream.Write(result, 0, result.Length);

            ms = new MemoryStream();
            while (!dataStream.DataAvailable)
            {
                //waste time
            }
            while (recieved < fileSize)
            {
                byte[] data = new byte[camClient.ReceiveBufferSize];
                recieved += dataStream.Read(data, 0, data.Length);
                ms.Write(data, 0, data.Length);

            }
            //the below class simply sends function calls from secondary thread back to the main thread
            UnityMainThreadDispatcher.Instance().Enqueue(convertBytesToTexture(ms.ToArray()));
            dataStream.Close();
            camClient.Close();

        }
    }

void convertBytesToTexture(byte[] byteArray) {
        try
        {
            camTexture.LoadImage(byteArray); //Texture2D object
            camImage.texture = camTexture; //RawImage object
        }
        catch (Exception e)
        {
            print(e);
        }
    }

Счетчик байтов соответствует отправленным и полученным совпадениям, как и должно быть.По общему признанию я плохо знаком с работой с сокетами, но я почти уверен, что данные прибывают целыми и неповрежденными.К сожалению, я действительно понятия не имею, почему изображение разделяется как есть.(Как показано на рисунке выше). Если это вообще важно, функции сервера и клиента выполняются в своих отдельных потоках.

Я запустил сценарии на отдельных хостах и ​​клиентах, и результаты остались прежними. Если требуется какая-либо другая информация, просто спросите.Я буду рад обновить по мере необходимости.

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

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

  • Разделитель : используйте набор байтов для разделения каждого изображения (но этот набор байтов не должен повторяться в вашем потоке), например:

1 - отправьте байты изображения

2-отправить разделитель

на клиенте, который вы начинаете читать, пока не достигнете разделителя

  • Рассмотрите возможность определения протокола и сначала отправьте размер изображения, а затем отправьтенапример, данные изображения:

1-первые 8 байтов всегда имеют размер изображения

2-, затем байты изображения

и когда высначала читайте данные на клиенте, вы всегда читаете 8 байтов, а затем, исходя из этого размера, читаете остальные данные.

  • Другой способ - использовать данные фиксированного размера (что довольно сложно для вас вв этом случае) вы получаете байтовый массив фиксированного размера на сервере, и если данные изображенияЕсли байтовый массив заполнен нулями, то клиент всегда читает байты, равные размеру массива сервера.

Байт отправки сервером [2048], и клиент читает до байтов,прочитанная досягаемость 2048

0 голосов
/ 01 октября 2018

Мне удалось правильно собрать картинку, удалив пустое пространство, вставленное с помощью следующего кода:

while (recieved < fileSize)
{
     byte[] data = new byte[camClient.ReceiveBufferSize];
     recieved += dataStream.Read(data, 0, data.Length);
     ms.Write(data, 0, data.Length);

}

, заменив его следующим:

int increment = 0;
while (recieved < fileSize)
{
     byte[] data = new byte[camClient.ReceiveBufferSize];
     increment = dataStream.Read(data, 0, data.Length);
     recieved += increment; 
     ms.Write(data.Take(increment).ToArray(), 0, increment);

}

Поэтому вместо того, чтобы взятьмассив размера клиентского буфера в потоке памяти (даже если он не был заполнен), он уплотняется только до объема информации, полученной с помощью метода read.Это эффективно удалило все пустое пространство в полученном изображении.

...