I'm trying to take a screenshot and send it to another computer using python.
I've tried to do it in many different ways. Unfortunately, I didn't find a way to do it.
I would appreciate your help!
server:
from PIL import Image
from PIL import ImageGrab
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 1111))
server_socket.listen(1)
print('Waiting For Connection...')
(client_socket, client_address) = server_socket.accept()
print('Connected to: ', client_address[0])
img = ImageGrab.grab(bbox=(10, 10, 500, 500))
photo_to_send= img.tobytes()
size = len(photo_to_send)
client_socket.send(bytes(str(size), 'utf-8'))
while size >= 0:
msg = photo_to_send[:4096]
client_socket.send(bytes(str(msg), 'utf-8'))
photo_to_send= photo_to_send[4096:]
client:
import socket
from PIL import Image
my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
my_socket.connect(('127.0.0.1', 1111))
print("Connected to the server")
size = int(my_socket.recv(1024).decode('utf-8'))
the_photo = ""
the_photo = the_photo.encode('utf-8')
while size > 0:
data = my_socket.recv(4096)
size -= len(data)
the_photo += data
img_to_save = Image.frombytes("RGB", (490,490), the_photo)
img_to_save .save("screenshot.png")
You can use pickle to serialize your Image objects. Pickle just converts an python object into bytes. pickle.dumps just encodes it to bytes and pickle.loads decodes it back.
Hope this helps!
The problem is that you are sending the textual value of the length immediately before the data itself. For example, if the image data started with AAABBB, you would be sending
1234AAABBB...
But if the image was bigger, you might send something like
56789AAABBB...
The client has no way of telling where the length ends and the data starts! To fix this, you need to send a fixed size length parameter. If you still want to a textual length, you could use zfill:
client_socket.send(str(size).zfill(16).encode())
...
size = int(my_socket.recv(16).decode())
(where 16 is chosen to be long enough to fit any possible image size)
This is not terribly efficient; real protocols usually use binary encoding:
client_socket.send(struct.pack('>I', size))
...
size, = struct.unpack('>I', my_socket.recv(4))
But this may be more complex than you need for your application.
The problem was that I encoded the photo twice.
Thanks for your help!
I really appreciate it!
Here is the solution:
server:
from PIL import ImageGrab
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 1112))
server_socket.listen(1)
print('Waiting For Connection...')
(client_socket, client_address) = server_socket.accept()
print('Connected to: ', client_address[0])
img = ImageGrab.grab(bbox=(10, 10, 500, 500))
photo_to_send = img.tobytes()
size = len(photo_to_send)
print(size)
print(photo_to_send)
client_socket.send(bytes(str(size), 'utf-8'))
client_socket.send(photo_to_send)
client:
import socket
from PIL import Image
my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
my_socket.connect(('127.0.0.1', 1112))
print("Connected to the server")
size = int(my_socket.recv(10).decode('utf-8'))
print(size)
the_photo = my_socket.recv(size)
print(the_photo)
img_to_save = Image.frombytes("RGB", (490, 490), the_photo)
img_to_save.save("screenshot.png")
struct and pickle makes everything easy for you.
server.py
imageBytes = pickle.dumps(image)
sock.sendall(struct.pack("L", len(imageBytes)) + imageBytes)
client.py
data = b""
payload_size = struct.calcsize("L")
while True:
data += sock.recv(payload_size)
packedImageSize = data[:payload_size]
imageSize = struct.unpack("L", packedImageSize)[0]
data = data[payload_size:]
while len(data) < imageSize:
data += sock.recv(65000)
frameData = data[:imageSize]
data = data[imageSize:]
frame = pickle.loads(frameData)
Related
I got this code for streaming a video from a client to a server:
Client:
import cv2, imutils
import mss
import numpy
from win32api import GetSystemMetrics
import pickle
import socket, struct
client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host_ip = "IPADRESS"
port = 9999
client_socket.connect((host_ip,port))
with mss.mss() as sct:
monitor = {"top": 0, "left": 0, "width": GetSystemMetrics(0), "height": GetSystemMetrics(1)}
while True:
img = numpy.array(sct.grab(monitor))
frame = imutils.resize(img, width=1400)
a = pickle.dumps(frame)
message = struct.pack("Q",len(a))+a
client_socket.send(message)
Server:
import cv2, imutils
import numpy as np
import pickle, struct
import socket
import threading
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host_ip = "IP_ADRESS"
port = 9999
socket_address = (host_ip,port)
server_socket.bind(socket_address)
server_socket.listen()
print("Listening at",socket_address)
def show_client(addr,client_socket):
try:
print('CLIENT {} CONNECTED!'.format(addr))
if client_socket: # if a client socket exists
data = b""
payload_size = struct.calcsize("Q")
while True:
while len(data) < payload_size:
packet = client_socket.recv(4*1024)
if not packet:
break
data+=packet
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack("Q",packed_msg_size)[0]
while len(data) < msg_size:
data += client_socket.recv(4*1024)
frame_data = data[:msg_size]
data = data[msg_size:]
frame = pickle.loads(frame_data)
cv2.imshow("Screen", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
client_socket.close()
except Exception as e:
print(e)
print(f"CLINET {addr} DISCONNECTED")
pass
while True:
client_socket,addr = server_socket.accept()
thread = threading.Thread(target=show_client, args=(addr,client_socket))
thread.start()
print("TOTAL CLIENTS ",threading.activeCount() - 1)
A lot of this code is from a youtuber called "pyshine", and everything is working just fine, but I don't understand, what a specific part of this code is really doing.
These are the parts:
First of all in the client-code:
message = struct.pack("Q",len(a))+a
I know that it does something with the length of the pickle and, that it appends the pickle to it, but not more.
Second of all in the server-code:
data = b""
payload_size = struct.calcsize("Q")
while True:
while len(data) < payload_size:
packet = client_socket.recv(4*1024)
if not packet:
break
data+=packet
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack("Q",packed_msg_size)[0]
while len(data) < msg_size:
data += client_socket.recv(4*1024)
frame_data = data[:msg_size]
With printing out some values, I definitely understood it a bit better, but the whole process, how it gets the final "frame_data", is still a mystery to me. So I would really appreciate, if someone could explain me the process that is going there.
socket is primitive object and it doesn't care what data you send. You can send two frames and client can get it as single package or it may get it as many small packages - socket doesn't care where is end of first frame. To resolve this problem this code first sends len(data) and next data. It uses struct("Q") so this value len(data) has always 8 bytes. This way receiver knows how much data it has to receive to have complete frame - first it gets 8 bytes to get len(data) and later it use this value to get all data. And this is what second code does - it repeats recv() until it gets all data. It also checks if it doesn't get data from next frame - and keep this part as data[payload_size:] to use it with next frame/
If you will use the same rule(s) on both sides - sender first sends 8 bytes with sizeand next data, receiver first gets 8 bytes with size and next get data (using size) - then you have defined protocol. (similar to other protocols: HTTP (HyperText Transfer Protocol), FTP (File Transfer Protocol), SMTP (Send Mail Transfer Protocol), etc.)
I'm trying to send python-opencv frames over sockets. I'm pickling the data and unpickling but for some reason it's blank or nothing is showing up.
This is my terminal when I run client.py
new message length: b'720 '
It should be streaming the webcam from server but nothing is showing up.
Here is my code for the client and server:
client.py
import socket
import numpy as np
import cv2
import pickle
HEADERSIZE = 10
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 1232))
while True:
full_msg = b''
new_msg = True
while True:
msg = s.recv(16)
if new_msg:
print(f'new message length: {msg[:HEADERSIZE]}')
msglen = int(msg[:HEADERSIZE])
new_msg = False
full_msg += msg
if len(full_msg)-HEADERSIZE == msglen:
print('full msg recvd')
print(full_msg[HEADERSIZE:])
d = pickle.loads(full_msg[HEADERSIZE:])
print(d)
cv2.namedWindow('Webcam', cv2.WINDOW_NORMAL)
cv2.imshow('Webcam', full_msg[HEADERSIZE:])
new_msg = True
full_msg = b''
print(full_msg)
server.py
import socket
import numpy as np
import cv2
import time
import pickle
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)
HEADERSIZE = 10
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), 1232))
s.listen(5)
cap = cv2.VideoCapture(0)
while True:
clientsocket, address = s.accept()
print(f"Connection from {address} has been established!")
while True:
ret, frame = cap.read()
msg = pickle.dumps(frame)
print(frame)
msg = bytes(f'{len(frame):<{HEADERSIZE}}', "utf-8") + msg
clientsocket.send(msg)
I have no idea why nothing is showing up. I don't even know if anything is coming though. Does it have to do with numpy data? I heard that can be tricky.
When you stream frames of 720 bytes from server, you are actually sending 730 bytes (length 10 bytes + data 720 bytes) per frame continuously, one frame after another.
In client you are reading 16 bytes per recv(). Hence your condition if len(full_msg)-HEADERSIZE == msglen: will never be true, with header size 10, as 730 is not divisible by 16.
So your program is looping indefinitely on while True: in client.
Try below program for client. I tested with dummy data.
client.py
import socket
import numpy as np
import cv2
import pickle
HEADERSIZE = 10
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 1232))
while True:
msg_length=int(s.recv(HEADERSIZE))
full_msg=b''
while len(full_msg)<msg_length:
full_msg+=s.recv(msg_length-len(full_msg))
print(full_msg)
d = pickle.loads(full_msg)
cv2.namedWindow('Webcam', cv2.WINDOW_NORMAL)
cv2.imshow('Webcam', full_msg)
I want to send an image from one server to another. I don't want to save the file on disk. I directly want to read the send data. I've written a script for this.
server.py
import socket
import cv2
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 6002))
s.listen(10)
c, addr = s.accept()
print('{} connected.'.format(addr))
img = cv2.imread("test.jpg")
img = cv2.imencode('.jpg', img)[1].tostring()
c.send( str(len(img)).ljust(16));
c.send(img)
i = 0
datas = img[i:i+1024]
i = i + 1024
while datas:
datas = img[i:i+1024]
c.send(datas)
i = i + 1024
print("Done sending...")
client.py
import socket
import numpy as np
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("", 6002))
img = ""
while True:
datas = s.recv(1024)
while datas:
img = img + str(datas)[2:-1]
datas = s.recv(1024)
break
print("Done receiving")
img_np = np.fromstring(img, np.uint8)
img_np = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
I'm receiving image in img but when decoding it using opencv, I'm getting empty matrix img_np.
I suggest you to use Memory-Mapped File instead of ZeroMQ as it yields less latency.
Here is an example I wrote that you can use: https://github.com/off99555/python-mmap-ipc
I have a numpy ndarray that I'm trying to send via socket connection. When I try to load it on the server, using pickle.loads I get EOFError: ran out of input.
client.py
import numpy as np
import socket, pickle
import struct
HOST = "192.168.143.xxx"
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
centers = np.zeros((100, 43919))
packet = pickle.dumps(centers)
length = struct.pack('>I', len(packet))
packet = length + packet
s.send(packet)
data = s.recv(8192)
data_new = pickle.loads(data)
s.close()
print ('Received', data_new)
server.py
import socket, pickle, numpy as np
import struct
HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(2)
while 1:
L = np.zeros((100, 43919))
#wait to accept a connection - blocking call
conn, addr = s.accept()
print ('Connected with ', addr)
buf = b''
while len(buf) < 4:
buf += conn.recv(4 - len(buf))
length = struct.unpack('>I', buf)[0]
print(length)
data = conn.recv(length)
if not data: break
M = pickle.loads(data) # HERE IS AN ERROR, the same as np.loads(...)
L += M
data_out = pickle.dumps(L)
conn.sendall(data_out)
conn.close()
s.close()
I've tried reading this, this and this but nothing helps.
I'm using Python 3.4.
EDIT:
The exact error is:
File server.py, line 30, in <module>:
M = pickle.loads(data) #HERE IS AN ERROR
EOFError: Ran out of input.
conn.recv(length) does not necessarily read length bytes, if there are less than that number available. You need to loop until you have enough.
See When does socket.recv(recv_size) return?
data = b''
l = length
while l > 0:
d = sock.recv(l)
l -= len(d)
data += d
There is a similar issue in your code that uses .send(), although in that case there is an easy fix: just use .sendall() as you do in the other place.
This is my code:
server.py:
#The server receives the data
import socket
from PIL import Image
import pygame,sys
import pygame.camera
from pygame.locals import *
import time
host = "localhost"
port = 1890
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(1)
conn, addr = s.accept()
print "connected by",addr
screen = pygame.display.set_mode((640,480))
while 1:
data = conn.recv(921637)
image = pygame.image.fromstring(data,(640,480),"RGB")
screen.blit(image,(0,0))
pygame.display.update()
if not image:
break;
conn.send(data)
conn.close()
client.py
#The client sends the data
import socket
from PIL import Image
import pygame,sys
import pygame.camera
from pygame.locals import *
import time
host = "localhost"
port = 1890
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0",(640,480))
cam.start()
while 1:
image = cam.get_image()
data = pygame.image.tostring(image,"RGB")
s.sendall(data)
s.close()
print "recieved", repr(data)
Just to test, I tried the following code and it's working fine, but the above does not...
Working code when implemeted without sockets: camcapture.py
import sys
import time
import pygame
import pygame.camera
from pygame.locals import *
pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0",(640,480))
cam.start()
screen = pygame.display.set_mode((640,480))
while 1:
image = cam.get_image()
data = pygame.image.tostring(image,"RGB")
img = pygame.image.fromstring(data,(640,480),"RGB")
screen.blit(img,(0,0))
pygame.display.update()
The error is:
image = pygame.image.fromstring(data,(640,480),"RGB")
ValueError: String length does not equal format and resolution size
Where did I go wrong?
The problem is not the camera.
The problem is that you send a very large string over the socket and you incorrectly assume that you can read the entire string at once with conn.recv(921637).
You'll have to call recv multiple times to receive all over your data. Try printing the length of data you send in client.py and print the length of data in server.py before calling pygame.image.fromstring and you'll see.
There are several ways to solve this:
make a new connection for each image you send
send the size of your data so the server knows how much data to read
use some kind of end marker
Here's a simple example:
sender:
import socket
import pygame
import time
host = "localhost"
port = 1890
pygame.init()
image = pygame.surface.Surface((640, 480))
i=0
j=0
while 1:
image.fill((i,j,0))
i+=10
j+=5
if i >= 255: i = 0
if j >= 255: j = 0
data = pygame.image.tostring(image,"RGB")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.sendall(data)
s.close()
time.sleep(0.5)
receiver:
import socket
import pygame
host="localhost"
port=1890
screen = pygame.display.set_mode((640,480))
while 1:
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(1)
conn, addr = s.accept()
message = []
while True:
d = conn.recv(1024*1024)
if not d: break
else: message.append(d)
data = ''.join(message)
image = pygame.image.fromstring(data,(640,480),"RGB")
screen.blit(image,(0,0))
pygame.display.update()