This commit is contained in:
DataXujing 2019-07-06 09:59:59 +08:00
commit 79868c96e2
47 changed files with 193176 additions and 0 deletions

52
README.md Normal file
View File

@ -0,0 +1,52 @@
# 车牌检测和识别的Python应用软件实现
徐静
## 1.车牌检测和识别项目介绍
![](./pic/p2.jpg)
*图片来源https://www.cnblogs.com/polly333/p/7367479.html*
车牌的检测和识别的应用非常广泛,比如交通违章车牌追踪,小区或地下车库门禁。在对车牌识别和检测的过程中,因为车牌往往是规整的矩形,长宽比相对固定,色调纹理相对固定,常用的方法有:基于形状、基于色调、基于纹理、基于文字特征等方法,近年来随着深度学习的发展也会使用目标检测的一些深度学习方法。该项目主要的流程如下图所示:
![](./pic/p1.png)
1.输入原始图片,通过二值化,边缘检测,和基于色调的颜色微调等办法检测出原图中的车牌号的位置;
2.把检测到的车牌(ROI)裁剪,为车牌号的识别做准备;
3.基于裁剪的车牌号使用直方图的波峰波谷分割裁剪的车牌号如上图中的第3步
4.训练机器学习模型做车牌识别这里训练了2个SVM,一个SVM用来识别省份简称(如 鲁)另一个SVM用来识别字母和数字。
5.通过PyQt5把整个算法封装成GUI程序并打包发布安装软件。
## 2.项目代码解析
下图描述了整个项目的代码结构可以访问https://github.com/DataXujing/vehicle-license-plate-recognition 查看,其结构如下:
![](./pic/file_struct.png)
## 3.项目演示
可以通过访问项目地址 ( https://github.com/DataXujing/vehicle-license-plate-recognition )查看整个应用,或者访问安装程序下载地址 https://pan.baidu.com/s/1IazbGFLlQkb8BQmK_EAeRA 提取码v103 )安装安装程序进行测试,这里展示一些识别结果和测试视频:
![](./pic/test1.png)
![](./pic/test2.png)
![](./pic/test3.png)
<video src="./pic/demo.mp4" controls="controls" ></video>
## 4.TODO
目前识别的效果针对于某些场景下仍然很不理想技术层面上的主要原因有两个一个是车牌检测算法并没有检测到车牌这主要是检测算法的问题可以尝试一些目标检测的算法比如Faster R-CNN(速度可能慢一些)YOLO系列, SSD系列等的经典的目标检测算法然后做矫正或进一步的区域筛选另一个原因是是在识别算法上本次我们仅是基于少量的训练数据训练了SVM可以尝试增加训练集并把模型替换成一些更复杂的机器学习模型如XGBoost,LightGBM,CatBoost等模型或使用CNN训练一个多分类的深度学习模型 亦或者是直接考虑一些基于Attention的CNN-RNN架构的OCR识别模型。
## Reference
1.[OpenCV图像识别车牌定位算法源码Python语言实现](https://blog.csdn.net/sumkee911/article/details/79435983)
2.[车牌号识别 python + opencv](https://blog.csdn.net/wzh191920/article/details/79589506)
3 [License-Plate-Recognition](https://github.com/wzh191920/License-Plate-Recognition)
4.[车牌识别(一)-车牌定位](https://www.cnblogs.com/polly333/p/7367479.html)
5.[在PyQt5中美化和装扮图形界面](https://zmister.com/archives/477.html)

279
Ui_my_main_ui.py Normal file
View File

@ -0,0 +1,279 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file
# 'C:\Users\Administrator\Desktop\car_id_detect_reg\card_soft\my_main_ui.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
# __author__ = xujing
# __date__ = 2019-07-05
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QToolTip
from PyQt5.QtGui import QFont,QIcon
import sys
import qtawesome
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(906, 600)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/pic/pic/logo.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
# 将按钮边框去掉,文字设置为白色,背景灰色
qss1 = '''
QPushButton{border:none;color:cyan;}
QPushButton#pushButton_3{
border:none;
border-bottom:1px solid cyan;
font-size:22px;
font-weight:700;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
QPushButton#pushButton_4{
border:none;
border-bottom:1px solid cyan;
font-size:22px;
font-weight:700;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
QPushButton#pushButton_5{
border:none;
border-bottom:1px solid cyan;
font-size:22px;
font-weight:700;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
QPushButton#pushButton_8{
border:none;
border-bottom:1px solid cyan;
font-size:15px;
font-weight:900;
}
QPushButton#pushButton_3:hover{border-left:4px solid red;font-weight:700;}
QPushButton#pushButton_4:hover{border-left:4px solid red;font-weight:700;}
QPushButton#pushButton_5:hover{border-left:4px solid red;font-weight:700;}
QPushButton#pushButton_8:hover{border-left:4px solid red;font-weight:700;}
backfround:black;
'''
# 圆角窗口
qss2 = '''#MainWindow{
background:black;
border-radius:30px
}
'''
# 下拉列表样式
qss3 = '''QComboBox{
border: 1px solid cyan;
border-radius:2px;
color: white;
select-background-color:cyan;
background-color:black;
selection-color:black;
}
'''
# table qss
qss4 = '''
#tableWidget{
bordernone;
border-color:cyan;
background:black;
color: cyan;
font-size: 14px;
font-weight:800;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
'''
# graph qss
qss5 = '''
#graphicsView{
bordernone;
border-color:cyan;
background:black;
}
'''
# button 6,7
qss6 = '''
QPushButton
{
font-size:16px;
font-weight:900;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
/* 前景色 */
color:rgb(248, 248, 255);
/* 背景色 */
background-color:rgb(0,229,238);
/* 边框风格 */
border-style:outset;
/* 边框宽度 */
border-width:2px;
/* 边框颜色 */
border-color:rgb(255,0,0);
/* 边框倒角 */
border-radius:10px;
/* 控件最小宽度 */
min-width:50px;
/* 控件最小高度 */
min-height:20px;
/* 内边距 */
padding:4px;
}
/* 鼠标按下时的效果 */
QPushButton#pushButton:pressed
{
/* 改变背景色 */
background-color:rgb(0,255,127);
/* 改变边框风格 */
border-style:inset;
/* 使文字有一点移动 */
padding-left:3px;
padding-top:3px;
}
/* 按钮样式 */
QPushButton:flat
{
border:2px solid red;
}
/*鼠标悬浮时的效果*/
QPushButton:hover
{
color:rgb(255, 222, 173);
background-color:rgb(0,255,127); /*改变背景色*/
border-style:inset;/*改变边框风格*/
padding-left:3px;
padding-top:3px;
}
'''
MainWindow.setStyleSheet(qss2)
MainWindow.setWindowFlag(QtCore.Qt.FramelessWindowHint) # 隐藏边框
MainWindow.setWindowOpacity(0.95) # 设置窗口透明度
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.centralWidget.setStyleSheet(qss3)
self.label = QtWidgets.QLabel(self.centralWidget)
self.label.setGeometry(QtCore.QRect(30, 50, 611, 501))
self.label.setText("")
self.label.setObjectName("label")
self.pushButton = QtWidgets.QPushButton(self.centralWidget)
self.pushButton.setGeometry(QtCore.QRect(720, 20, 75, 23))
self.pushButton.setObjectName("pushButton")
QToolTip.setFont(QFont("SansSerif",6))
self.pushButton.setToolTip("<b>最小化</b>")
self.pushButton.setStyleSheet('''QPushButton{background:#F76677;border-radius:5px;}QPushButton:hover{background:red;}''')
self.pushButton_2 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_2.setGeometry(QtCore.QRect(810, 20, 75, 23))
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_2.setToolTip("<b>关闭</b>")
self.pushButton_2.setStyleSheet('''QPushButton{background:#6DDF6D;border-radius:5px;}QPushButton:hover{background:green;}''')
self.pushButton_3 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_3.setGeometry(QtCore.QRect(690, 90, 191, 23))
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/pic/pic/cut.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_3.setIcon(icon1)
self.pushButton_3.setObjectName("pushButton_3")
self.pushButton_3.setStyleSheet(qss1)
self.label_2 = QtWidgets.QLabel(self.centralWidget)
self.label_2.setGeometry(QtCore.QRect(700, 140, 181, 61))
self.label_2.setText("")
self.label_2.setObjectName("label_2")
self.pushButton_4 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_4.setGeometry(QtCore.QRect(690, 230, 191, 23))
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/pic/pic/shibie.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_4.setIcon(icon2)
self.pushButton_4.setObjectName("pushButton_4")
self.pushButton_4.setStyleSheet(qss1)
self.label_3 = QtWidgets.QLabel(self.centralWidget)
self.label_3.setGeometry(QtCore.QRect(700, 290, 171, 41))
self.label_3.setText("")
self.label_3.setObjectName("label_3")
self.label_3.setStyleSheet("color: rgb(255, 255, 255);font-size: 24px;font-weight:1000;")
self.pushButton_5 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_5.setGeometry(QtCore.QRect(700, 360, 181, 23))
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(":/pic/pic/color.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_5.setIcon(icon3)
self.pushButton_5.setObjectName("pushButton_5")
self.pushButton_5.setStyleSheet(qss1)
self.label_4 = QtWidgets.QLabel(self.centralWidget)
self.label_4.setGeometry(QtCore.QRect(700, 400, 181, 21))
self.label_4.setStyleSheet("color: rgb(255, 255, 255);")
self.label_4.setText("")
self.label_4.setObjectName("label_4")
self.pushButton_6 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_6.setGeometry(QtCore.QRect(710, 492, 75, 41))
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/pic/pic/image.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_6.setIcon(icon4)
self.pushButton_6.setObjectName("pushButton_6")
self.pushButton_6.setStyleSheet(qss6)
self.pushButton_7 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_7.setGeometry(QtCore.QRect(810, 490, 71, 41))
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(":/pic/pic/video.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_7.setIcon(icon5)
self.pushButton_7.setObjectName("pushButton_7")
self.pushButton_7.setStyleSheet(qss6)
self.pushButton_8 = QtWidgets.QPushButton(self.centralWidget)
self.pushButton_8.setGeometry(QtCore.QRect(30, 10, 231, 31))
font = QtGui.QFont()
font.setFamily("Meiryo UI")
self.pushButton_8.setFont(font)
self.pushButton_8.setIcon(icon)
self.pushButton_8.setObjectName("pushButton_8")
self.pushButton_8.setStyleSheet(qss1)
MainWindow.setCentralWidget(self.centralWidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "交通门禁车牌检测和识别系统"))
self.pushButton.setText(_translate("MainWindow", "-"))
self.pushButton_2.setText(_translate("MainWindow", ""))
self.pushButton_3.setText(_translate("MainWindow", "车牌检测"))
self.pushButton_4.setText(_translate("MainWindow", "车牌识别"))
self.pushButton_5.setText(_translate("MainWindow", "车牌颜色"))
self.pushButton_6.setText(_translate("MainWindow", "图像"))
self.pushButton_7.setText(_translate("MainWindow", "视频"))
self.pushButton_8.setText(_translate("MainWindow", "交通门禁车牌检测和识别系统"))
import my_pic_rc
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

0
__init__.py Normal file
View File

6
_eric6project/car.e4q Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE UserProject SYSTEM "UserProject-4.0.dtd">
<!-- eric6 user project file for project car -->
<!-- Saved: 2019-07-05, 16:11:31 -->
<!-- Copyright (C) 2019 DataXujing, 274762204@qq.com -->
<UserProject version="4.0"/>

7
_eric6project/car.e6t Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Tasks SYSTEM "Tasks-6.0.dtd">
<!-- eric6 tasks file for project car -->
<!-- Saved: 2019-07-05, 16:11:31 -->
<Tasks version="6.0">
<ProjectScanFilter></ProjectScanFilter>
</Tasks>

50
car.e4p Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-5.1.dtd">
<!-- eric project file for project car -->
<!-- Saved: 2019-07-05, 16:11:30 -->
<!-- Copyright (C) 2019 DataXujing, 274762204@qq.com -->
<Project version="5.1">
<Language>en_US</Language>
<Hash>7b44b6b179c6221a079f95a039d33daff1089ecc</Hash>
<ProgLanguage mixed="0">Python3</ProgLanguage>
<ProjectType>PyQt5</ProjectType>
<Description>car id detection.</Description>
<Version>0.1</Version>
<Author>DataXujing</Author>
<Email>274762204@qq.com</Email>
<Eol index="0"/>
<Sources>
<Source>Ui_my_main_ui.py</Source>
<Source>my_main_ui.py</Source>
<Source>my_pic_rc.py</Source>
</Sources>
<Forms>
<Form>my_main_ui.ui</Form>
</Forms>
<Translations/>
<Resources>
<Resource>my_pic.qrc</Resource>
</Resources>
<Interfaces/>
<Others/>
<Vcs>
<VcsType>None</VcsType>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.e4p" type="OTHERS"/>
<FiletypeAssociation pattern="*.idl" type="INTERFACES"/>
<FiletypeAssociation pattern="*.md" type="OTHERS"/>
<FiletypeAssociation pattern="*.py" type="SOURCES"/>
<FiletypeAssociation pattern="*.py3" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw3" type="SOURCES"/>
<FiletypeAssociation pattern="*.qm" type="TRANSLATIONS"/>
<FiletypeAssociation pattern="*.qrc" type="RESOURCES"/>
<FiletypeAssociation pattern="*.rst" type="OTHERS"/>
<FiletypeAssociation pattern="*.ts" type="TRANSLATIONS"/>
<FiletypeAssociation pattern="*.txt" type="OTHERS"/>
<FiletypeAssociation pattern="*.ui" type="FORMS"/>
<FiletypeAssociation pattern="README" type="OTHERS"/>
<FiletypeAssociation pattern="README.*" type="OTHERS"/>
</FiletypeAssociations>
</Project>

294
car_id_detect.py Normal file
View File

@ -0,0 +1,294 @@
'''
基于形状和色调的检测车牌号并提取车牌号图片
'''
import cv2
import numpy as np
from numpy.linalg import norm
import sys
import os
import json
SZ = 20 # 训练图片长宽
MAX_WIDTH = 1000 # 原始图片最大宽度
Min_Area = 2000 # 车牌区域允许最大面积
PROVINCE_START = 1000
def point_limit(point):
if point[0] < 0:
point[0] = 0
if point[1] < 0:
point[1] = 0
def accurate_place(card_img_hsv, limit1, limit2, color,cfg):
row_num, col_num = card_img_hsv.shape[:2]
xl = col_num
xr = 0
yh = 0
yl = row_num
#col_num_limit = cfg["col_num_limit"]
row_num_limit = cfg["row_num_limit"]
col_num_limit = col_num * 0.8 if color != "green" else col_num * 0.5 # 绿色有渐变
for i in range(row_num):
count = 0
for j in range(col_num):
H = card_img_hsv.item(i, j, 0)
S = card_img_hsv.item(i, j, 1)
V = card_img_hsv.item(i, j, 2)
if limit1 < H <= limit2 and 34 < S and 46 < V:
count += 1
if count > col_num_limit:
if yl > i:
yl = i
if yh < i:
yh = i
for j in range(col_num):
count = 0
for i in range(row_num):
H = card_img_hsv.item(i, j, 0)
S = card_img_hsv.item(i, j, 1)
V = card_img_hsv.item(i, j, 2)
if limit1 < H <= limit2 and 34 < S and 46 < V:
count += 1
if count > row_num - row_num_limit:
if xl > j:
xl = j
if xr < j:
xr = j
return xl, xr, yh, yl
def CaridDetect(car_pic):
# 加载图片
img = cv2.imread(car_pic)
pic_hight, pic_width = img.shape[:2]
if pic_width > MAX_WIDTH:
resize_rate = MAX_WIDTH / pic_width
img = cv2.resize(img, (MAX_WIDTH, int(pic_hight*resize_rate)), interpolation=cv2.INTER_AREA)
# 车牌识别的部分参数保存在js中便于根据图片分辨率做调整
f = open('config.js')
j = json.load(f)
for c in j["config"]:
if c["open"]:
cfg = c.copy()
break
else:
raise RuntimeError('[ ERROR ] 没有设置有效配置参数.')
blur = cfg["blur"]
# 高斯去噪
if blur > 0:
img = cv2.GaussianBlur(img, (blur, blur), 0) #图片分辨率调整
oldimg = img
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#equ = cv2.equalizeHist(img)
#img = np.hstack((img, equ))
# 去掉图像中不会是车牌的区域
kernel = np.ones((20, 20), np.uint8)
# morphologyEx 形态学变化函数
img_opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
img_opening = cv2.addWeighted(img, 1, img_opening, -1, 0);
# 找到图像边缘 Canny边缘检测
ret, img_thresh = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
img_edge = cv2.Canny(img_thresh, 100, 200)
# 使用开运算和闭运算让图像边缘成为一个整体
kernel = np.ones((cfg["morphologyr"], cfg["morphologyc"]), np.uint8)
img_edge1 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel)
img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel)
# 查找图像边缘整体形成的矩形区域,可能有很多,车牌就在其中一个矩形区域中
# cv2.findContours()函数来查找检测物体的轮廓
try:
contours, hierarchy = cv2.findContours(img_edge2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
except ValueError:
image, contours, hierarchy = cv2.findContours(img_edge2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = [cnt for cnt in contours if cv2.contourArea(cnt) > Min_Area]
# print('[ INFO ] len(contours): {}'.format(len(contours)))
# 一一排除不是车牌的矩形区域,找到最小外接矩形的长宽比复合车牌条件的边缘检测到的物体
car_contours = []
for cnt in contours:
rect = cv2.minAreaRect(cnt)
# 生成最小外接矩形,点集 cnt 存放的就是该四边形的4个顶点坐标点集里面有4个点
# 函数 cv2.minAreaRect() 返回一个Box2D结构rect最小外接矩形的中心xy宽度高度旋转角度
# 但是要绘制这个矩形我们需要矩形的4个顶点坐标box, 通过函数 cv2.boxPoints() 获得,
# 返回形式[ [x0,y0], [x1,y1], [x2,y2], [x3,y3] ]。
# 得到的最小外接矩形的4个顶点顺序、中心坐标、宽度、高度、旋转角度是度数形式不是弧度数
# https://blog.csdn.net/lanyuelvyun/article/details/76614872
area_width, area_height = rect[1]
if area_width < area_height:
area_width, area_height = area_height, area_width
wh_ratio = area_width / area_height
#print(wh_ratio)
# 要求矩形区域长宽比在2到5.5之间2到5.5是车牌的长宽比,其余的矩形排除 一般的比例是3.5
if wh_ratio > 2 and wh_ratio < 5.5:
car_contours.append(rect)
box = cv2.boxPoints(rect)
box = np.int0(box)
#oldimg = cv2.drawContours(oldimg, [box], 0, (0, 0, 255), 2)
#cv2.imshow("edge4", oldimg)
#print(rect)
# print("[ INFo ] len(car_contours): {}".format(len(car_contours)))
# print("[ INFO ] 精确定位.")
card_imgs = []
# 矩形区域可能是倾斜的矩形,需要矫正,以便使用颜色定位
# 这个就是为什么我们不选择YOLO,SSD或其他的目标检测算法来检测车牌号的原因给自己偷懒找个台阶 :) )
for rect in car_contours:
if rect[2] > -1 and rect[2] < 1:#创造角度,使得左、高、右、低拿到正确的值
angle = 1
else:
angle = rect[2]
rect = (rect[0], (rect[1][0]+5, rect[1][1]+5), angle)#扩大rect范围避免车牌边缘被排除
box = cv2.boxPoints(rect)
# 避免边界超出图像边界
heigth_point = right_point = [0, 0]
left_point = low_point = [pic_width, pic_hight]
for point in box:
if left_point[0] > point[0]:
left_point = point
if low_point[1] > point[1]:
low_point = point
if heigth_point[1] < point[1]:
heigth_point = point
if right_point[0] < point[0]:
right_point = point
if left_point[1] <= right_point[1]: # 正角度
new_right_point = [right_point[0], heigth_point[1]]
pts2 = np.float32([left_point, heigth_point, new_right_point])#字符只是高度需要改变
pts1 = np.float32([left_point, heigth_point, right_point])
M = cv2.getAffineTransform(pts1, pts2) # 仿射变换
dst = cv2.warpAffine(oldimg, M, (pic_width, pic_hight))
point_limit(new_right_point)
point_limit(heigth_point)
point_limit(left_point)
card_img = dst[int(left_point[1]):int(heigth_point[1]), int(left_point[0]):int(new_right_point[0])]
card_imgs.append(card_img)
#cv2.imshow("card", card_img)
#cv2.waitKey(0)
elif left_point[1] > right_point[1]: # 负角度
new_left_point = [left_point[0], heigth_point[1]]
pts2 = np.float32([new_left_point, heigth_point, right_point])#字符只是高度需要改变
pts1 = np.float32([left_point, heigth_point, right_point])
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(oldimg, M, (pic_width, pic_hight))
point_limit(right_point)
point_limit(heigth_point)
point_limit(new_left_point)
card_img = dst[int(right_point[1]):int(heigth_point[1]), int(new_left_point[0]):int(right_point[0])]
card_imgs.append(card_img)
#cv2.imshow("card", card_img)
#cv2.waitKey(0)
# 开始使用颜色定位,排除不是车牌的矩形,目前只识别蓝、绿、黄车牌
colors = []
for card_index,card_img in enumerate(card_imgs):
green = yello = blue = black = white = 0
card_img_hsv = cv2.cvtColor(card_img, cv2.COLOR_BGR2HSV)
#有转换失败的可能,原因来自于上面矫正矩形出错
if card_img_hsv is None:
continue
row_num, col_num= card_img_hsv.shape[:2]
card_img_count = row_num * col_num
for i in range(row_num):
for j in range(col_num):
H = card_img_hsv.item(i, j, 0)
S = card_img_hsv.item(i, j, 1)
V = card_img_hsv.item(i, j, 2)
if 11 < H <= 34 and S > 34:#图片分辨率调整
yello += 1
elif 35 < H <= 99 and S > 34:#图片分辨率调整
green += 1
elif 99 < H <= 124 and S > 34:#图片分辨率调整
blue += 1
if 0 < H <180 and 0 < S < 255 and 0 < V < 46:
black += 1
elif 0 < H <180 and 0 < S < 43 and 221 < V < 225:
white += 1
color = "no"
limit1 = limit2 = 0
if yello*2 >= card_img_count:
color = "yello"
limit1 = 11
limit2 = 34#有的图片有色偏偏绿
elif green*2 >= card_img_count:
color = "green"
limit1 = 35
limit2 = 99
elif blue*2 >= card_img_count:
color = "blue"
limit1 = 100
limit2 = 124#有的图片有色偏偏紫
elif black + white >= card_img_count*0.7: #TODO
color = "bw"
# print("[ INFO ] color: {}".format(color))
colors.append(color)
# print(blue, green, yello, black, white, card_img_count)
#cv2.imshow("color", card_img)
#cv2.waitKey(0)
if limit1 == 0:
continue
#以上为确定车牌颜色
#以下为根据车牌颜色再定位,缩小边缘非车牌边界
xl, xr, yh, yl = accurate_place(card_img_hsv, limit1, limit2, color,cfg)
if yl == yh and xl == xr:
continue
need_accurate = False
if yl >= yh:
yl = 0
yh = row_num
need_accurate = True
if xl >= xr:
xl = 0
xr = col_num
need_accurate = True
card_imgs[card_index] = card_img[yl:yh, xl:xr] if color != "green" or yl < (yh-yl)//4 else card_img[yl-(yh-yl)//4:yh, xl:xr]
if need_accurate:#可能x或y方向未缩小需要再试一次
card_img = card_imgs[card_index]
card_img_hsv = cv2.cvtColor(card_img, cv2.COLOR_BGR2HSV)
xl, xr, yh, yl = accurate_place(card_img_hsv, limit1, limit2, color,cfg)
if yl == yh and xl == xr:
continue
if yl >= yh:
yl = 0
yh = row_num
if xl >= xr:
xl = 0
xr = col_num
card_imgs[card_index] = card_img[yl:yh, xl:xr] if color != "green" or yl < (yh-yl)//4 else card_img[yl-(yh-yl)//4:yh, xl:xr]
roi = card_img
card_color = color
labels = (int(right_point[1]), int(heigth_point[1]), int(left_point[0]), int(right_point[0]))
return roi,labels, card_color#定位的车牌图像、车牌颜色
if __name__ == '__main__':
for pic_file in os.listdir("./test_img"):
roi, label,color = CaridDetect(os.path.join("./test_img",pic_file))
cv2.imwrite(os.path.join("./result",pic_file),roi)
print("*"*50)
print("[ ROI ] {}".format(roi))
print("[ Color ] {}".format(color))
print("[ Label ] {}".format(label))

182
card_seg.py Normal file
View File

@ -0,0 +1,182 @@
import cv2
import numpy as np
from numpy.linalg import norm
import sys
import os
import json
from car_id_detect import *
from svm_train import *
SZ = 20 #训练图片长宽
MAX_WIDTH = 1000 #原始图片最大宽度
Min_Area = 2000 #车牌区域允许最大面积
PROVINCE_START = 1000
svm_model = SVM(C=1, gamma=0.5)
model_1,model_2 = svm_model.train_svm()
def find_waves(threshold, histogram):
'''
根据设定的阈值和图片直方图找出波峰用于分隔字符
'''
up_point = -1 #上升点
is_peak = False
if histogram[0] > threshold:
up_point = 0
is_peak = True
wave_peaks = []
for i,x in enumerate(histogram):
if is_peak and x < threshold:
if i - up_point > 2:
is_peak = False
wave_peaks.append((up_point, i))
elif not is_peak and x >= threshold:
is_peak = True
up_point = i
if is_peak and up_point != -1 and i - up_point > 4:
wave_peaks.append((up_point, i))
return wave_peaks
def seperate_card(img, waves):
'''
根据找出的波峰分隔图片从而得到逐个字符图片
'''
part_cards = []
for wave in waves:
part_cards.append(img[:, wave[0]:wave[1]])
return part_cards
def Cardseg(rois,colors,save_path):
'''
把一个roi列表和color列表对应的每个车牌分割成一个一个的字
然后做预测分类
当然也可以考虑OCR的办法这里使用的是传统的分类问题解决的
'''
seg_dic = {}
old_seg_dic = {}
for i, color in enumerate(colors):
if color in ("blue", "yello", "green"):
card_img = rois[i]
gray_img = cv2.cvtColor(card_img, cv2.COLOR_BGR2GRAY)
#黄、绿车牌字符比背景暗、与蓝车牌刚好相反,所以黄、绿车牌需要反向
if color == "green" or color == "yello":
gray_img = cv2.bitwise_not(gray_img)
ret, gray_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
#查找水平直方图波峰
x_histogram = np.sum(gray_img, axis=1)
x_min = np.min(x_histogram)
x_average = np.sum(x_histogram)/x_histogram.shape[0]
x_threshold = (x_min + x_average)/2
wave_peaks = find_waves(x_threshold, x_histogram)
if len(wave_peaks) == 0:
# print("peak less 0:")
continue
#认为水平方向,最大的波峰为车牌区域
wave = max(wave_peaks, key=lambda x:x[1]-x[0])
gray_img = gray_img[wave[0]:wave[1]]
#查找垂直直方图波峰
row_num, col_num= gray_img.shape[:2]
#去掉车牌上下边缘1个像素避免白边影响阈值判断
gray_img = gray_img[1:row_num-1]
y_histogram = np.sum(gray_img, axis=0)
y_min = np.min(y_histogram)
y_average = np.sum(y_histogram)/y_histogram.shape[0]
y_threshold = (y_min + y_average)/5 #U和0要求阈值偏小否则U和0会被分成两半
wave_peaks = find_waves(y_threshold, y_histogram)
#for wave in wave_peaks:
# cv2.line(card_img, pt1=(wave[0], 5), pt2=(wave[1], 5), color=(0, 0, 255), thickness=2)
#车牌字符数应大于6
if len(wave_peaks) <= 6:
# print("peak less 1:", len(wave_peaks))
continue
wave = max(wave_peaks, key=lambda x:x[1]-x[0])
max_wave_dis = wave[1] - wave[0]
#判断是否是左侧车牌边缘
if wave_peaks[0][1] - wave_peaks[0][0] < max_wave_dis/3 and wave_peaks[0][0] == 0:
wave_peaks.pop(0)
#组合分离汉字
cur_dis = 0
for i,wave in enumerate(wave_peaks):
if wave[1] - wave[0] + cur_dis > max_wave_dis * 0.6:
break
else:
cur_dis += wave[1] - wave[0]
if i > 0:
wave = (wave_peaks[0][0], wave_peaks[i][1])
wave_peaks = wave_peaks[i+1:]
wave_peaks.insert(0, wave)
#去除车牌上的分隔点
point = wave_peaks[2]
if point[1] - point[0] < max_wave_dis/3:
point_img = gray_img[:,point[0]:point[1]]
if np.mean(point_img) < 255/5:
wave_peaks.pop(2)
if len(wave_peaks) <= 6:
# print("peak less 2:", len(wave_peaks))
continue
part_cards = seperate_card(gray_img, wave_peaks)
predict_result = []
for i, part_card in enumerate(part_cards):
#可能是固定车牌的铆钉
if np.mean(part_card) < 255/5:
# print("a point")
continue
part_card_old = part_card
w = abs(part_card.shape[1] - SZ)//2
part_card = cv2.copyMakeBorder(part_card, 0, 0, w, w, cv2.BORDER_CONSTANT, value = [0,0,0]) #用来给图片添加边框
part_card = cv2.resize(part_card, (SZ, SZ), interpolation=cv2.INTER_AREA)
#part_card = deskew(part_card)
part_card = preprocess_hog([part_card])
if i == 0:
resp = model_2.predict(part_card)
charactor = provinces[int(resp[0]) - PROVINCE_START]
else:
resp = model_1.predict(part_card)
charactor = chr(resp[0])
#判断最后一个数是否是车牌边缘假设车牌边缘被认为是1
if charactor == "1" and i == len(part_cards)-1:
if part_card_old.shape[0]/part_card_old.shape[1] >= 7:#1太细认为是边缘
continue
predict_result.append(charactor)
# # 保存图片
# cv2.imwrite(os.path.join(save_path,str(i)+".jpg"),part_card)
seg_dic[i] = part_cards
old_seg_dic[i] = part_card_old
return seg_dic, old_seg_dic, predict_result
if __name__ == "__main__":
for pic_file in os.listdir("./test_img"):
roi, label, color = CaridDetect(os.path.join("./test_img",pic_file))
save_path = "./result_seg/"+pic_file.split(".")[0]
if not os.path.exists(save_path):
os.makedirs(save_path)
seg_dict, _ , pre= Cardseg([roi],[color],save_path)
print(pre)

20
config.js Normal file
View File

@ -0,0 +1,20 @@
{
"config":[
{
"open":1,
"blur":3,
"morphologyr":4,
"morphologyc":19,
"col_num_limit":10,
"row_num_limit":21
},
{
"open":0,
"blur":3,
"morphologyr":5,
"morphologyc":12,
"col_num_limit":10,
"row_num_limit":18
}
]
}

BIN
logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

136
my_main_ui.py Normal file
View File

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
"""
__author__ = xujing
__date__ = 2019-07-05
"""
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5 import *
from PyQt5.QtGui import *
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow
from Ui_my_main_ui import Ui_MainWindow
import sys
import cv2
from car_id_detect import *
from svm_train import *
from card_seg import *
class MainWindow(QMainWindow, Ui_MainWindow):
"""
槽函数
"""
def __init__(self, parent=None):
"""
Constructor
@param parent reference to the parent widget
@type QWidget
"""
super(MainWindow, self).__init__(parent)
self.setupUi(self)
@pyqtSlot()
def on_pushButton_clicked(self):
"""
最下化
"""
print('最小化')
QMainWindow.showMinimized(self)
@pyqtSlot()
def on_pushButton_2_clicked(self):
"""
退出
"""
print("退出")
sys.exit(0)
@pyqtSlot()
def on_pushButton_6_clicked(self):
"""
加载图像
"""
print("加载图像")
try:
self.file_dir_temp,_ = QFileDialog.getOpenFileName(self,"选择被检测的车辆","D:/")
self.file_dir = self.file_dir_temp.replace("\\","/")
print(self.file_dir)
roi, label, color = CaridDetect(self.file_dir)
seg_dict, _, pre = Cardseg([roi],[color],None)
print(pre)
# segment
cv2.imwrite(os.path.join("./temp/seg_card.jpg"),roi)
seg_img = cv2.imread("./temp/seg_card.jpg")
seg_rows, seg_cols, seg_channels = seg_img.shape
bytesPerLine = seg_channels * seg_cols
cv2.cvtColor(seg_img, cv2.COLOR_BGR2RGB,seg_img)
QImg = QImage(seg_img.data, seg_cols, seg_rows,bytesPerLine, QImage.Format_RGB888)
self.label_2.setPixmap(QPixmap.fromImage(QImg).scaled(self.label_2.size(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
# reg result
pre.insert(2,"·")
self.label_3.setText(" "+"".join(pre))
# clor view
if color == "yello":
self.label_4.setStyleSheet("background-color: rgb(255, 255, 0);")
elif color == "green":
self.label_4.setStyleSheet("background-color: rgb(0, 255,0);")
elif color == "blue":
self.label_4.setStyleSheet("background-color: rgb(0, 0, 255);")
else:
self.label_4.setText("未识别出车牌颜色")
frame = cv2.imread(self.file_dir)
# cv2.rectangle(frame, (label[0],label[2]), (label[1],label[3]), (0,0,255), 2)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame, 'https://github.com/DataXujing/vehicle-license-plate-recognition', (10, 10), font, 0.3, (0, 0, 255), 1)
img_rows, img_cols, channels = frame.shape
bytesPerLine = channels * img_cols
cv2.cvtColor(frame, cv2.COLOR_BGR2RGB,frame)
QImg = QImage(frame.data, img_cols, img_rows,bytesPerLine, QImage.Format_RGB888)
self.label.setPixmap(QPixmap.fromImage(QImg).scaled(self.label.size(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
QtWidgets.QApplication.processEvents()
except Exception as e:
QMessageBox.warning(self,"错误提示","[错误提示(请联系开发人员处理)]\n" + str(e)+"\n或识别失败导致")
@pyqtSlot()
def on_pushButton_7_clicked(self):
"""
加载视频
"""
print("加载视频")
QMessageBox.information(self,"加载实时视频","未检测到实时视频源或暂未开通快该服务!")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
splash = QSplashScreen(QtGui.QPixmap(':/pic/pic/face.png'))
splash.show()
splash.showMessage('渲染界面...')
QThread.sleep(0.6)
splash.showMessage('正在初始化程序...')
QThread.sleep(0.6)
app. processEvents()
ui =MainWindow()
ui.show()
splash.finish(ui)
sys.exit(app.exec_())

36
my_main_ui.spec Normal file
View File

@ -0,0 +1,36 @@
# -*- mode: python -*-
block_cipher = None
a = Analysis(['my_main_ui.py'],
pathex=['C:\\Users\\xujing.LAPTOP-LLR84L1D\\Desktop\\car_id_detect_reg\\card_soft'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='my_main_ui',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False , icon='logo.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='my_main_ui')

215
my_main_ui.ui Normal file
View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>906</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>交通门禁车牌检测和识别系统</string>
</property>
<property name="windowIcon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/logo.ico</normaloff>:/pic/pic/logo.ico</iconset>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>30</x>
<y>50</y>
<width>611</width>
<height>501</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>720</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>最小化</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>810</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>关闭</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_3">
<property name="geometry">
<rect>
<x>690</x>
<y>90</y>
<width>191</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>车牌检测</string>
</property>
<property name="icon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/cut.png</normaloff>:/pic/pic/cut.png</iconset>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>700</x>
<y>140</y>
<width>181</width>
<height>61</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_4">
<property name="geometry">
<rect>
<x>690</x>
<y>230</y>
<width>191</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>车牌识别</string>
</property>
<property name="icon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/shibie.png</normaloff>:/pic/pic/shibie.png</iconset>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>700</x>
<y>290</y>
<width>171</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_5">
<property name="geometry">
<rect>
<x>700</x>
<y>360</y>
<width>181</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>车牌颜色</string>
</property>
<property name="icon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/color.png</normaloff>:/pic/pic/color.png</iconset>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>700</x>
<y>400</y>
<width>181</width>
<height>21</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(0, 0, 255);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton_6">
<property name="geometry">
<rect>
<x>710</x>
<y>492</y>
<width>75</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>图像</string>
</property>
<property name="icon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/image.png</normaloff>:/pic/pic/image.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="pushButton_7">
<property name="geometry">
<rect>
<x>810</x>
<y>490</y>
<width>71</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>视频</string>
</property>
<property name="icon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/video.png</normaloff>:/pic/pic/video.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="pushButton_8">
<property name="geometry">
<rect>
<x>30</x>
<y>10</y>
<width>201</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<family>Meiryo UI</family>
</font>
</property>
<property name="text">
<string>交通门禁车牌检测和识别系统</string>
</property>
<property name="icon">
<iconset resource="my_pic.qrc">
<normaloff>:/pic/pic/logo.ico</normaloff>:/pic/pic/logo.ico</iconset>
</property>
</widget>
</widget>
</widget>
<resources>
<include location="my_pic.qrc"/>
</resources>
<connections/>
</ui>

11
my_pic.qrc Normal file
View File

@ -0,0 +1,11 @@
<RCC>
<qresource prefix="pic">
<file>pic/color.png</file>
<file>pic/cut.png</file>
<file>pic/face.png</file>
<file>pic/image.png</file>
<file>pic/logo.ico</file>
<file>pic/shibie.png</file>
<file>pic/video.png</file>
</qresource>
</RCC>

71955
my_pic_rc.py Normal file

File diff suppressed because it is too large Load Diff

BIN
pic/color.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
pic/cut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
pic/demo.mp4 Normal file

Binary file not shown.

BIN
pic/face.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
pic/file_struct.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
pic/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
pic/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
pic/p1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

BIN
pic/p2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
pic/shibie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
pic/test1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

BIN
pic/test2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

BIN
pic/test3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 KiB

BIN
pic/video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

6
requirement.txt Normal file
View File

@ -0,0 +1,6 @@
cv2
numpy
json
PyQt5
qtawesome
pyinstaller

8
run.bat Normal file
View File

@ -0,0 +1,8 @@
@echo off
color a
echo [ INFO ] Time :
time/T
python my_main_ui.py
pause

173
svm_train.py Normal file
View File

@ -0,0 +1,173 @@
'''
训练svm
'''
import cv2
import numpy as np
from numpy.linalg import norm
import sys
import os
import json
SZ = SZ = 20
PROVINCE_START = 1000
provinces = [
"zh_cuan", "",
"zh_e", "",
"zh_gan", "",
"zh_gan1", "",
"zh_gui", "",
"zh_gui1", "",
"zh_hei", "",
"zh_hu", "",
"zh_ji", "",
"zh_jin", "",
"zh_jing", "",
"zh_jl", "",
"zh_liao", "",
"zh_lu", "",
"zh_meng", "",
"zh_min", "",
"zh_ning", "",
"zh_qing", "",
"zh_qiong", "",
"zh_shan", "",
"zh_su", "",
"zh_sx", "",
"zh_wan", "",
"zh_xiang", "",
"zh_xin", "",
"zh_yu", "",
"zh_yu1", "",
"zh_yue", "",
"zh_yun", "",
"zh_zang", "",
"zh_zhe", ""
]
# 数据处理
def deskew(img):
m = cv2.moments(img)
if abs(m['mu02']) < 1e-2:
return img.copy()
skew = m['mu11']/m['mu02']
M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]])
img = cv2.warpAffine(img, M, (SZ, SZ), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)
return img
# 特征工程
def preprocess_hog(digits):
samples = []
for img in digits:
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
mag, ang = cv2.cartToPolar(gx, gy)
bin_n = 16
bin = np.int32(bin_n*ang/(2*np.pi))
bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:]
mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:]
hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
hist = np.hstack(hists)
# transform to Hellinger kernel
eps = 1e-7
hist /= hist.sum() + eps
hist = np.sqrt(hist)
hist /= norm(hist) + eps
samples.append(hist)
return np.float32(samples)
class StatModel(object):
def load(self, fn):
self.model = self.model.load(fn)
def save(self, fn):
self.model.save(fn)
class SVM(StatModel):
def __init__(self, C = 1, gamma = 0.5):
self.model = cv2.ml.SVM_create()
self.model.setGamma(gamma)
self.model.setC(C)
self.model.setKernel(cv2.ml.SVM_RBF)
self.model.setType(cv2.ml.SVM_C_SVC)
# train svm
def train(self, samples, responses):
self.model.train(samples, cv2.ml.ROW_SAMPLE, responses)
# inference
def predict(self, samples):
r = self.model.predict(samples)
return r[1].ravel()
def train_svm(self):
#识别英文字母和数字
self.model = SVM(C=1, gamma=0.5)
#识别中文
self.modelchinese = SVM(C=1, gamma=0.5)
if os.path.exists("./train_dat/svm.dat"):
self.model.load("./train_dat/svm.dat")
else:
chars_train = []
chars_label = []
for root, dirs, files in os.walk("./train/chars2"):
if len(os.path.basename(root)) > 1:
continue
root_int = ord(os.path.basename(root))
for filename in files:
filepath = os.path.join(root,filename)
digit_img = cv2.imread(filepath)
digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
chars_train.append(digit_img)
#chars_label.append(1)
chars_label.append(root_int)
chars_train = list(map(deskew, chars_train))
chars_train = preprocess_hog(chars_train)
#chars_train = chars_train.reshape(-1, 20, 20).astype(np.float32)
chars_label = np.array(chars_label)
print(chars_train.shape)
self.model.train(chars_train, chars_label)
if os.path.exists("./train_dat/svmchinese.dat"):
self.modelchinese.load("./train_dat/svmchinese.dat")
else:
chars_train = []
chars_label = []
for root, dirs, files in os.walk("./train/charsChinese"):
if not os.path.basename(root).startswith("zh_"):
continue
pinyin = os.path.basename(root)
index = provinces.index(pinyin) + PROVINCE_START + 1 #1是拼音对应的汉字
for filename in files:
filepath = os.path.join(root,filename)
digit_img = cv2.imread(filepath)
digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
chars_train.append(digit_img)
#chars_label.append(1)
chars_label.append(index)
chars_train = list(map(deskew, chars_train))
chars_train = preprocess_hog(chars_train)
#chars_train = chars_train.reshape(-1, 20, 20).astype(np.float32)
chars_label = np.array(chars_label)
print(chars_train.shape)
self.modelchinese.train(chars_train, chars_label)
return self.model, self.modelchinese
def save_traindata(self):
if not os.path.exists("./train_dat/svm.dat"):
self.model.save("./train_dat/svm.dat")
if not os.path.exists("./train_dat/svmchinese.dat"):
self.modelchinese.save("./train_dat/svmchinese.dat")
if __name__ == "__main__":
svm_model = SVM(C=1, gamma=0.5)
# svm_model.save_traindata()
model_1,model_2 = svm_model.train_svm()
print(model_1)

BIN
temp/seg_card.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
test_img/cAA662F.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
test_img/green.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
test_img/lLD9016.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
test_img/result/test1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

BIN
test_img/result/test2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

BIN
test_img/result/test3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

BIN
test_img/result/test4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

BIN
test_img/result/test5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

BIN
test_img/result/test6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 KiB

BIN
test_img/wA87271.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
test_img/wAUB816.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test_img/yello.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

67352
train_dat/svm.dat Normal file

File diff suppressed because it is too large Load Diff

52394
train_dat/svmchinese.dat Normal file

File diff suppressed because it is too large Load Diff