设计思路
运行思路如下
由于在做人脸视频检测的项目的时候需要将检测的人脸区域全部保存下来,并发送到服务器端(用虚拟机来代替),来进行人脸对比,整个代码设计思路如下。
客户端:
- save_file_dir作用是读取文件路径(返回每一个图片的具体路径)
- file_deal作用是将图片路径进行读取,返回字节数
- send就是发送图片带服务器的过程,首先肯定要先和服务器进行connect,然后再确认一下服务器get到文件长度和文件名数据,就可以进行发送图片了。
服务器端:
- 只需要定义一个函数,用于和客户端进行端口链接,通过sock.accept() 得到客户端发送的数据和ip和port,然后通过decode().split(’|’)获取到客户端发送的东西(包括字节长度和文件路径),最后通过创建一个图片,把图片信息写入进行就可以了。
代码实现
#===============================================================
# 客户端
import socket,os
def save_file_dir(file_dir):
import glob, os
filenames = glob.glob(os.path.join(file_dir, '*.jpg'))
nums = len(filenames)
print('共有{}张图片'.format(nums))
return filenames
def read_pic(path1):
if os.path.exists(path1):
print('path exist.......')
dirnames = sorted(os.listdir(path1))
fullnames=[]
for dirname in dirnames:
dirfullname=os.path.join(path1,dirname)
fullnames.append(dirfullname)
print('the file is {}'.format(len(dirnames)))
return dirnames,fullnames
def send(IMAGE_PATH):
ip_addr = ("10.16.97.201", 8888)
filepaths,fullnames=read_pic(IMAGE_PATH)
for photo in fullnames:
print('sending {}'.format(photo))
data = file_deal(photo)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(ip_addr)
sock.send('{}|{}'.format(len(data), photo).encode()) # 默认编码 utf-8,发送文件长度和文件名
reply = sock.recv(1024)
if 'ok' == reply.decode(): # 确认一下服务器get到文件长度和文件名数据
go = 0
total = len(data)
while go < total: # 发送文件
data_to_send = data[go:go + 1024]
sock.send(data_to_send)
go += len(data_to_send)
reply = sock.recv(1024)
if 'copy' == reply.decode():
print('{} send successfully'.format(photo))
sock.close() # 由于tcp是以流的形式传输数据,我们无法判断开头和结尾,简单的方法是没传送一个文件,就使用一个socket,但是这样是消耗计算机的资源,博主正在探索更好的方法,有机会交流一下
def file_deal(file_path): # 读取文件的方法
# open image
myfile = open(file_path, 'rb')
bytes = myfile.read()
return bytes
IMAGE_PATH = '/home/z/Documents/face_detect_yolov4_yolov4tiny_ssd-master/yolov4_cam_result/'
send(IMAGE_PATH)
#===============================================================
# 服务端
# 先通过服务器端运行,在通过客户端运行(传送图片来自客户端),需要两者的地址和端口号都来自于服务器端
import socket
import random
import os
def server():
port=8888
ip_addr = ("10.16.97.201", port)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # socket.AF_INET 指ipv4 socket.SOCK_STREAM 使用tcp协议
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #设置端口
sock.bind((ip_addr)) #绑定端口
sock.listen(3) #监听端口
print('waiting for file...')
while True:
sc,sc_name = sock.accept() #当有请求到指定端口是 accpte()会返回一个新的socket和对方主机的(ip,port)
print('收到{}请求'.format(sc_name))
infor = sc.recv(1024) #首先接收一段数据,这段数据包含文件的长度和文件的名字,使用|分隔,具体规则可以在客户端自己指定
length,file_name = infor.decode().split('|')
(filepath,tempfilename)=os.path.split(file_name)
(shotname,extension)=os.path.splitext(tempfilename)
#portion = os.path.splitext(dirpath) # 把文件名拆分为名字和后缀
#print(portion)
if length and file_name:
image_path='F:/pycharm/client/'
os.makedirs(os.path.dirname(image_path),exist_ok=True)
newfile = open(image_path+shotname+'.jpg','wb') #这里可以使用从客户端解析出来的文件名
print('length {},filename {}'.format(length,file_name))
sc.send(b'ok') #表示收到文件长度和文件名
file = b''
total = int(length)
get = 0
while get < total: #接收文件
data = sc.recv(1024)
file += data
get = get + len(data)
print('应该接收{},实际接收{}'.format(length,len(file)))
if file:
print('acturally length:{}'.format(len(file)))
newfile.write(file[:])
newfile.close()
sc.send(b'copy') #告诉完整的收到文件了
sc.close()
server()
传输结果
#!/usr/bin/python
# -*-coding:utf-8 -*-
import socket
import cv2
import numpy
from time import sleep
# socket.AF_INET 用于服务器与服务器之间的网络通信
# socket.SOCK_STREAM 代表基于TCP的流式socket通信
# 连接服务端
ip_addr = ("10.16.55.26", 1818)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(ip_addr)
# 从摄像头采集图像
# 参数是0,表示打开笔记本的内置摄像头,参数是视频文件路径则打开视频
capture = cv2.VideoCapture(0,cv2.CAP_DSHOW)
# capture.read() 按帧读取视频
# ret,frame 是capture.read()方法的返回值
# 其中ret是布尔值,如果读取帧正确,返回True;如果文件读到末尾,返回False。
# frame 就是每一帧图像,是个三维矩阵
ret, frame = capture.read()
i=25
while ret:
ret, frame = capture.read()
if ret:
i+=1
else:
break
# 首先对图片进行编码,因为socket不支持直接发送图片
# '.jpg'表示把当前图片frame按照jpg格式编码
# result, img_encode = cv2.imencode('.jpg', frame)
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 50]
img_encode = cv2.imencode('.jpg', frame, encode_param)[1]
data = numpy.array(img_encode)
name='{}.jpg'.format(i)
sock.send('{}|{}'.format(len(data), name).encode()) # 默认编码 utf-8,发送文件长度和文件名
reply = sock.recv(1024)
if 'ok' == reply.decode(): # 确认一下服务器get到文件长度和文件名数据
go = 0
total = len(data)
# 然后一个字节一个字节发送编码的内容
while go < total: # 发送文件
data_to_send = data[go:go + 1024]
sock.send(data_to_send)
go += len(data_to_send)
reply = sock.recv(1024)
if 'copy' == reply.decode():
print('{} send successfully'.format(name))
# sleep(1)
# cv2.resize(frame, (640, 480))
# str_data = data.tostring()
# 首先发送图片编码后的长度
# sock.send(str(len(stringData),decoding='utf8').ljust(16))
# 发送
# sock.send(str(len(str_data)).ljust(16).encode())
# 然后一个字节一个字节发送编码的内容
# 如果是python对python那么可以一次性发送,如果发给c++的server则必须分开发因为编码里面有字符串结束标志位,c++会截断
# for i in range(0, len(stringData)):
# sock.send(stringData[i])
# sock.send(str_data)
# ret, frame = capture.read()
# cv2.resize(frame, (640, 480))
sock.close()
cv2.destroyAllWindows()