关于EasyPR识别车牌的调用
关于EasyPR的调用说明
未经允许,严禁外传
请预先阅读 Ubuntu与opencv的配置
0.写在前面
本文最后更新于:2022/5/2 此后不再更新
0.1 预先工作
你需要安装完毕虚拟机VMware
然后安装Ubuntu
0.2 版本说明
Ubuntu为18.xx版本
VMware为16版本
主机系统Win11
0.3注意
本文档基本没有废话!
请您仔细阅读,你感觉没用的都是要做铺垫的。
1.前期准备
首先打开你的Ubuntu
1.1了解你的home
首先打开你的文件图标
![[关于EasyPR识别车牌的调用/文件.png]]
打开后你就可以看到侧边栏
![[关于EasyPR识别车牌的调用/文件目录.png]]
你的主目录就是你的home,而home不是桌面,你可以暂且把home理解为你的C盘。
注意:Linux是没有分区的!这里只是一个比喻
强烈建议你把你的东西放在你的home里面而不是你的桌面
1.2了解你的终端
![[关于EasyPR识别车牌的调用/终端图标.png]]
这是你的终端,就是你大一上常说的黑框框。
1.2.1了解终端命令(重要)
①
cd /home/wjj/opencvdemo
意思:切换到/home/wjj/opencvdemo文件夹内(直接进入到opencvdemo)
注意:
你也可以直接使用右键,打开文件夹,但你的终端就默认执行了
cd /…(你的文件夹)
的命令,你可以这样理解。注意cd后面的路径会有一些讲究,只能逐级访问,不能越级访问!
比如你的/home 下有A与B两个文件夹,A下有c、d两个子文件夹。
那么在c文件夹下直接输入
cd /home/B
是无效的,系统会提示找不到。你可以使用
cd ../B
这里的
..
表示本文件夹的上一级别文件夹,这样可以越级访问了。你也可以用
.
表示当前文件夹迭代使用
..
和.
是允许的,你可用../../
表示上一级的上一级如果你不能理解,建议直接使用手动鼠标切换文件夹再右键打开终端解决
比如:cmake .
表示用cmake编译当前目录
注意:cd后需要跟上一个空格才输入文件路径
②
sudo 命令
相当于Windows
下的管理员模式
③
cp
复制命令,当然我建议你直接鼠标复制粘贴
注意
命令的每一个换行和空格都很重要,请在使用前仔细检查!
1.2.2 VIM编辑器
相当于Windows下的txt编辑器,但其使用比较麻烦,如果你要使用,请自行搜索。
1.2. TXT文本编辑器
如果你上的是老白的课,那么在wsl下,你需要使用vim编辑器来编辑txt文档。但你选择了虚拟机,它提供了一个可视化页面,你可以完全使用可视化页面下的txt文本编辑器来编辑你的c++文本。注意你需要把后缀.txt修改为.cpp
如何找到你的txt文本编辑器呢?首先在你的桌面找到下图左下角那个九宫格小点并点击
随后将会展示你的所有应用
如果你只有很少几个应用,请检查你的最下方是否是“全部”而不是“常用”
第二排第三个就是你的txt编辑器,你可以右键把他放到你的快捷方式栏
你将会用它编写cmakelist.txt和你的代码文本。
2. Make的简单说明
2.1简单介绍
我们编写的大型程序往往由多个编译单元(或者说,多个源代码文件)构成。因此,构建应用时,发出的编译命令可能会比较长。
另外,在开发过程中,可能只修改了一些而不是全部的编译单元,因此在构建时,只需重新编译修改过的编译单元,再重新链接即可。
为达到此目的,推荐的构建方式是使用make工具。
如果你看不懂,咱们不用管
2.2我采用的方法&&原理介绍
——声明——
我不会写make!
我不会写make!
我不会写make!
但是easypr
在源文件里面已经提供了一个cmakelist.txt
用以构建demo
什么是demo呢?
如果你还没有解压EasyPR-master
,请先解压,并放在/home下
现在请你打开你的EasyPR-master
(以下称easypr主文件夹
)
右键打开终端
依次输入:
1 | ./build.sh |
如果你已经按照PPT编译了easypr
,请不要输入第一条
你会看到如下内容:
1 | /////////////////////////////////// |
在其后输入数字可以体验识别车牌的快乐。
其实demo就是一个运行程序,他跟你的c语言大作业编译出来的xxx.exe
文件一样,但Ubuntu不支持双击运行,且它没有后缀。
这个demo是怎么来的呢?
请你打开easypr主文件夹下的CMakeList.txt
文件(下称cmakelist
)
往下滑,你可看到
1 | ....(省略一大堆内容) |
这句话的意思是你要编译的主程序是test文件夹下的main.cpp文件。现在你可以打开easypr主文件夹下的test文件夹,就会发现main.cpp这个文件,这就是demo的主文件。
介绍一下:cpp就是c++,按照老白的新书里面所讲,c++就是一个崭新的c,你大可不必担心自己不会c++,c已经足够了,况且我后面也会介绍c++的特殊用法。声明:我不会c++!我只是随便看了一点点!!!只能处理本课程的作业。
当然,你需要注意的是,如此庞大的工程必定不可能直接用一个main写出来,一定还有其他cpp文件。他们通过cmake的链接、头文件的链接………………一同参与编译,最终形成程序。相当于main是主程序,其他是辅助。
你看到的cmakelist可能是下面这个东西,那么我们怎么实现编译我们自己的写的代码呢?
其实只需要告诉cmakelist,别tm的编译test文件的main.cpp了,去编译我们的文件
在cmakelist里面,#
才是注释,//
不是
1 | cmake_minimum_required(VERSION 3.0.0) |
请务必仔细阅读上方中文注释内容!
建议直接修改主文件里面的cmakelist文件并保存在easypr主文件夹里面,不要另外创建,否则会引发冲突。
3. 代码编写的说明
提示:我并不会c++,以下只是基于我对其粗浅的理解,其中有很多不严谨的地方。
注意:我们将学习c++的类,它跟我们c语言的顺序表很像,所以,请您先仔细看看顺序表章节。(注意顺序表!不是链表!)
本节会比较难,但也是最难的部分。
我把cmakelist都发给你了,编代码不会还要我直接一个字一个字给你打吧……
还是提醒一句:请自己修改你自己的camkelist!不要直接复制粘贴!!!!!!!!!!否则后果很严重(我也解决不了)
3.1 c++的类和申请空间
1 | using namespace std; |
你需要使用以上两行代码来申请内存空间,来存放一个类。
你可以理解为c语言的typedef+结构体,也可以说说定义了一个顺序表的结构体类型(请注意区分结构体
与结构体类型
),结构体里面存放着形如数组元素和last值的参数信息(如果你看不懂这个,建议看看数据结构的书),咱们的顺序表结构体类型就是一个类。
但深层理解我可不会。
如果你不申请,会导致内存访问出错。
1 | terminate called after throwing an instance of 'std::out_of_range' |
形如上方的都是内存访问出错的报错
using namespace建议放在main函数前面。
3.2 Easypr
的一些参数与vector
①
在easypr
提供的文档里面有这样一段代码
1 | CPlateRecognize pr; |
第一行的CPlateRecognize
就是一个类,然后它定义了一个pr的对象(你可以理解为变量),这个变量是CPlateRecognize
类型的
pr这个变量其实是一个结构体
pr.xxx就是对pr这个“结构体”的内容值进行赋值。换个方式理解一下,用顺序表的代码就形如L->elem[1]=0;
②
1 | vector<CPlate> plateVec; |
这里的CPlate也是一个类,vector表明一个容器盒子,用以存储CPlate这个类的数据,你也可以理解为申请一个空间
这是他的官方解释:
向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,vector是一个能够存放任意类型的动态数组。
你可以理解为我们定义了一个动态顺序表叫做plateVec。
为什么要用动态顺序表呢?因为程序不知道你要是别多少个车牌。动态顺序表可以动态扩展存储空间来存放更多的车牌。
我知道你这段可能看不懂,再捋一下。我们向内存申请了一段空间,这个空间大小是”CPlate“这个类的大小。然后,我们的车牌信息将会存储在这个plateVec这个动态顺序表内。如果你要访问这个信息直接输入”plateVec.at(i)
“即可实现,就类似于顺序表的的”L->elem[i]
”
使用vector需要使用其相关头文件,在后面会提到。
③
1 | Mat src = imread(filepath); |
这是opencv提供的,读取文件的函数,src变量用以存储图像信息,类比c语言文件章节的fp指针。
filepath需要你自己填写,代表要识别的图片的地址。请注意填写时需要加上双引号(如“../test/zzz.cpp”
)
④
1 | int result = pr.plateRecognize(src, plateVec); |
这段代码的意思是,把③中src的图像通过easypr识别了,其识别数据传入到你在②定义的 plateVec的结构体
需要注意的是,result并不是识别的车牌信息,而是0与非0值,0代表识别成功(至少一个车牌)
⑤
那么如何获取识别到的信息呢?
请别忘了,我们定义了一个顺序表叫做plateVec,他的类型是CPlate。
plateVec这个动态顺序表下就包含了车牌的众多信息
CPlate plate = plateVec.at(i);
Mat plateMat = plate.getPlateMat();
RotatedRect rrect = plate.getPlatePos();
string license = plate.getPlateStr();
你唯一需要用到的参数是license
(即最后一排),它代表车牌字符串,例如“蓝牌:苏EUK722”。这就是你的识别结果了。
你只需要输出license
即可。
这段话给爱钻研的有能力看的想ball研的室友看:
第一排咱们又定义了一个CPlate类型的变量plate。
我们把识别的第i个车牌的所有信息(
plateVec.at(i)
)存储到这个plate
里面,这个plate
也是一个结构体。后续通过访问plate这个结构体的的具体数据如getPlateStr()
获取这个车牌信息的详细内容比如识别第1个车牌,plateVec.at(1)的内容(第一个车牌的所有信息)就赋值给了plate结构体
第二三排plateMat代表车牌图像,rrect代表车牌的可旋转矩形位置
其他的(前三排)无需修改,但你需要将他们加入到代码里面。
再介绍一个
plateVec.size()
是一个函数,直接调用会返回一个int值,表示easypr实际识别到了多少个车牌。
⑥
我们在①介绍了pr这个变量,pr也是个结构体,其可以设置识别时候的各种参数
比如
1 | pr.setResultShow(false); |
这句话设置EasyPR是否打开结果展示窗口,如下图。设置为true就是打开,否则就是关闭。
在需要观看定位结果时,建议打开,快速运行时(查看识别结果时)关闭。
1 | pr.setDetectType(PR_DETECT_CMSER); |
这句话设置EasyPR采用的车牌定位算法。CMER代表文字定位方法,SOBEL和COLOR分别代表边缘和颜色定位方法。可以通过”|”符号结合。随便你改不改。以上两个设置其中一个即可
1 | pr.setLifemode(true); |
这句话设置开启生活模式,这个能增大搜索范围,推荐打开。
1 | pr.setMaxPlates(4); |
这句话设置EasyPR最多查找多少个车牌。当一副图中有大于n个车牌时,EasyPR最终只会输出可能性最高的n个。
注意:pr.setMaxPlates(4)
不保证一定能识别到4个车牌!建议调大一点,比如要是别5个车牌就改成7-8。
3.3 c++的输出语句
1 | std::cout << "识别第"<<num<<"个车牌为: " << license << endl; |
输出的固定内容用" "
包括,上面语句中num
和license都是变量,前者是int值,后者是字符串。变量和固定内容间用<<
链接。endl表示换行。
比如: 上面的num为1,license为 川A123456
输出:
1 | 识别第1个车牌为: 川A123456 |
4.代码编写实操
头文件和申请空间是固定的
1 | #include <iostream> |
主函数仍然存在
1 | int main(){ |
程序内部(仅供参考:
1 | int main(){ |
注意:为了防止抄袭,每一处TODO的内容都需要自己填写,另外我设置了两个不显而易见的语法错误,请自行阅读报错信息修改。
所有的参数文档都介绍了
5.编译与运行
在easypr主文件夹右键打开终端。
依次输入
1 | cmake . |
如果任何一步出现报错,那么你修改你的程序并需要重新编译!
重新编译只需要删除CMakeCache.txt
再运行上方内容即可。
如果成功创立,你的.exe
程序大概率会放在easypr主文件夹的上一级文件夹里面(一般也就是是你的home),你需要把那个.exe
文件移动到easypr主文件里面。
然后在终端输入
1 | ./xxx |
xxx为你的.exe文件的名字
再次提醒:Ubuntu 下这样编写的运行程序其实没有后缀,为了方便我只是把运行程序叫成 .exe
如果你忘了你的程序名字,你可以打开你的cmakelist,看到这段话
1
2
3
4
5
6 if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
set(EXECUTABLE_NAME "demo")
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
set(EXECUTABLE_NAME "demo")
endif ()
#上方代码的意思是根据你的操作系统命名你的.exe文件,默认是demo,你可以换一个。比如ylg's-rec等等。这段代码的demo处就是你的程序名字。
然后你就会看到识别结果
6.图片
easypr识别3个以上车牌时略显不不靠谱
推荐使用高清拼接图片。
拼接图片可以用PPT拼接
搜索关键词建议:
?还想看呢,自己搜,我不会告诉你的!
7.常见错误
- 如果头文件
easypr.h
报错找不到,请换成相对地址或绝对地址。注意不能用< >
而是用" "
- 如果编译代码时提示”xxx is not declar in this scope”(在这个范围里未定义xxx),首先你检查一下你的拼写拼写错没?包括大小写。有无未定义的情形?若以上都没有,且这个变量凭借你的第六感时easypr给出的,或者这个类名它找不到。那么请在这个变量前加上
easypr::
例如:easypr::CPlateRecognize
- 如果有提示或者
1
2terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr:__pos等字样,说明的内存使用错误1
核心存储已转移
请检查:
①图片的地址是否正确?
②for循环是否超出界限?
③输出的车牌个数必须小于等于plateVec.size()而不是pr.setMaxPlates(x);
④数组的使用
⑤有特殊情况,可能车牌一个都识别不了,建议在for前方输出实际车牌识别的个数值。
- 如果发现复制我的代码,然后出现中文输出异常(例如为:
xxxx
形式的乱码),请你重新手输中文内容,不要复制粘贴。
8.完整代码
1 |
|