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)
.
Quelques choses:
sendall
au lieu de send
car vous n'êtes pas sûr que tout sera envoyé en une seule foispickle
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)recv
vous obtiendrez de meilleures performances si vous recevez de gros morceaux, alors remplacez 80 par 4096 ou même plussys.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êmesUn 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.
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
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îneServer.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)
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
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:
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()
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