0
点赞
收藏
分享

微信扫一扫

Python、OpenCV实现的电脑远程拍照控制系统,照片并以web形式发布

一、题目:

利用OpenCV等,自行Python编程实现一个远程拍照控制系统,该系统包括摄像头端(Server)和用户端(Client)。Server端运行.py程序,接受Client控制,实现拍照、存储在本地,同时提供Web服务,可以将照片以Web方式发布;Client端运行.py程序,通过按钮或输入特殊字符控制Server拍照,并且可利用浏览器访问Server即时照片。要求至少在局域网内成功运行,鼓励内网穿透,在Internent上测试执行。


二、本程序实现功能


三、建议及注意事项


四、文件夹目录


五、源代码:

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

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>1910300105</title>
</head>

<body>
    <br><br>
    <form method="POST">
        请输入查询密钥:<br>
        <input type="text" name="key">
        <input type="submit" value="查询">
    </form>

</body>

</html>

ShowImg.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>1910300105</title>
</head>

<body>
    <br><br>
    <form method="post">
        请输入查询密钥:<br>
        <input type="text" name="key"value={{key_value}}>
        <input type="submit" value="查询">
    </form>
    <br>
    <p>{{info}}</p>
    <br>
    <img src="../static/{{img_Id}}.jpg">

</body>

</html>

六、源代码运行过程说明

举报

相关推荐

0 条评论