赞
踩
毕业设计想做一个集大学所自学过的所有语言,再加上嵌入式的完整应用类型。但由于疫情的原因,没办法完整的展现。不过中间还是自己一系列将前端、后端、前后端交互、数据库以及Android控制都整了一遍,还是受益匪浅。在此将经验分享出来顺便自己也留恋一下,止增消遣。
在最初,本来想直接在树莓派上跑车牌识别的Python源码,但由于树莓派用的是3B型号的,要跑这种深度学习框架的东西内存不太够,即使改了特征参数还是容易崩掉,最后实在没办法就采用PC做主控端、树莓派做采集端的方案来做。后面发现这样搞还容易加web端这些后续拓展,这也算是曲线救国哈哈
链接:https://pan.baidu.com/s/11HsGVzaFizBd-yzvfqK9gw
提取码:zxuv
具体的源码请看百度网盘下载,也在csdn里扔了一份。这里贴上一部分主要程序,可单独运行。
PC识别主控端:
1.surface.py
# -*- coding: utf-8 -*- import tkinter as tk import urllib from tkinter.filedialog import * from tkinter import ttk import predict import cv2 from PIL import Image, ImageTk import threading import time import numpy as np import urllib.request import socket import MySQLdb # 用于显示的图片的路径 IMAGE_PATH = '.\\pic\\chepai.jpg' host = "192.168.43.161:8080"# 对应树莓派上开启的地址 if len(sys.argv)>1: host = sys.argv[1] hoststr = 'http://' + host + '/?action=stream' print('Streaming ' + hoststr) stream=urllib.request.urlopen(hoststr) bytes=b'' class Surface(ttk.Frame): pic_path = "" viewhigh = 600 viewwide = 600 update_time = 0 thread = None thread_run = False camera = None color_transform = {"green":("绿牌","#55FF55"), "yello":("黄牌","#FFFF00"), "blue":("蓝牌","#6666FF")} def __init__(self, win): ttk.Frame.__init__(self, win) frame_left = ttk.Frame(self) frame_right1 = ttk.Frame(self) frame_right2 = ttk.Frame(self) win.title("车牌识别") win.state("zoomed") self.pack(fill=tk.BOTH, expand=tk.YES, padx="5", pady="5") frame_left.pack(side=LEFT,expand=1,fill=BOTH) frame_right1.pack(side=TOP,expand=1,fill=tk.Y) frame_right2.pack(side=RIGHT,expand=0) ttk.Label(frame_left, text='原图:').pack(anchor="nw") ttk.Label(frame_right1, text='车牌位置:').grid(column=0, row=0, sticky=tk.W) from_pic_ctl = ttk.Button(frame_right2, text="来自图片", width=20, command=self.from_pic) from_vedio_ctl = ttk.Button(frame_right2, text="来自摄像头", width=20, command=self.from_vedio) self.image_ctl = ttk.Label(frame_left) self.image_ctl.pack(anchor="nw") self.roi_ctl = ttk.Label(frame_right1) self.roi_ctl.grid(column=0, row=1, sticky=tk.W) ttk.Label(frame_right1, text='识别结果:').grid(column=0, row=2, sticky=tk.W) self.r_ctl = ttk.Label(frame_right1, text="") self.r_ctl.grid(column=0, row=3, sticky=tk.W) self.color_ctl = ttk.Label(frame_right1, text="", width="20") self.color_ctl.grid(column=0, row=4, sticky=tk.W) from_vedio_ctl.pack(anchor="se", pady="5") from_pic_ctl.pack(anchor="se", pady="5") self.predictor = predict.CardPredictor() self.predictor.train_svm() def get_imgtk(self, img_bgr): img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) im = Image.fromarray(img) imgtk = ImageTk.PhotoImage(image=im) wide = imgtk.width() high = imgtk.height() if wide > self.viewwide or high > self.viewhigh: wide_factor = self.viewwide / wide high_factor = self.viewhigh / high factor = min(wide_factor, high_factor) wide = int(wide * factor) if wide <= 0 : wide = 1 high = int(high * factor) if high <= 0 : high = 1 im=im.resize((wide, high), Image.ANTIALIAS) imgtk = ImageTk.PhotoImage(image=im) return imgtk def show_roi(self, r, roi, color): if r : roi = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB) roi = Image.fromarray(roi) self.imgtk_roi = ImageTk.PhotoImage(image=roi) self.roi_ctl.configure(image=self.imgtk_roi, state='enable') self.r_ctl.configure(text=str(r)) self.update_time = time.time() try: c = self.color_transform[color] self.color_ctl.configure(text=c[0], background=c[1], state='enable') except: self.color_ctl.configure(state='disabled') elif self.update_time + 8 < time.time(): self.roi_ctl.configure(state='disabled') self.r_ctl.configure(text="") self.color_ctl.configure(state='disabled') def from_vedio(self): if self.thread_run: return if self.camera is None: self.camera = cv2.VideoCapture(0) if not self.camera.isOpened(): mBox.showwarning('警告', '摄像头打开失败!') self.camera = None return self.thread = threading.Thread(target=self.vedio_thread, args=(self,bytes)) self.thread.setDaemon(True) self.thread.start() self.thread_run = True def from_pic(self): self.thread_run = False self.pic_path = askopenfilename(title="选择识别图片", filetypes=[("jpg图片", "*.jpg")]) if self.pic_path: img_bgr = predict.imreadex(self.pic_path) self.imgtk = self.get_imgtk(img_bgr) self.image_ctl.configure(image=self.imgtk) r, roi, color = self.predictor.predict(img_bgr) combine(r) self.show_roi(r, roi, color) @staticmethod def vedio_thread(self,bytes): self.thread_run = True predict_time = time.time() while self.thread_run: # _, img_bgr = self.camera.read() # self.imgtk = self.get_imgtk(img_bgr) # self.image_ctl.configure(image=self.imgtk) # if time.time() - predict_time > 2: # r, roi, color = self.predictor.predict(img_bgr) # self.show_roi(r, roi, color) # predict_time = time.time() bytes += stream.read(1024) a = bytes.find(b'\xff\xd8') b = bytes.find(b'\xff\xd9') if a != -1 and b != -1: jpg = bytes[a:b + 2] bytes = bytes[b + 2:] # flags = 1 for color image # 来自网页的摄像头数据 frame = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), flags=1) self.imgtk = self.get_imgtk(frame) self.image_ctl.configure(image=self.imgtk) if time.time() - predict_time > 2: r, roi, color = self.predictor.predict(frame) if len(r): print('OK') combine(r) cv2.imwrite(IMAGE_PATH, frame) # 保存当前识别的图片 image = cv2.imread(IMAGE_PATH) cv2.imshow('img', image) self.show_roi(r, roi, color) cv2.waitKey() predict_time = time.time() print("run end") def close_window(): print("destroy") if surface.thread_run : surface.thread_run = False surface.thread.join(2.0) win.destroy() def combine(r): # 把数组中的字组合起来 result = "" for data in r: result += data send_message(result) send_mysql(result) def send_message(result): try: print('result:', result) if result: client.send(result.encode(encoding='utf-8')) # 不能发空的东西,需要变成utf-8编码形式 print('OK') except Exception: client.close() def send_mysql(result): if result: print('OK5') #连接 cxn = MySQLdb.Connect(host = '127.0.0.1', port = 3306, user = 'root', passwd = '12345678', charset="utf8") #游标 cur = cxn.cursor() cur.execute("USE che1") #首先得运行mysql.py 或者直接在MySQL上创建表 #查询 cur.execute("SELECT * FROM users where name='{}'".format(result))#定义查询 items =cur.fetchall() #获取查询到数据 #将结果集强转为list items = list(items) fw=open('F:\\python\\web端\\flaskweb\\flaskweb\\web.txt','w') #对应web端的直接路径 for t in items: fw.write(' '.join(str(s) for s in t) + '\n') fw.close() #print("保存文件成功") #关闭 cur.close()#关闭游标 cxn.commit()#提交事务 cxn.close()#释放数据库资源在这里插入代码片 if __name__ == '__main__': client = socket.socket() # 有一些默认参数,即可使用ipv4,这一句是声明socket类型和返回socket连接对象 client.connect(("192.168.43.161", 12348)) win = tk.Tk() surface = Surface(win) win.protocol('WM_DELETE_WINDOW', close_window) win.mainloop()
2.predict.py参照之前大佬博客,定位矫正切割识别这部分就改参数,其他不改动
3.mysql.py
import MySQLdb #连接 cxn = MySQLdb.Connect(host = '127.0.0.1', port = 3306, user = 'root', passwd = '12345678', charset="utf8") #游标 cur = cxn.cursor() try: cur.execute("DROP DATABASE che1") except Exception as e: print(e) finally: pass #创建数据库 cur.execute("CREATE DATABASE che1") cur.execute("USE che1") #创建表 cur.execute("CREATE TABLE users (id INT, name VARCHAR(255), username VARCHAR(255), sex VARCHAR(10), birth TIMESTAMP, telephone VARCHAR(20))") #插入 cur.execute("INSERT INTO users VALUES(1, '闽A77518', '李明', '男', '1993-06-05 15:20:00', 13758546621),(2, '皖A87271', '张三', '女', '1993-04-03', 15487596721)") #查询 cur.execute("SELECT * FROM users where name='闽A77518'") for row in cur.fetchall(): print('%s\t%s\t%s\t%s\t%s\t%s' %row) #关闭 cur.close() cxn.commit() cxn.close()
web端
1.flaskweb.py
# _*_ coding:utf-8 _*_ import cv2 from flask import Flask, render_template # 用于显示的图片的路径 IMAGE_PATH = '.\\static\\img\\chepai.jpg' app = Flask(__name__) #程序实例是Flask的对象,一般情况下用如下方法实例化,Flask类只有一个必须指定的参数,即程序主模块或者包的名字,__name__是系统变量,该变量指的是本py文件的文件名""" @app.route("/") #当与前端约定好路由接口路径时会自动执行下方的index函数 def index(): return render_template("index.html") #将参数返回到index.html里 @app.route("/charts/") def charts(): return render_template("charts.html") @app.route("/faq/") def faq(): return render_template("faq.html") @app.route("/grid/") def grid(): return render_template("grid.html") @app.route("/test",methods=['GET']) #与ajax方法的url协定统一路径,使用get无加密方法 def test(): fw=open('web.txt','r') da = fw.read() #读取文件 #print(d) if da.strip()=='': da=('数据库中未有信息') #print(d) return da if __name__ == '__main__': app.run( host='192.168.43.161', #本地IP port= 2222, #端口号 )
2.同目录下templates文件夹下html
<!DOCTYPE html> <html lang="en"><head> <meta charset="utf-8" /> <title>车牌识别门禁系统</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-responsive.min.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/font-awesome.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/adminia.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/pages/plans.css') }}"> <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements --> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script language="javascript"> //页面加载调用 window.onload=function(){ //每1秒刷新时间 setInterval("NowTime()",1000); } function NowTime(){ var myDate=new Date(); var y = myDate.getFullYear(); var M = myDate.getMonth()+1; //获取当前月份(0-11,0代表1月) var d = myDate.getDate(); //获取当前日(1-31) var h = myDate.getHours(); //获取当前小时数(0-23) var m = myDate.getMinutes(); //获取当前分钟数(0-59) var s = myDate.getSeconds(); //获取当前秒数(0-59) //检查是否小于10 M=check(M); d=check(d); h=check(h); m=check(m); s=check(s); var timestr = y+"-"+M+"-"+d+" "+h+":"+m+":"+s; document.getElementById("nowtime").innerHTML="当前时间:" + timestr; } //时间数字小于10,则在之前加个“0”补位。 function check(i){ var num = (i<10)?("0"+i) : i; return num; } </script> </head> <body> <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a> <a class="brand" href="./">车牌识别门禁系统web端</a> </div> <!-- /container --> </div> <!-- /navbar-inner --> </div> <!-- /navbar --> <div id="content"> <div class="container"> <div class="row"> <div class="span3"> <div class="account-container"> <div class="account-avatar"> <img src="{{ url_for('static', filename='img/headshot.png') }}" alt="" class="thumbnail" /> </div> <!-- /account-avatar --> <div class="account-details"> <span class="account-name">引觞垂月</span> <span class="account-role">人间清欢不觉淡 谁知其味漫</span> </div> <!-- /account-details --> </div> <!-- /account-container --> <hr /> <ul id="main-nav" class="nav nav-tabs nav-stacked"> <li> <a href="./"> <i class="icon-home"></i> 首页 </a> </li> <li> <a href="/charts/"> <i class="icon-signal"></i> 图片 </a> </li> <li> <a href="/faq/"> <i class="icon-th-list"></i> 视频 </a> </li> <li> <a href="/grid"> <i class="icon-th-large"></i> 历史文学 </a> </li> <li> <i class="icon-home"></i> 目的: </li> </ul> <hr /> <div class="sidebar-extra"> <p>将车牌识别结果导入数据库里查询车主信息</p> </div> <!-- .sidebar-extra --> <br /> </div> <!-- /span3 --> <div class="span9"> <h1 class="page-title"> <i class="icon-th-list"></i> 车主信息 </h1> <div class="widget"> <div class="widget-header"> <h3>对应信息为: 序号 车牌号 车主名 性别 出生时间 联系方式</h3> </div> <!-- /widget-header --> <div class="widget-content"> <div class="pricing-plans plans-3"> <div id="ajaxDiv" style="margin-top: 20px"></div> </div> <!-- /pricing-plans --> </div> <!-- /widget-content --> </div> <!-- /widget --> <div class="widget"> <div class="widget-header"> <h3>时间</h3> </div> <!-- /widget-header --> <div class="widget-content"> <div id="nowtime">在这里显示时间</div> </div> <!-- /widget-content --> </div> <!-- /widget --> </div> <!-- /span9 --> </div> <!-- /row --> </div> <!-- /container --> </div> <!-- /content --> <!-- Le javascript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="{{ url_for('static', filename='js/jquery-1.7.2.min.js') }}"></script> <script src="{{ url_for('static', filename='js/bootstrap.js') }}"></script> </body> </html> <script type="text/javascript"> setInterval(function(){ $.ajax({ type:"get", dataType:'text', url:"/test", cache:false, success:function(data){ $("#ajaxDiv").html(data); }, error:function(){ alert("失败,请稍后再试!"); } }); },1000); </script>
树莓派上运行rpi.py
import socket import tkinter as tk import threading import MySQLdb import RPi.GPIO import time time_out=5 RELAY=18 def get_message(conn): while True: data = conn.recv(1024) #if not data : #print('this user is end,exit!\n next user') #break if data: print('data:',data.decode()) var.set(data.decode()) l = tk.Label(win,textvariable=var,font=('Arial', 30),width=30,height=10).pack(side='right')# #连接 cxn = MySQLdb.Connect(host = '127.0.0.1', port = 3306, user = 'root', passwd = '123456', charset="utf8") #游标 cur = cxn.cursor() cur.execute("USE che1") #查询 cur.execute("SELECT * FROM users where name='{}'".format(data.decode()))#定义查询 d=cur.fetchall() #fetchall:接收全部的返回结果行 if len(d)>0: RPi.GPIO.setmode(RPi.GPIO.BCM) RPi.GPIO.setup(RELAY,RPi.GPIO.OUT) RPi.GPIO.output(RELAY,RPi.GPIO.HIGH) time.sleep(time_out) RPi.GPIO.output(RELAY,RPi.GPIO.LOW) time.sleep(time_out) RPi.GPIO.cleanup() #关闭 cur.close() cxn.commit() cxn.close() server = socket.socket() server.bind(("192.168.43.197",12348)) #绑定要监听的端口port server.listen(5) # 监听,这里表示最多有5个客户端连接服务器,python2不好使 print('waiting the call') conn,addr = server.accept() # 等电话打进来,每个conn代表一个客户端的连接 print(conn) print('the call has comming') thread = threading.Thread(target = get_message, args = (conn, )) thread.start() win = tk.Tk() win.title('chepai') win.geometry('960x800') tk.Label(win, text='车牌号',font=('Arial', 30),width=30,height=10).pack(side='left')#左 var = tk.StringVar() win.mainloop()
Android通过tcp/ip控制gpio从而控制继电器,源码就不贴了,需要的可以去链接下了看看
1、Raspberry Pi 3B 嵌入式开发板(系统烧写、配置、驱动)
2、7 寸 LCD 触摸屏
3、CSI 摄像头模块(用USB摄像头也行)
4、继电器模块
具体效果视频是当时线上答辩演示时拍的,放在b站上。由于懒写论文就没加Android手动控制,反正也无伤大雅 演示视频.
就后面增加的Android就是类似聊天框通信的那种,很简单界面布置,就当测试用。切记,本人比较懒,IP写死了,需要同一局域网下。
界面如下
前后大概用了20多天的时间,后面会陆续将每天的工作日记写上来。如果有什么错误或者问题,我应该大部分都有遇到,希望能解决各位心中的疑惑。本来想做到服务器上的,可惜腾讯云太坑了硬是域名解析不了,虽说这个东西只需要到本地局域网就可以了但还是很想过个瘾,只能说小小的遗憾咯~
最后,七夕到处浪,写博客它不香吗
人间清欢不觉淡,谁知其味漫,各位七夕快乐!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。