I am doing a project that needs to live stream the video at the client side using USB webcam at the server side.
I am using (Opencv 3.2.0 + python 3.4.3).
The below given code works fine, I want to know how this code actually works and how it is transferring the frames to the client.
There is a delay in this code, and how to overcome that delay. I tried to set resolution but it gives error at the client side like ([Error]: total size of new array must be unchanged).when i set it to default resolution (i.e.,640*480) it works fine.
Help me
1. to set the resolution. -answered
2. why dtype uint8 is used? -answered
3. what is role fileDescriptor? -answered
updated questions:
Lets see who will answer all these questions !!!
server:
import cv2
import time
import json
import socket
import base64
import numpy as np
from threading import Thread
SERVER_IP = "x.x.x.x"
SERVER_PORT = xxxx
MAX_NUM_CONNECTIONS = 20
DEVICE_NUMBER = 0
class ConnectionPool(Thread):
def __init__(self, ip_, port_, conn_, device_):
Thread.__init__(self)
self.ip = ip_
self.port = port_
self.conn = conn_
self.device = device_
print("[+] New server socket thread started for " + self.ip + ":" +str(self.port))
def run(self):
try:
while True:
ret, frame = self.device.read()
a = b'\r\n'
data = frame.tostring()
da = base64.b64encode(data)
self.conn.sendall(da + a)
except Exception as e:
print("Connection lost with " + self.ip + ":" + str(self.port) +"\r\n[Error] " + str(e.message))
self.conn.close()
if __name__ == '__main__':
cap = cv2.VideoCapture(DEVICE_NUMBER)
print("Waiting connections...")
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
connection.bind((SERVER_IP, SERVER_PORT))
connection.listen(MAX_NUM_CONNECTIONS)
while True:
(conn, (ip, port)) = connection.accept()
thread = ConnectionPool(ip, port, conn, cap)
thread.start()
connection.close()
cap.release()
client:
import cv2
import socket
import base64
import numpy as np
IP_SERVER = "x.x.x.x"
PORT_SERVER = xxxx
TIMEOUT_SOCKET = 10
SIZE_PACKAGE = 4096
IMAGE_HEIGHT = 480
IMAGE_WIDTH = 640
COLOR_PIXEL = 3 # RGB
if __name__ == '__main__':
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
connection.settimeout(TIMEOUT_SOCKET)
connection.connect((IP_SERVER, PORT_SERVER))
while True:
try:
fileDescriptor = connection.makefile(mode='rb')
result = fileDescriptor.readline()
fileDescriptor.close()
result = base64.b64decode(result)
frame = np.fromstring(result, dtype=np.uint8)
frame_matrix = np.array(frame)
frame_matrix = np.reshape(frame_matrix, (IMAGE_HEIGHT, IMAGE_WIDTH,COLOR_PIXEL))
cv2.imshow('Window title', frame_matrix)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except Exception as e:
print("[Error] " + str(e))
connection.close()
- to set the resolution.
You have to change it in both places, server side and client side. Most probably the error you have comes from the reshape
function. This function will take the data from the image and reshape it, this means that the data will be untouched and only the size will changed... In this case it is used to change a 1D data array to a 2D matrix 3 channel matrix (however the data order is unchanged). How to change it, you have to change it first in the server part.
Where it says
ret, frame = self.device.read()
After it you can add the resize function of OpenCV:
frame = cv2.resize(frame ,(width/2, height/2), interpolation = cv2.INTER_LINEAR )
This will be half the size. Now in the client side you can reduce to:
IMAGE_HEIGHT = 480 / 2
IMAGE_WIDTH = 640 / 2
- why dtype uint8 is used?
This comes from the image representation used by default by OpenCV (BGR), this means that each pixel color it is represented by 3 channels, Blue Green and Red. Each of this channels goes from 0-255 in value, which means that it can be represented with 8 bits, and that is why dtype uint8. Later on the function reshape will create pixels from 3 continuous values (COLOR_PIXEL value).
In this part the code reads a line of text, which is the image data and puts it in an 1D array. This will tell that each 8bit of that line is a value and should be seen as an integer.
- what is role fileDescriptor?
This is one way to read data from a socket. See the documentation. This way you can use readline, and get a chunk of data limited by a character of new line. In you case '\r\n'
. See the lines:
a = b'\r\n'
data = frame.tostring()
da = base64.b64encode(data)
self.conn.sendall(da + a)
a
is the return character, which is sent after the whole image data represented as a string and encoded with base 64.