1.前言
题目来源东北大学的认知实习,为积累经验,主要的难度还是来源于1.OCR的训练,2.数据库的创建和连接,3.移植系统至移动端,主要流程如下:(当前只完成了主要功能,大佬勿喷QWQ)
2.OCR以及数据集的选取
2.1PaddleOCR
关于深度学习的选取,查阅了很多资料,也看了很多文章,现在主流的det模型主要为yolov5为主,rec模型主要为CRNN或者LPRNet为主,以上的两种模型比较成熟,文件结构也有很多解读,大家也比较认可,官方网站如下:https://github.com/ultralytics/yolov5
但我选取OCR的时候,重点考虑到了算力的部分,我的笔记本为办公本,在使用yolov5进行训练中,一次只能训练数十张照片,训练效果极差,准确率也不行,所以选用办公本进行深度学习是很难的。
但PaddleOCR可以使用百度旗下的AI Studio进行训练(其实按理也可以训练其他的模型),由百度提供算力支持,可以大大节约时间和提高准确性,所以最后选择了百度的PaddleOCR
参考链接:智能交通-车牌识别 - 飞桨AI Studio (baidu.com)
2.2CCPD数据集
现有最大的车牌数据开源数据集:CCPD(Chinese City Parking Dataset, ECCV)
CCPD2019和CCPD2020,前者主要是蓝牌数据,约34W;后者主要是新能源绿牌数据,约1万
AI Studio提供上述两个数据集,并有额外的黄牌数据集:
3.MySQL:数据库的创建与链接
3.1MySQL创建
MySQL官网:MySQL(具体安装教程网上很多,这里就不多赘述了)
MySQL的可视化管理工具,我使用的是:Navicat for MySQL,这是一个付费软件,不过网上也有很多学习版可以下载。
由于项目时间比较紧,而且我专业问题(为什么东B通信工程不学数据库???),所以只建立的一张表用于实现核心功能,主键为车牌,状态01表示是否违停:
如需在此基础上扩展功能,可以多添加几张表
3.2后端与MySQL的链接与交互
主要功能为添加,删除,更改,列表,查找:
Python通过Pymysql以函数的形式实现:
(其中每个函数的输入和输出尽量规范性,用于判断是否成功)
1.添加
def insert_into(car):
#查询重复添加
i = find_mysql(car)
print(i)
if i != -1:
i= -1
return i
# 建立数据库连接
conn = pymysql.connect(
host='localhost',
user='root',
password='chepaishibie',
db='car_license'
)
# 创建游标对象
cursor = conn.cursor()
# 执行SQL插入语句
sql = "INSERT INTO mysql (car_license, state) VALUES (%s, %s)"
values = (car, 0)
cursor.execute(sql, values)
# 提交更改
conn.commit()
#判断是否插入成功
if cursor.rowcount > 0:
tik = 1
else:
tik = 0
# 关闭游标和连接
conn.close()
cursor.close()
return tik
2.删除
def delete_car(car):
# 建立数据库连接
conn = pymysql.connect(
host='localhost',
user='root',
password='chepaishibie',
db='car_license'
)
# 创建游标对象
cursor = conn.cursor()
# 执行删除语句
sql = "DELETE FROM mysql WHERE car_license = %s"
value = car
cursor.execute(sql, (value,))
# 提交更改
conn.commit()
# 关闭游标和连接
cursor.close()
conn.close()
# 查询是否更改成功
tik = find_mysql(car)
if tik == -1:
return 1
else:
return 0
3.更改
def update_car(car,new_state):
# 建立数据库连接
conn = pymysql.connect(
host='localhost',
user='root',
password='chepaishibie',
db='car_license'
)
# 创建游标对象
cursor = conn.cursor()
# 执行SQL更新语句
sql = "UPDATE mysql SET state = %s WHERE car_license = %s"
value1 = new_state
value2 = car
cursor.execute(sql, (value1, value2))
# 提交更改
conn.commit()
# 关闭游标和连接
cursor.close()
conn.close()
4.列表
def list_mysql():
# 建立数据库连接
conn = pymysql.connect(
host='localhost',
user='root',
password='chepaishibie',
db='car_license'
)
# 创建游标对象
cursor = conn.cursor()
# 执行查询语句
sql = "SELECT car_license, state FROM mysql"
cursor.execute(sql)
# 获取查询结果
result = cursor.fetchall()
# 将查询结果存放在一个二维数组中
data_array = []
for row in result:
data_array.append(row)
#关闭游标和连接
cursor.close()
conn.close()
return data_array
5.查找
def find_mysql(car):
# 建立数据库连接
conn = pymysql.connect(
host='localhost',
user='root',
password='chepaishibie',
db='car_license'
)
# 创建游标对象
cursor = conn.cursor()
#查询是否存在
sql = "SELECT car_license FROM mysql where car_license = %s"
value = car
cursor.execute(sql, (value,))
result = cursor.fetchall()
if result:
tik=1
else:
tik=-1
#根据是否存在进行下一步操作
if tik==1:
sql2 = "SELECT state FROM mysql where car_license = %s"
value = car
cursor.execute(sql2, (value,))
result = cursor.fetchall()
result_int = int(result[0][0])
# 关闭游标和连接
cursor.close()
conn.close()
return result_int
else:
# 关闭游标和连接
cursor.close()
conn.close()
return tik
4.移动端部署
移动端部署也有很多方案,但很大的问题还是移动端算力问题,移动端是否能够轻松的运行整个程序是一件很难的事情,于是从工程上来讲,将需求算力的部分部署在服务器端是一件更优的方案
4.1HTML制作前端网页(JavaScript和CSS)
HTML教程可以参照:HTML 基础 | 菜鸟教程 (runoob.com)
小白感谢大佬的干货!
成品演示:(摄像头是主动关的)
以下是我的成品代码:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>车辆违停拍照</title>
<meta content="" name="Keywords" />
<meta content="" name="Description"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="format-detection" content="telephone=no, email=no">
<meta name="HandheldFriendly" content="true" />
</head>
<body>
<style type="text/css">
*{ padding:0; margin:0; }
body{ padding:0 10px }
h1,h2,h3{ margin:10px 0; }
#video{ background:#000; display:block; }
#snap{ width:100px; height:40px; line-height:40px; margin:10px 0; }
</style>
<h1>手持式车辆违停系统网页端</h1>
<h2 id="right"></h2>
<h3 id="support"></h3>
<video id="video" width="320" height="320"></video>
<button id="snap" >拍照</button>
<canvas id="canvas" style="display:none" width="320" height="320"></canvas>
<script>
//判断浏览器是否支持HTML5 Canvas
window.onload = function () {
try {
//动态创建一个canvas元 ,并获取他2Dcontext。如果出现异常则表示不支持 document.createElement("canvas").getContext("2d");
document.getElementById("support").innerHTML = "浏览器支持拍照";
takePhotos();
}
catch (e) {
document.getElementById("support").innerHTML = "浏览器不支持拍照";}
};
//拍照主函数
function takePhotos(){
//这段代 主要是获取摄像头的视频流并显示在Video 签中
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
video = document.getElementById("video");
function successCallback(stream) {
// Set the source of the video element with the stream from the camera
if (video.mozSrcObject !== undefined) {
video.mozSrcObject = stream;
} else {
video.srcObject = stream;
}
video.play();
}
function errorCallback(error) {
alert('错误代码: [CODE ' + error.code + ']');
// Display a friendly "sorry" message to the user
}
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
// Call the getUserMedia method with our callback functions
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true}, successCallback, errorCallback);
} else {
alert('浏览器不支持getUserMedia.');
// Display a friendly "sorry" message to the user
}
//这个是拍照按钮的事件,
document.getElementById("snap").onclick = function(){
context.drawImage(video, 0, 0, 320, 320);
//获取浏览器页面的画布对象
var canvas = document.getElementById("canvas");
//以下开始编 数据
var imageData = canvas.toDataURL();
//将图像转换为base64数据
var img = new Image();
img.src = imageData;
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
// 处理响应数据
var response = xhr.responseText;
document.getElementById("right").innerHTML = response;
} else {
// 处理错误
document.getElementById("right").innerHTML = 'error'; }
};
xhr.open('post','/upload');
var formData = new FormData();
formData.append('image', img.src);
xhr.send(formData);
}
}
</script>
</body>
</html>
4.2Flask后端程序
关于flask这个python的web框架,网上也有很多教程可以学习,此项目的主要功能,主要为网页端上传照片,服务端识别并标记该车牌号,并返回结果。
我的源代码为:
from flask import Flask, request, jsonify, render_template
from flask import Flask, request, make_response
from text_output import car_det_rec
from car_mysql_storage import find_mysql,update_car
import base64
app = Flask(__name__)
app.config['DEFAULT_ENCODING'] = 'UTF-8'
@app.route('/')
def index():
return render_template('test.html')
@app.route('/upload', methods=['POST'])
def upload():
formData = request.form
image_data = formData.get('image')
image_binary = image_data.split(',')[1]
with open('image.png', 'wb') as f:
f.write(base64.b64decode(image_binary))
image_path='D:/python-yolo5/paddleocr-develop/car-manage/image.png'
car=car_det_rec(image_path)
check_car=find_mysql(car)
if check_car==-1:
tik='不存在'
else:
update_car(car,'1')
tik='已标记违停'
car.insert(1, tik)
content = b'\n'.join([s.encode('utf-8') for s in car])
response = make_response(content)
response.charset = 'utf-8'
return response
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
5.总结
本项目基本完成了手持式车辆违停管理系统的基本功能,还有一个后端的数据库管理系统,使用tkinter制作的ui,也学习到很多没有学过的东西,但还有很多地方可以优化的地方