近期测试方面有需求,利用一台主机进行图像数据获取,然后传输至另一台主机上,我的方法是利用socket实现,开发语言是python,能够在客户端实现图像的获取,然后发送至服务器,服务器端可以选择保存图片至指定文件夹,也可以不保存,实时查看。
一、视频转化为图片的传输:
客户端打开笔记本摄像头,将图片格式进行转换后,发送至服务器,服务器格式转换后显示保存。
服务器程序:
# -*- coding: utf-8 -*-
#功能:
# 1.一台电脑/两台电脑间,客户端自动截屏后传输图像至服务器
# 2.一台电脑/两台电脑间,客户端自动打开摄像头传输图像至服务器
#服务器程序
import socketserver, struct, gzip, time
import PIL.ImageShow
from PIL import ImageFile, Image
from io import BytesIO
import matplotlib.pyplot as plt
import cv2
import numpy
def Save(imgBytes):
imgIO = BytesIO(imgBytes)
img = Image.open(imgIO)
img = img.convert('YCbCr') # 转换成YCbCr格式
# img.save('.\screenshot\{:.2f}.jpeg'.format(time.time()))#保存图片,注意图片会一直存储,要及时清理
class DATA(socketserver.BaseRequestHandler):
def handle(self):
ImageFile.LOAD_TRUNCATED_IMAGES = True
s = self.request
while True:
t1 = time.time()
headSize = struct.calcsize('l') # 计算文件头长度
head = s.recv(headSize) # 接收文件头
if head:
imgSize = struct.unpack('l', head)[0] # 获取图像长度,类型:int
recvSize = 0 # 接收到的数据长度
imgBytesZ = b'' # 接收到的数据
print("imgSize,recvSize,imgBytesZ",imgSize,recvSize,imgBytesZ)
while True:
if imgSize - recvSize > 0: # 不断接收数据直至没有数据
imgBuf = s.recv(SERVER().bufSize)
recvSize += len(imgBuf)
else:
break
imgBytesZ += imgBuf
imgBytes = gzip.decompress(imgBytesZ) # 解压数据
t2 = time.time()
T = str(str((t2 - t1) * 1000) + "ms")
print("接受图片所需时间",T)
Save(imgBytes)#保存图像
#图像显示
bytes_stream = BytesIO(imgBytes)#图像由Byte格式转换回PCI.Image格式
image = Image.open(bytes_stream)
PIL.ImageShow.show(image)
# img = cv2.cvtColor(numpy.asarray(image), cv2.COLOR_RGB2BGR)#显示图片,每次都会重新打开一个界面显示传输的图片
class SERVER():
def __init__(self):
self.bufSize = 8388608 # 每一次接受的字节流长度
def Server(self):
server = socketserver.ThreadingTCPServer(('192.168.1.12', 8000), DATA) # 自定义端口,不同主机的话,服务器写自己的主机端口,客户机写服务器主机端口
#server = socketserver.ThreadingTCPServer(('127.0.0.1', 8000), DATA) #同一主机
server.serve_forever()
if __name__ == "__main__":
SERVER().Server()
客户端程序:
# -*- coding: utf-8 -*-
#功能:
# 1.一台电脑/两台电脑间,客户端自动截屏后传输图像至服务器
# 2.一台电脑/两台电脑间,客户端自动打开摄像头传输图像至服务器
#客户端程序
import socket, struct, pyscreenshot, gzip
from PIL import Image
from io import BytesIO
import cv2
import time
import numpy
class CLIENT():
def __init__(self):
self.bufSize = 8388608 # 每一次传输的字节流长度
#自动截屏功能,需要可以开启,使用的话注意注释掉下面的调用摄像头代码
'''
# def Data(self, s):
# while True:
# img = pyscreenshot.grab() # 截取屏幕图像,类型:PIL.PngImagePlugin.PngImageFile
#
# imgW = img.size[0] # 图像宽度
# imgH = img.size[1] # 图像长度
# if imgW > 1920: # 图像宽度不超过1920
# zoom = 1920 / imgW
# else:
# zoom = 1
# imgResize = img.resize((int(imgW * zoom), int(imgH * zoom)), Image.Resampling.LANCZOS) # 调整后的图像
# print(type(imgResize))
# imgIO = BytesIO() # 创建文件对象,类型:io.BytesIO
# imgResize.save(imgIO, 'JPEG') # 以JPEG格式存储,减少数据大小
# print(type(imgIO))
# imgIOZ = BytesIO() # 创建文件对象,类型:io.BytesIO
# imgIOZ.write(gzip.compress(imgIO.getvalue())) # 压缩原图并存入文件对象
# print(type(imgIOZ))
# imgBytes = imgIOZ.getvalue() # 图像的字节流,类型:bytes
# print(len(imgBytes)) # 显示字节流长度
#
# imgSize = len(imgBytes) # 图像大小(字节流长度),类型:int
# head = struct.pack('l', imgSize) # 构造文件头信息,内容是图像大小(字节流长度),类型:bytes
# s.send(head) # 发送文件头
# imgIOZ.seek(0, 0) # 从开头读取图片
# while True:
# imgBuf = imgIOZ.read(self.bufSize) # self.bufSize大小的图片,类型:bytes
# if not imgBuf: # 传输完成退出循环
# break
# s.send(imgBuf) # 发送self.bufSize大小的图片
'''
#调用笔记本摄像头程序,使用时注意注释掉上方截屏部分的代码
def Data(self, s):
# screen = cv2.VideoCapture(0)
while True:
# img即为
t1 = time.time()
sucess, img = screen.read()
# 转为灰度图片,转不转都可以
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = numpy.array(img_encode)
# str_encode = data_encode.tostring()
str_encode = data_encode.tobytes()
encode_len = str(len(str_encode))
print(encode_len) # 输出看一下encode码的大小,可有可无
# 显示摄像头
cv2.imshow("321img", gray)
# 保持画面的持续。
k = cv2.waitKey(3) #这里如果为0的话,就是将你目前所在的画面定定格,为其他数字比如1的时候,表示1秒后程序结束。但是由于是死循环,所以结束后马上开启,就为连续图像,
if k == 27:
# 通过esc键退出摄像
cv2.destroyAllWindows()
break
elif k == ord("s"): # 关闭摄像头
# 通过s键保存图片,并退出。
cv2.imwrite("image2.jpg", img)
cv2.destroyAllWindows()
break
imgResize = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))#将opencv格式图像,转化为PIC.Image格式
print(type(imgResize))
imgIO = BytesIO() # 创建文件对象,类型:io.BytesIO
imgResize.save(imgIO, 'JPEG') # 以JPEG格式存储,减少数据大小
print(type(imgIO))
imgIOZ = BytesIO() # 创建文件对象,类型:io.BytesIO
imgIOZ.write(gzip.compress(imgIO.getvalue())) # 压缩原图并存入文件对象
print(type(imgIOZ))
imgBytes = imgIOZ.getvalue() # 图像的字节流,类型:bytes
print(len(imgBytes)) # 显示字节流长度
imgSize = len(imgBytes) # 图像大小(字节流长度),类型:int
head = struct.pack('l', imgSize) # 构造文件头信息,内容是图像大小(字节流长度),类型:bytes
s.send(head) # 发送文件头
imgIOZ.seek(0, 0) # 从开头读取图片
while True:
imgBuf = imgIOZ.read(self.bufSize) # self.bufSize大小的图片,类型:bytes
if not imgBuf: # 传输完成退出循环
break
s.send(imgBuf) # 发送self.bufSize大小的图片
print("send successed")
t2 = time.time()
T = str(str((t2 - t1)*1000) + "ms")
print("发送图片所需时间:", T)
def Client(self):
while True: # 死循环加上try语句可以不用考虑先运行服务器端
try: # 连接不上重试
s = socket.socket()
s.connect(('192.168.1.12', 8000)) # 自定义服务器IP和端口
#s.connect(('127.0.0.1', 8000))# 同一台主机下服务器IP和端口
except Exception as e:
print('Connection error')
print(e)
s.close()
continue
try: # 传输数据发生错误重试
self.Data(s) # 处理数据并传输
except Exception as e:
print('Data error')
print(e)
finally:
s.close()
if __name__ == '__main__':
screen = cv2.VideoCapture(0)
CLIENT().Client()
二、图片、文件的传输,都是一样利用的socket,代码如下:
客户端代码:
# 服务器端server.py
# 能实现主机自身或不同主机间传图片或者文件,不过目前只能是客户端发,服务器收,
# 互相发送的功能还未实现,如果是单纯不想利用U盘传图片,笨方法是互相调换身份,
# 那就用客户端运行服务器程序,注意更换ip
import socket
import os
import sys
import struct
def socket_service_image():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 8000))
s.listen(10)
except socket.error as msg:
print(msg)
sys.exit(1)
print("Wait for Connection.....................")
while True:
sock, addr = s.accept() # addr是一个元组(ip,port)
deal_image(sock, addr)
def deal_image(sock, addr):
print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口
while True:
fileinfo_size = struct.calcsize('128sq')
buf = sock.recv(fileinfo_size) # 接收图片名
print(buf)
if buf:
filename, filesize = struct.unpack('128sq', buf)
fn = filename.decode().strip('\x00')
print(type(fn))
new_filename = os.path.join(r'E:\\reseive_images\\' + fn)
recvd_size = 0
fp = open(new_filename, 'wb')
while not recvd_size == filesize:
if filesize - recvd_size > 1024:
data = sock.recv(1024)
recvd_size += len(data)
else:
data = sock.recv(1024)
recvd_size = filesize
fp.write(data) # 写入图片数据
fp.close()
sock.close()
break
if __name__ == '__main__':
socket_service_image()
客户端程序:
# 客户端client.py
# 图片或者文件都可以
import socket
import os
import sys
import struct
def sock_client_image():
while True:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# s.connect(('改成服务器ip地址', 8001)) # 服务器和客户端在不同的系统或不同的主机下时使用的ip和端口,首先要查看服务器所在的系统网卡的ip
# s.connect(('127.0.0.1', 8000)) #服务器和客户端都在一个系统下时使用的ip和端口
s.connect(('192.168.1.13', 8000)) # 服务器和客户端是两个主机时,服务器的ip和端口
except socket.error as msg:
print(msg)
print(sys.exit(1))
# 输入当前目录下的图片名 xxx.jpg
filepath = r'F:\pythonProject\Pyserial-Demo-master\Pyserial-Demo-master\test1.jpg'
fhead = struct.pack(b'128sq', bytes(os.path.basename(filepath), encoding='utf-8'),os.stat(filepath).st_size) # 将xxx.jpg以128sq的格式打包
s.send(fhead)
fp = open(filepath, 'rb') # 打开要传输的图片
while True:
data = fp.read(1024) # 读入图片数据
if not data:
print('{0} send over...'.format(filepath))
break
s.send(data) # 以二进制格式发送图片数据
s.close()
break #循环发送
if __name__ == '__main__':
sock_client_image()
三、大文件的传输,其实原理和上面是一样的,结构也差不多,代码如下:
服务器端:
#快速读取快速存储,大文件也可以
import time
import cv2
import os
import json
import socket
import threading
#读取文件的最大数
max_len=8388608 #也可以提前写一个大数字,这样传输的会快一些,或者用于,如4294967296
#端口号和IP地址
remote_PORT=5555
remote_IP='127.0.0.1'
remote_addr=(remote_IP,remote_PORT)
#绑定IP地址和端口号PORT
socket_Server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_Server.bind(remote_addr)
#监听
socket_Server.listen()
print('正在监听来自客户端的消息......')
def Server_Recv_File(socket):
"""
:param socket: 服务端套接字
:param root: 主窗口
:return:
"""
t1 = time.time()
#获取客户端发送的消息头
msg_header=socket.recv(max_len)
header=json.loads(msg_header.decode('utf-8'))
#输出客户端发送的消息头信息
print(header)
#保存接收文件的地方
# curr_path=os.getcwd()
curr_path = os.path.join(r'E:\reseive_images_1')
# time = datetime.now()
# time = datetime.strftime(datetime.now(), '%Y%m%d-%H%M%S')
filename = curr_path + '\\recv_' + header['filename'] + header['msg_type']
get_file_Size=header['msg_len']
file_size=0
#输出文件名和文件大小
print('文件名: {}'.format(filename))
print(type(filename))
print('file_size: {}'.format(get_file_Size))
recv_count = 0
#如果文件不存在则创建
if os.path.exists(filename)==False:
with open(filename,'wb') as fp:
while file_size!=get_file_Size:
message=socket.recv(max_len)
fp.write(message)
file_size+=len(message)
print(file_size)
recv_count+=1
else:
with open(filename, 'wb') as fp:
while file_size != get_file_Size:
message = socket.recv(max_len)
fp.write(message)
file_size += len(message)
print(file_size)
recv_count += 1
print('接收次数: {}'.format(recv_count))
socket.close()
print('接收完成...')
t2 = time.time()
t = t2-t1
print(t)
if __name__=='__main__':
print('Pycharm')
new_socket,addr=socket_Server.accept()
Server_Recv_File(new_socket)
客户端:
#快速读取快速存储,大文件也可以
import os
import json
import socket
import threading
import time
#读取文件的最大数,可以修改,提前写一个大数字,这样传输的会快一些,或者用于,如4294967296
max_len=8388608
#端口号和IP地址
remote_PORT=5555
remote_IP='127.0.0.1'
remote_addr=(remote_IP,remote_PORT)
#绑定端口号和IP地址
socket_Client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_Client.connect(remote_addr)
def Client_Send_File(socket_Client,filename):
"""
:param socket: 客户端套接字
:param filename: 要传输的文件
:param root:主窗口
:return:
"""
t1 = time.time()
#首先将消息头发送至服务端
file,class_file=os.path.splitext(filename)
#获取文件大小
# file_size=os.path.getsize(filename)
file_Size=os.stat(filename).st_size
msg_header={'filename':file,'msg_type':class_file,'msg_len':file_Size}
msg_header_bytes=bytes(json.dumps(msg_header),encoding='utf-8')
#当消息头的长度不满1024时,使用空格填充
msg_header_bytes+=b''*(max_len-len(msg_header_bytes))
socket_Client.send(msg_header_bytes)
file_len=0
recv_count=0
#发送的文件头大小
print('msg_header_bytes: {}'.format(len(msg_header_bytes)))
#发送的文件大小
print('file_size: {}'.format(file_Size))
with open(filename,'rb') as fp:
while file_len!=file_Size:
message=fp.read(max_len)
socket_Client.send(message)
file_len+=len(message)
print(file_len)
recv_count+=1
print('发送次数: {}'.format(recv_count))
# socket_Client.close()
print('发送完成...')
t2 =time.time()
t=t2-t1
print(t)
if __name__=='__main__':
print('Pycharm')
#第二个参数为图片或者其他文件的路径
Client_Send_File(socket_Client,'yolov3.weights')