web-dev-qa-db-fra.com

Envoi d'images vidéo en direct sur le réseau dans python opencv

J'essaie d'envoyer une image vidéo en direct que je capture avec ma caméra à un serveur et de les traiter. J'utilise opencv pour le traitement d'image et python pour la langue. Voici mon code

client_cv.py

import cv2
import numpy as np
import socket
import sys
import pickle
cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))
while True:
    ret,frame=cap.read()
    print sys.getsizeof(frame)
    print frame
    clientsocket.send(pickle.dumps(frame))

server_cv.py

import socket
import sys
import cv2
import pickle
import numpy as np
Host=''
PORT=8089

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print 'Socket created'

s.bind((Host,PORT))
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'

conn,addr=s.accept()

while True:
    data=conn.recv(80)
    print sys.getsizeof(data)
    frame=pickle.loads(data)
    print frame
    cv2.imshow('frame',frame)

Ce code me donne une erreur de fin de fichier, ce qui est logique car les données arrivent toujours sur le serveur et pickle ne sait pas quand terminer. Ma recherche sur Internet m'a fait utiliser du cornichon mais cela ne fonctionne pas jusqu'à présent.

Note: J'ai mis conn.recv À 80 parce que c'est le nombre que j'obtiens quand je dis print sys.getsizeof(frame).

15
atayenel

Quelques choses:

  • utilisez sendall au lieu de send car vous n'êtes pas sûr que tout sera envoyé en une seule fois
  • pickle est correct pour la sérialisation des données mais vous devez créer un protocole de votre choix pour les messages que vous échangez entre le client et le serveur, de cette façon, vous pouvez connaître à l'avance la quantité de données à lire pour le décryptage (voir au dessous de)
  • pour recv vous obtiendrez de meilleures performances si vous recevez de gros morceaux, alors remplacez 80 par 4096 ou même plus
  • attention à sys.getsizeof: il renvoie la taille de l'objet en mémoire, qui n'est pas la même que la taille (longueur) des octets à envoyer sur le réseau; pour une chaîne Python les deux valeurs ne sont pas du tout les mêmes
  • tenez compte de la taille du cadre que vous envoyez. Le code ci-dessous prend en charge un cadre jusqu'à 65535. Changez "H" en "L" si vous avez un cadre plus grand.

Un exemple de protocole:

client_cv.py

import cv2
import numpy as np
import socket
import sys
import pickle
import struct ### new code
cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))
while True:
    ret,frame=cap.read()
    data = pickle.dumps(frame) ### new code
    clientsocket.sendall(struct.pack("H", len(data))+data) ### new code

server_cv.py

import socket
import sys
import cv2
import pickle
import numpy as np
import struct ## new

Host=''
PORT=8089

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print 'Socket created'

s.bind((Host,PORT))
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'

conn,addr=s.accept()

### new
data = ""
payload_size = struct.calcsize("H") 
while True:
    while len(data) < payload_size:
        data += conn.recv(4096)
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("H", packed_msg_size)[0]
    while len(data) < msg_size:
        data += conn.recv(4096)
    frame_data = data[:msg_size]
    data = data[msg_size:]
    ###

    frame=pickle.loads(frame_data)
    print frame
    cv2.imshow('frame',frame)

Vous pouvez probablement optimiser tout cela beaucoup (moins de copie, en utilisant l'interface tampon, etc.) mais au moins vous pouvez avoir l'idée.

15
mguijarr

Après des mois de recherche sur Internet, voici ce que j'ai trouvé, je l'ai soigneusement emballé dans des classes, avec des tests unitaires et de la documentation comme SmoothStream check it out, c'était la seule version simple et fonctionnelle de streaming que je pouvais trouver n'importe où.

J'ai utilisé ce code et enroulé le mien autour de lui.

Viewer.py

import cv2
import zmq
import base64
import numpy as np

context = zmq.Context()
footage_socket = context.socket(zmq.SUB)
footage_socket.bind('tcp://*:5555')
footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))

while True:
    try:
        frame = footage_socket.recv_string()
        img = base64.b64decode(frame)
        npimg = np.fromstring(img, dtype=np.uint8)
        source = cv2.imdecode(npimg, 1)
        cv2.imshow("Stream", source)
        cv2.waitKey(1)

    except KeyboardInterrupt:
        cv2.destroyAllWindows()
        break

Streamer.py

import base64
import cv2
import zmq

context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://localhost:5555')

camera = cv2.VideoCapture(0)  # init the camera

while True:
    try:
        grabbed, frame = camera.read()  # grab the current frame
        frame = cv2.resize(frame, (640, 480))  # resize the frame
        encoded, buffer = cv2.imencode('.jpg', frame)
        jpg_as_text = base64.b64encode(buffer)
        footage_socket.send(jpg_as_text)

    except KeyboardInterrupt:
        camera.release()
        cv2.destroyAllWindows()
        break
5
Rohan Sawant

