#ifndef EASYPR_CORE_PLATEJUDGE_H_
#define EASYPR_CORE_PLATEJUDGE_H_

#include "easypr/core/plate.hpp"
#include "easypr/core/feature.h"//参见文末6

namespace easypr {

class PlateJudge {
 public:

//类名后面加*,表示对应类的指针类型。对应变量值为类指针类型,函数的返回值只一个Room指针,它的定义参见文末2
  static PlateJudge* instance();

//加载模型文件xml
  void LoadModel(std::string path)

//使用NMS删除重叠的框,参见文末5
int plateJudgeUsingNMS(const std::vector<CPlate>&, std::vector<CPlate>&, int maxPlates = 5);

//,通过调用SVM模型进行判别, 设置车牌分数,0 is plate, -1 is not.参见文末4
  int plateSetScore(CPlate& plate);

 //三个构造函数,定义在plate_judge.cpp

//将输出普通Mat转化为CPlate对象,调用plateSetScore函数求出其score
int plateJudge(const Mat& plateMat);

//输入一组Mat,对其Mat逐个调用上面结构的plateJudge,求出每个Mat对应的score
int plateJudge(const std::vector<Mat> &inVec,std::vector<Mat> &resultVec);

//同上,输入一组CPlate对象,取出它的Mat成员,然后对其逐个调用上面第一个结构的plateJudge
  int plateJudge(const std::vector<CPlate> &inVec,std::vector<CPlate> &resultVec);

private:
  // singleton,构造函数,参见文末3
  PlateJudge();

static PlateJudge* instance_;//定义指针

svmCallback extractFeature;//定义回调函数

cv::Ptr<ml::SVM> svm_;//SVM对象

};
}

#endif  // EASYPR_CORE_PLATEJUDGE_H_

1、我们看到头文件#include "easypr/core/plate.hpp",该头文件主要以类的形式定义了一个车牌类型,用于存放车牌区域的信息,例如,坐标,大小,score,等等。

这个hpp文件和h文件有什么区别呢?

hpp,顾名思义等于.h加上.cpp ,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库。(1个cpp生成1个 obj文件,1个project可以有若干个cpp文件组成,每个各自产生自己的 obj文件。然后通过链接,把 obj文件们和 库文件们链接成 exe 文件(或DLL文件) .  )

hpp的优点不少,但是编写中有以下几点要注意:https://blog.csdn.net/shinehoo/article/details/6317747

2、

//PlateJudge* 代表参数instance_的类型,它是一个PlateJudge类的类指针
PlateJudge* PlateJudge::instance_ = nullptr;//就是定义出一个PlateJudge*指针PlateJudge* PlateJudge::instance() {if (!instance_) {//当指针是空的时候,生成一个新的instance_ = new PlateJudge;//这个到底是怎么调用??????}return instance_;}

instance_是一个变量,是PlateJudge的指针类型,它指向的地方保存着一个PlateJudge类型的对象 ,

代码和这个一样
AAA x;
AAA*a;
a=&x;
可以用*a来代替对象x;

3、私有函数PlateJudge的定义,是否使用LBP特征。

