利用OpenCV等,自行Python编程实现一个远程拍照控制系统,该系统包括摄像头端(Server)和用户端(Client)。Server端运行.py程序,接受Client控制,实现拍照、存储在本地,同时提供Web服务,可以将照片以Web方式发布;Client端运行.py程序,通过按钮或输入特殊字符控制Server拍照,并且可利用浏览器访问Server即时照片。要求至少在局域网内成功运行,鼓励内网穿透,在Internent上测试执行。
二、本程序实现功能
本程序实现功能:
程序通过多线程实现了多个客户端可以同时远程拍照,并以客户端的mac地址作为客户端id和图片的名称,拍完照片后并通过加密返回给客户端一个密钥。然后服务器也建立一个线程,专门通过flask发布照片。客户端通过网页凭密钥访问照片,服务器解密密钥,从而得到需要访问照片的名称,以此多个客户端只可以访问到自己所拍的照片。照片以web形式发布。
三、建议及注意事项
建议:
建议在虚拟环境里安装包依赖、运行程序
注意:
本程序仅适配于局域网,外网请自行配置内网穿透
本程序服务端客户端运行系统是Win10
Sever.py端的Python解释器版本3.6.6。不得用高版本,否则,opencv报错
切记包版本不要更改,尤其是numpy,否则opencv报错
文末有程序运行过程的说明,仅供参考
四、文件夹目录
五、源代码: Server.py包依赖
文件名:requirements.txt
click==8.0.4
colorama==0.4.4
dataclasses==0.8
Flask==2.0.3
importlib-metadata==4.8.3
itsdangerous==2.0.1
Jinja2==3.0.3
MarkupSafe==2.0.1
numpy==1.19.5
opencv-python==3.4.1.15
typing-extensions==4.1.1
Werkzeug==2.0.3
zipp==3.6.0
Server.py
from concurrent.futures import thread
from distutils.log import debug
from flask import Flask, render_template, request
import cv2
import numpy as np
import threading
import socket
import os
#加密字典对照
dict_Password={'0':'L','1':'X','2':'H','3':'0',\
'4':'1','5':'9','6':'3','7':'Y',\
'8':'J','9':'2','a':'4','b':'Q',\
'c':'Z','d':'6','e':'D','f':'8'}
#这是加密函数,对需要加密的字符串进行加密,返回值为加密后的字符串
def data_Encrypt(key):
i_key=''
for x in key: #遍利需要加密的字符串每个元素,并将每个元素(键)按加密字典对照表(10-13行)得到其对应的值,然后拼接成字符串
i_key=i_key+dict_Password[x]
return i_key
#这是解密函数,对需要解密的字符串进行解密,返回值为解密后的字符串
def data_Decrypt(key):
i_key=''
for x in key:#遍利需要解密的字符串的每个元素,每个元素通过值来查找获得对应的键。 然后拼接成字符串
k2 = [k for k, v in dict_Password.items() if v == x]
k2=''.join(k2)
i_key=i_key+k2
return i_key
#这是一个拍照函数,传入的id用以给图片命名
def camera_Save_Fun(id):
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
cv2.imwrite(r"static/"+id+".jpg",frame)
cap.release()
cv2.destroyAllWindows()
#这是一个flask服务,并将其放到一个线程里,用以提供web服务
def flask_Thead_Fun():
# 创建Flask对象
app = Flask(__name__)
# route()函数告诉那个URL执行哪个函数
@app.route("/",methods=['GET', 'POST'])
def index():
if request.method == 'POST':#提交表单后,浏览器向服务器发送一个post请求,其中包含参数,这是处理post请求的部分
print('接收的密钥值为'+request.form.get('key'))#request.form.get('key')是为了获得post中的参数,即密钥
img_id=data_Decrypt(request.form.get('key'))#将密钥进行解密,解密后的值赋给img_id,img_id的意思是本地图片的名字
if os.path.exists("static/"+img_id+".jpg"):#if语句用来判断本地图片(img_id)是否存在
sys_Info="您所拍的照片如下:" #如果存在,将存在的提示信息赋值给sys_Info
else: #如果不存在,将不存在的提示信息赋值给sys_Info
sys_Info="您还未拍照片或照片已删除,请重拍"
#将图片是否存在的信息、本地图片的id(img_id)赋值给 HTML,然后生成静态网页,
#key_value=request.form.get('key') 是为了刷新html后,表单中的值(即密钥)依然存在,不用再输入一遍
return render_template('ShowImg.html',info= sys_Info,img_Id=img_id,key_value=request.form.get('key'))
else:
return render_template('index.html')#浏览器第一次访问肯定是get请求,所以返回的是只含有表单的网页,
#提交表单之后就是post请求,就可以处理post参数(即密钥)了
if __name__ == "__main__":
app.run(host="0.0.0.0",debug = False) #可访问127.0.0.1:5000,或者localhost:5000
#以下两种线程函数实现了,多个客户端可以同时访问服务器进行拍照,通过传来的客户端id(client_id)进行区分不同的客户端,并通过客户端id命名图片
# 拿到客户端id的同时,会加密 客户端id 得到一个密钥(client_id_encrypt),并再发送给客户端,客户端在网页中输入密钥,递交表单,flask会解密,
# 解密后得到正确的客户端id名,即图片的名称,然后就可以访问此电脑拍摄的图片,而不是其他电脑的图片。加密解密是为了更安全
#这是一个管理客户端连接的线程。循环为需要连接的客户端分配一个客户端数据传输的线程
def client_Connect_Thead_Fun():
while True:
conn, client_addr = server_sk.accept()
client_data_Thead = threading.Thread(target=client_data_Thead_Fun,args=(conn,))#为需要连接的客户端分配一个客户端数据传输的线程
client_data_Thead.start()
#这是一个客户端数据传输的线程
def client_data_Thead_Fun(conn):
#接收客户端id
data_client = conn.recv(1024)
client_id=data_client.decode('utf-8')
if client_id == '':
pass
else:
print('服务器已连接ID为{0}的客户端'.format(client_id))
client_id_encrypt=data_Encrypt(client_id)#加密客户端id,得到密钥,赋给client_id_encrypt
message="您的密钥为: "+client_id_encrypt#将密钥发送给客户端
conn.send(message.encode('utf-8'))
#异常处理是因为如果客户端直接断开连接,服务器不会崩溃
try:
while True:
# 接收客户端信息
data_client = conn.recv(1024)
print(data_client.decode('utf-8'))
#客户端发来的信息若是y,就拍照,是deleteImg,就删除照片,是exit,就跳出循环,关闭和客户端的连接
if data_client.decode('utf-8') == 'y':
camera_Save_Fun(client_id)#调用拍照函数,将客户端id传给此函数
message="提示:您的密钥为"+client_id_encrypt+"请访问xxxxxx查看照片"#信息提示传给客户端
conn.send(message.encode('utf-8'))
elif data_client.decode('utf-8') == 'deleteImg':
try:#异常处理。处理文件不存在的异常,并将是否删除成功的信息传给客户端
os.remove("static/"+client_id+".jpg")
message="删除图片成功"
conn.send(message.encode('utf-8'))
except FileNotFoundError:
print('无法删除:系统找不到static/{0}.jpg文件'.format(client_id))
message="无法删除,系统找不到图片文件"
conn.send(message.encode('utf-8'))
elif data_client.decode('utf-8') == 'exit':
break #跳出循环
else:
message="输入错误,请重新输入"
conn.send(message.encode('utf-8'))
conn.close()
print('服务器已断开ID为{0}的客户端'.format(client_id))
except:
print('ID为{0}的客户端强迫关闭了现有的连接'.format(client_id))
#从这里开始运行!!!
server_sk = socket.socket()
# 设置给定套接字选项的值。
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 把地址绑定到套接字
server_sk.bind(('', 8000))#默认8000为本机socket通信端口
# 监听链接
server_sk.listen()
# 接受客户端链接
#启动flask和管理客户端连接的线程
if __name__ == '__main__':
flask_Thead = threading.Thread(target=flask_Thead_Fun,args=())
client_Connect_Thead = threading.Thread(target=client_Connect_Thead_Fun,args=())
flask_Thead.start()
client_Connect_Thead.start()
Client.py
若服务器和客户端运行在同一局域网,请将xxxxxxxx改为服务器的局域网IP,否则改为公网IP
import socket
import uuid
#获取本机的mac并返回
def get_mac_address():
mac=uuid.UUID(int = uuid.getnode()).hex[-12:]
return "".join([mac[e:e+2] for e in range(0,11,2)])
client_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#进行连接服务器
#client_sk.connect(('xxxxxx', 0000))#公网IP端口
client_sk.connect(('xxxxxxxx', 8000))#局域网服务器的IP及服务器socket端口,端口默认是8000
#将mac作为客户端id。发送给服务器,然后接收服务器发来的密钥和提示信息(接受的信息赋给data_server),密钥用以访问拍摄的图片
message=get_mac_address()
client_sk.send(message.encode('utf-8'))
data_server=client_sk.recv(1024)#接受服务端的信息,最大数据为1k
print(data_server.decode('utf-8'))
#循环
while True:
message=''
message=input('拍照[y/deleteImg/exit]')
client_sk.send(message.encode('utf-8'))#将发送的数据进行编码
if message=='exit':
client_sk.close()
print('客户端已退出')
break
data_server=client_sk.recv(1024)#接受服务端的信息,最大数据为1k
print(data_server.decode('utf-8'))
index.html
1910300105
ShowImg.html
1910300105
{{info}}
{img_Id}}.jpg">
六、源代码运行过程说明
服务器程序运行过程:
首先创建套接字的基本信息,然后给flask分配一个线程并启动,然后再给负责客户端连接的函数分配一个线程,用于检测是否有其他客户端连接进来。若有客户端连接进来,那么在为其分配一个数据传输的线程,用来传输数据。在数据传输线程中,首先会收到来自于客户端的mac地址即id,然后。服务器端进行加密,返回给客户端一个密钥,然后接下来会有一个异常处理作用是为了如果。客户端强迫关闭连接的话,服务器端程序不会崩溃。在异常处理的try里面,是一个循环用来循环检测客户端是否发来数据,并判断数据是什么,如果是y,则调用拍照函数,并将客户端的ID即mac作为拍照后图片的名称 ,如果是deleteImg,则删除图片,这里也有一个对,如果图片不存在的异常处理。如果是exit,则跳出循环,关闭与客户端的连接,然后此线程关闭。如果客户端输入的是其他信息,则向客户端发送输入错误,请重新输入。
flask运行过程:客户端访问网站会返回给浏览器初始页面,然后客户端输入,你要点击查询后,flask会接收到post的请求,然后解密后得到客户端ID既图片的名称,判断此图片是否在服务器中,然后将相应的提示信息赋给变量。然后将各参数传给html,然后返回html
客户端运行程序过程:建立套接字 获取本机的mac地址,然后接收服务器发来的信息,然后进入循环
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)