J'ai changé le code de @mguijarr pour travailler avec Python 3. Modifications apportées au code:

  • data est maintenant un littéral d'octet au lieu d'un littéral de chaîne
  • Changement de "H" en "L" pour envoyer des images de plus grandes tailles. Basé sur la documentation , nous pouvons maintenant envoyer des frames de taille 2 ^ 32 au lieu de seulement 2 ^ 16.

Server.py

import pickle
import socket
import struct

import cv2

Host = ''
PORT = 8089

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Socket created')

s.bind((Host, PORT))
print('Socket bind complete')
s.listen(10)
print('Socket now listening')

conn, addr = s.accept()

data = b'' ### CHANGED
payload_size = struct.calcsize("L") ### CHANGED

while True:

    # Retrieve message size
    while len(data) < payload_size:
        data += conn.recv(4096)

    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("L", packed_msg_size)[0] ### CHANGED

    # Retrieve all data based on message size
    while len(data) < msg_size:
        data += conn.recv(4096)

    frame_data = data[:msg_size]
    data = data[msg_size:]

    # Extract frame
    frame = pickle.loads(frame_data)

    # Display
    cv2.imshow('frame', frame)
    cv2.waitKey(1)

Client.py

import cv2
import numpy as np
import socket
import sys
import pickle
import struct

cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))

while True:
    ret,frame=cap.read()
    # Serialize frame
    data = pickle.dumps(frame)

    # Send message length first
    message_size = struct.pack("L", len(data)) ### CHANGED

    # Then data
    clientsocket.sendall(message_size + data)
2
nareddyt

Je l'ai fait fonctionner sur mon MacOS.

J'ai utilisé le code de @mguijarr et changé le struct.pack de "H" en "L".

Server.py:
==========
import socket
import sys
import cv2
import pickle
import numpy as np
import struct ## new

Host=''
PORT=8089

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print 'Socket created'

s.bind((Host,PORT))
print 'Socket bind complete'
s.listen(10)
print 'Socket now listening'

conn,addr=s.accept()

### new
data = ""
payload_size = struct.calcsize("L") 
while True:
    while len(data) < payload_size:
        data += conn.recv(4096)
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("L", packed_msg_size)[0]
    while len(data) < msg_size:
        data += conn.recv(4096)
    frame_data = data[:msg_size]
    data = data[msg_size:]
    ###

    frame=pickle.loads(frame_data)
    print frame
    cv2.imshow('frame',frame)

    key = cv2.waitKey(10)
    if (key == 27) or (key == 113):
        break

cv2.destroyAllWindows()



Client.py:
==========
import cv2
import numpy as np
import socket
import sys
import pickle
import struct ### new code
cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))
while True:
    ret,frame=cap.read()
    data = pickle.dumps(frame) ### new code
    clientsocket.sendall(struct.pack("L", len(data))+data) ### new code
1
Biranchi

Je suis un peu en retard mais mon puissant & fileté VidGear Traitement vidéo python fournit maintenant API NetGear , qui est exclusivement conçue pour transférer des images vidéo de manière synchrone entre des systèmes d'interconnexion sur le réseau en temps réel. Voici un exemple:

A. Fin du serveur: (exemple nu-minimum)

Ouvrez votre terminal préféré et exécutez le code python suivant:

Remarque: Vous pouvez arrêter le streaming à tout moment côté serveur et côté client en appuyant sur [Ctrl + c] sur votre clavier côté serveur !

# import libraries
from vidgear.gears import VideoGear
from vidgear.gears import NetGear

stream = VideoGear(source='test.mp4').start() #Open any video stream
server = NetGear() #Define netgear server with default settings

# infinite loop until [Ctrl+C] is pressed
while True:
    try: 
        frame = stream.read()
        # read frames

        # check if frame is None
        if frame is None:
            #if True break the infinite loop
            break

        # do something with frame here

        # send frame to server
        server.send(frame)

    except KeyboardInterrupt:
        #break the infinite loop
        break

# safely close video stream
stream.stop()
# safely close server
writer.close()

B. Client End: (exemple nu-minimum)

Ouvrez ensuite un autre terminal sur le même système et exécutez le code suivant python et voyez la sortie:

# import libraries
from vidgear.gears import NetGear
import cv2

#define netgear client with `receive_mode = True` and default settings
client = NetGear(receive_mode = True)

# infinite loop
while True:
    # receive frames from network
    frame = client.recv()

    # check if frame is None
    if frame is None:
        #if True break the infinite loop
        break

    # do something with frame here

    # Show output window
    cv2.imshow("Output Frame", frame)

    key = cv2.waitKey(1) & 0xFF
    # check for 'q' key-press
    if key == ord("q"):
        #if 'q' key-pressed break out
        break

# close output window
cv2.destroyAllWindows()
# safely close client
client.close()

NetGear prend actuellement en charge deux modèles de messagerie ZeroMQ: c'est-à-dire zmq.PAIR et zmq.REQ and zmq.REP et le protocole pris en charge sont: 'tcp', 'upd', 'pgm', 'inproc', 'ipc'

Une utilisation plus avancée peut être trouvée ici: https://github.com/abhiTronix/vidgear/wiki/NetGear

0
abhiTronix