PlateJudge::PlateJudge() { bool useLBP = false;if (useLBP) {LOAD_SVM_MODEL(svm_, kLBPSvmPath);//地址存放SVM模型xml文件extractFeature = getLBPFeatures;//函数定义在feature.cpp中,计算提取特征}else {LOAD_SVM_MODEL(svm_, kHistSvmPath);//地址存放SVM模型xml文件extractFeature = getHistomPlusColoFeatures;}}

这里我们重点看一下:extractFeature = getHistomPlusColoFeatures;

getHistomPlusColoFeatures是定义的一个函数,用于提取特征:

//! color feature and histom
void getHistomPlusColoFeatures(const cv::Mat& image, cv::Mat& features);

extractFeature对象 的定义为:svmCallback  extractFeature;其中svmCallback的声明是:

//! EasyPR的getFeatures回调函数
//! 用于从车牌的image生成svm的训练特征features
typedef void (*svmCallback)(const cv::Mat& image, cv::Mat& features);

“”svmCallback  extractFeature“”的意思是:声明了一个函数指针,用于保存传入的函数地址(即getHistomPlusColoFeatures函数的地址)。

 我的理解是,把函数getHistomPlusColoFeatures的地址赋给了extractFeature,因此当调用extractFeature时,会回调getHistomPlusColoFeatures函数,这样的方便时,我们可以自己定义不同的特征提取函数,然后把函数命名赋给extractFeature。

什么是回调函数(callback)
    模块A有一个函数foo,他向模块B传递foo的地址,然后在B里面发生某种事件(event)时,通过从A里面传递过来的foo的地址调用foo,通知A发生了什么事情,让A作出相应反应。 那么我们就把foo称为回调函数。

声明回调函数类型:
  typedef int (WINAPI *PFCALLBACK)(int Param1,int Param2) ;实际上是声明了一个返回值为int,传入参数为两个int的指向函数的指针。

举个回调函数的例子:

#include <stdio.h>
//回调函数
int ADD(int (*callback)(int,int), int a, int b){return (*callback)(a,b);//此处回调add函数...
}
//普通函数
int add(int a, int b){return a + b;
}int main(void){printf("%d\n",add(1,2));printf("%d\n",ADD(add,1,2));return 0;
}

具体可参考:https://blog.csdn.net/hellozex/article/details/81742348

4、调用SVM求每个车牌的score,这里score表示样本与决策边界的距离,score存放到plate类中的成员中,用于NMS,同时,根据score的大小,该函数返回0,-1,代表是否为车牌。

int PlateJudge::plateSetScore(CPlate& plate) {Mat features;extractFeature(plate.getPlateMat(), features);//回调特征提取函数,计算特征float score = svm_->predict(features, noArray(), cv::ml::StatModel::Flags::RAW_OUTPUT);//std::cout << "score:" << score << std::endl;if (0) {imshow("plate", plate.getPlateMat());waitKey(0);destroyWindow("plate");}// 分数score是margin的距离,零下是车牌,大于0则不是车牌,当得分低于零时,值越小,成为车牌的可能性越大。
//如果距离分类超平面(hyperlane)最近的数据点的距离(margin)越大,该分类器的噪声容忍度就越大,plate.setPlateScore(score);//注意这里的setPlateScore函数是类CPlate里定义的函数if (score < 0.5) return 0;else return -1;      }

5、NMS,computeIOU函数定义在core_func.cpp

void NMS(std::vector<CPlate> &inVec, std::vector<CPlate> &resultVec, double overlap) {std::sort(inVec.begin(), inVec.end());std::vector<CPlate>::iterator it = inVec.begin();for (; it != inVec.end(); ++it) {CPlate plateSrc = *it;//std::cout << "plateScore:" << plateSrc.getPlateScore() << std::endl;Rect rectSrc = plateSrc.getPlatePos().boundingRect();std::vector<CPlate>::iterator itc = it + 1;for (; itc != inVec.end();) {CPlate plateComp = *itc;//plateComp.getPlatePos()得到一个RotatedRect,再用boundingRect()求这个旋转矩形的最小外接矩形Rect rectComp = plateComp.getPlatePos().boundingRect();float iou = computeIOU(rectSrc, rectComp);//计算重叠区域if (iou > overlap) {itc = inVec.erase(itc);//IOU面积大于阈值,删除这个框}else {++itc;}}}resultVec = inVec;}float computeIOU(const Rect &rect1, const Rect &rect2) {Rect inter = interRect(rect1, rect2);Rect urect = mergeRect(rect1, rect2);float iou = (float) inter.area() / (float) urect.area();return iou;}Rect interRect(const Rect &a, const Rect &b) {Rect c;int x1 = a.x > b.x ? a.x : b.x;int y1 = a.y > b.y ? a.y : b.y;c.width = (a.x + a.width < b.x + b.width ? a.x + a.width : b.x + b.width) - x1;c.height = (a.y + a.height < b.y + b.height ? a.y + a.height : b.y + b.height) - y1;c.x = x1;c.y = y1;if (c.width <= 0 || c.height <= 0)c = Rect();return c;}Rect mergeRect(const Rect &a, const Rect &b) {Rect c;int x1 = a.x < b.x ? a.x : b.x;int y1 = a.y < b.y ? a.y : b.y;c.width = (a.x + a.width > b.x + b.width ? a.x + a.width : b.x + b.width) - x1;c.height = (a.y + a.height > b.y + b.height ? a.y + a.height : b.y + b.height) - y1;c.x = x1;c.y = y1;return c;}

6、#include "easypr/core/feature.h"包含求取车牌图像特征的头文件,函数定义在feature.cpp中

//! 获得车牌的特征数
cv::Mat getHistogram(cv::Mat in);

//! EasyPR的getFeatures回调函数, 用于从车牌的image生成svm的训练特征features
typedef void (*svmCallback)(const cv::Mat& image, cv::Mat& features);

//! EasyPR的getFeatures回调函数, 从车牌image生成gray ann的训练特征features
typedef void (*annCallback)(const cv::Mat& image, cv::Mat& features);

//! gray and project feature
void getGrayPlusProject(const cv::Mat& grayChar, cv::Mat& features);

//!本函数是获取垂直和水平的直方图图值
void getHistogramFeatures(const cv::Mat& image, cv::Mat& features);

//! 本函数是获取SIFT特征子
void getSIFTFeatures(const cv::Mat& image, cv::Mat& features);

//! 本函数是获取HOG特征子
void getHOGFeatures(const cv::Mat& image, cv::Mat& features);

//! 本函数是获取HSV空间量化的直方图特征子
void getHSVHistFeatures(const cv::Mat& image, cv::Mat& features);

//! LBP feature。。。。。等等

easyPR源码解析之plate_judge.h相关推荐

  1. easyPR源码解析之chars_segment.h

    chars_segment.h用于从已经通过SVM判别得到的车牌区域中将车牌的字符分割开,用于下一步的ANN字符识别. namespace easypr { class CCharsSegment { ...

  2. easyPR源码解析之ann_train.h/config.h

    ann_train.h源码定义一个 AnnTrain类,该类继承自ITrain类(在train.h文件中): #include "easypr/train/train.h" #in ...

  3. easyPR源码解析之chars_identify.h

    在上一篇文章的介绍中,我们已经通过相应的字符分割方法,将车牌区域进行分割,得到7个分割字符图块,接下来要做的就是将字符图块放入训练好的神经网络模型,通过模型来预测每个图块所表示的具体字符.本节主要介绍 ...

  4. easyPR源码解析之plate_locate.h

    从今天开始,准备一点一点的啃代码easyPR项目. 我们先从libesypr文件下的源文件/core/plate_locate.cpp开始: plate_locate.cpp文件中包含的头文件如下: ...

  5. 【darknet源码解析-24】shortcut_layer.h 和 shortcut_layer.c 解析

    本系列为darknet源码解析,本次解析src/short_layer.h 与 src/short_layer.c 两个.在yolo v3中short_layer主要完成直连操作,完成残差块中的恒等映 ...

  6. H.264压缩技术之视频基础(foundation of learning video)——Matlab源码解析

    前言 为了后续能更好的理解,I帧编码与P帧编码,所以笔者先对数字视频中的一些基础概念进行铺垫.后续比较复杂的帧内预测,与帧间预测理解起来就会相对容易些. 关于Matlab中h.264的main函数部分 ...

  7. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  8. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  9. libev源码解析——监视器(watcher)结构和组织形式

    在<libev源码解析--总览>中,我们介绍了libev的一些重要变量在不同编译参数下的定义位置.由于这些变量在多线程下没有同步问题,所以我们将问题简化,所提到的变量都是线程内部独有的,不 ...

最新文章

  1. Verilog中实现电平检测
  2. Kafka项目实战-用户日志上报实时统计之编码实践
  3. 在打开的Web表单模态窗口中,避免点击服务端控件时弹出新窗口的技巧
  4. mfc 找到字符串中字符_[LeetCode] 467. 环绕字符串中唯一的子字符串
  5. CF1313D:Happy New Year(状压dp)
  6. python渲染光线_python模板渲染配置文件
  7. Sql Update Alter Rename
  8. webbrowser只对浏览器外应用程序以及在_常用浏览器大盘点!
  9. Lync Server 服务器版本升级
  10. 大气压力换算公式_常用压力单位换算表
  11. java闰年的计算方法_java中对 闰年的计算 以及月份天数
  12. 英语单词之说文解字(7)
  13. gst-launch命令转换为C代码(gstreamer框架)
  14. windows 10专业版关闭自动更新
  15. CTF实验吧-简单的sql注入【SQL注入关键词绕过】
  16. 读文献——《Deep Residual Learning for Image Recognition》
  17. 排列显示阿拉伯语、数字及英文时的处理方法
  18. java-01背包(动态规划)
  19. Python 视频教程百度云分享
  20. [剑指Offer]斐波那契数列、跳台阶、兔子数量问题(递归、非递归)(Java)

热门文章

  1. MYSQL 常用查询命令
  2. 抗锯齿 文字_PS之使用文字工具
  3. esp32查询剩余内存_SQL 查询语句先执行 SELECT?兄弟你认真的么?
  4. python的repl模式_如何更改sublimeREPL默认python版本
  5. 20171107校内模拟赛
  6. bzoj2101【Usaco2010 Dec】Treasure Chest 藏宝箱
  7. Android中Service的使用
  8. 19个很有用的 JavaScript 库
  9. Flutter AnimatedWidget 实现动画的自动刷新
  10. Mr.J-- HTTP学习笔记(六)-- 代理