Add: ROS wrapper

master
1580336037@qq.com 3 years ago
parent aa70e2f0bd
commit 40c33caf02

6
.gitignore vendored

@ -1,9 +1,15 @@
# Editor
*~
*.swp
.vscode
# Build files
Prj-Linux/lpr/TEST_*
Prj-Linux/*build*/
*.pyc
/Prj-PHP/build
/Prj-ROS/build
/Prj-ROS/devel
/Prj-ROS/logs
/Prj-ROS/.catkin_tools

@ -0,0 +1,48 @@
# Prj-ROS
HyperLPR的ROS包封装了Prj-Linux的代码并为适配ROS进行了少量修改在Ubuntu20.04中ros-noetic上进行了测试。
## 安装依赖
``` bash
sudo apt install ros-noetic-desktop-full
```
ros-noetic使用的opencv版本为opencv4可能会与opencv3冲突请注意停用opencv3以免无法编译运行
## 编译
本文件所在路径为catkin_wsrospackage路径为 `/HyperLPR/Prj-ROS/src/hyperlpr`
``` bash
cd Prj-ROS
catkin build
echo "source ${PATH_TO_HYPERLPR}/Prj-ROS/devel/setup.bash" >> ~/.bashrc
```
进入`/Prj-ROS/src/hyperlpr/launch/bringup.launch`,修改`CameraTopic`为自己相机图像的topic
## 运行
``` bash
roslaunch hyperlpr bringup.launch
```
## 说明
与ros node有关的代码位于`/Prj-ROS/src/hyperlpr/src`中的`hyperlpr_node.h`和`hyperlpr_node.cpp`中,可以按需修改
运行时的rqt_graph如下
![image](./docs/rosgraph.png)
测试时使用的相机为Intel-RealSense D455`bringup.launch`中默认CameraTopic为RealSense彩色图像topic
发布的三个topic分别为
+ /recognition_confidence 识别的置信度
+ /recognition_result 识别的结果不过ROS不支持中文字符所以在使用时需要对订阅的数据另作处理想立即确定中文结果也可以查看terminal中ROS_INFO输出的结果
+ /recognized_image 框选出一帧所有可识别车牌的图像
以上三个topic均只对置信度大于75%的识别作出反应,可以调整代码中的置信度阈值以获得期待的识别效果

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.0.2)
project(hyperlpr)
set(SRC_DETECTION src/PlateDetection.cpp src/util.h include/PlateDetection.h)
set(SRC_FINEMAPPING src/FineMapping.cpp )
set(SRC_FASTDESKEW src/FastDeskew.cpp )
set(SRC_SEGMENTATION src/PlateSegmentation.cpp )
set(SRC_RECOGNIZE src/Recognizer.cpp src/CNNRecognizer.cpp)
set(SRC_PIPLINE src/Pipeline.cpp)
set(SRC_SEGMENTATIONFREE src/SegmentationFreeRecognizer.cpp )
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(OpenCV REQUIRED)
find_package(catkin REQUIRED COMPONENTS
cmake_modules
roscpp
rospy
std_msgs
sensor_msgs
geometry_msgs
cv_bridge
image_transport
)
catkin_package(
)
include_directories(
include
${catkin_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
)
add_executable(${PROJECT_NAME}_node ${SRC_DETECTION} ${SRC_FINEMAPPING} ${SRC_FASTDESKEW} ${SRC_SEGMENTATION} ${SRC_RECOGNIZE} ${SRC_PIPLINE} ${SRC_SEGMENTATIONFREE} src/hyperlpr_node.cpp src/main.cpp)
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
${OpenCV_LIBS}
)

@ -0,0 +1,24 @@
//
// Created by Jack Yu on 21/10/2017.
//
#ifndef HYPERPR_CNNRECOGNIZER_H
#define HYPERPR_CNNRECOGNIZER_H
#include "Recognizer.h"
namespace pr {
class CNNRecognizer : public GeneralRecognizer {
public:
const int CHAR_INPUT_W = 14;
const int CHAR_INPUT_H = 30;
CNNRecognizer(std::string prototxt, std::string caffemodel);
label recognizeCharacter(cv::Mat character);
private:
cv::dnn::Net net;
};
} // namespace pr
#endif // HYPERPR_CNNRECOGNIZER_H

@ -0,0 +1,17 @@
//
// Created by Jack Yu on 22/09/2017.
//
#ifndef HYPERPR_FASTDESKEW_H
#define HYPERPR_FASTDESKEW_H
#include <math.h>
#include <opencv2/opencv.hpp>
namespace pr {
cv::Mat fastdeskew(cv::Mat skewImage, int blockSize);
// cv::Mat spatialTransformer(cv::Mat skewImage);
} // namespace pr
#endif // HYPERPR_FASTDESKEW_H

@ -0,0 +1,29 @@
//
// Created by Jack Yu on 22/09/2017.
//
#ifndef HYPERPR_FINEMAPPING_H
#define HYPERPR_FINEMAPPING_H
#include <opencv2/dnn.hpp>
#include <opencv2/opencv.hpp>
#include <string>
namespace pr {
class FineMapping {
public:
FineMapping();
FineMapping(std::string prototxt, std::string caffemodel);
static cv::Mat FineMappingVertical(cv::Mat InputProposal, int sliceNum = 15,
int upper = 0, int lower = -50,
int windows_size = 17);
cv::Mat FineMappingHorizon(cv::Mat FinedVertical, int leftPadding,
int rightPadding);
private:
cv::dnn::Net net;
};
} // namespace pr
#endif // HYPERPR_FINEMAPPING_H

@ -0,0 +1,57 @@
//
// Created by Jack Yu on 22/10/2017.
//
#ifndef HYPERPR_PIPLINE_H
#define HYPERPR_PIPLINE_H
#include "CNNRecognizer.h"
#include "FastDeskew.h"
#include "FineMapping.h"
#include "PlateDetection.h"
#include "PlateInfo.h"
#include "PlateSegmentation.h"
#include "Recognizer.h"
#include "SegmentationFreeRecognizer.h"
namespace pr {
const std::vector<std::string> CH_PLATE_CODE{
"", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "0", "1", "2", "3", "4",
"5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G",
"H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U",
"V", "W", "X", "Y", "Z", "", "", "使", "", "", "", "",
"", "", "广", "", "", "", "", "", "", "", ""};
const int SEGMENTATION_FREE_METHOD = 0;
const int SEGMENTATION_BASED_METHOD = 1;
class PipelinePR {
public:
GeneralRecognizer *generalRecognizer;
PlateDetection *plateDetection;
PlateSegmentation *plateSegmentation;
FineMapping *fineMapping;
SegmentationFreeRecognizer *segmentationFreeRecognizer;
PipelinePR();
~PipelinePR();
void initialize(std::string detector_filename,
std::string finemapping_prototxt,
std::string finemapping_caffemodel,
std::string segmentation_prototxt,
std::string segmentation_caffemodel,
std::string charRecognization_proto,
std::string charRecognization_caffemodel,
std::string segmentationfree_proto,
std::string segmentationfree_caffemodel);
std::vector<std::string> plateRes;
std::vector<PlateInfo> RunPiplineAsImage(cv::Mat plateImage, int method);
};
} // namespace pr
#endif // HYPERPR_PIPLINE_H

@ -0,0 +1,32 @@
//
// Created by Jack Yu on 20/09/2017.
//
#ifndef HYPERPR_PLATEDETECTION_H
#define HYPERPR_PLATEDETECTION_H
#include <PlateInfo.h>
#include <opencv2/opencv.hpp>
#include <vector>
namespace pr {
class PlateDetection {
public:
PlateDetection(std::string filename_cascade);
PlateDetection();
void LoadModel(std::string filename_cascade);
void plateDetectionRough(cv::Mat InputImage,
std::vector<pr::PlateInfo> &plateInfos,
int min_w = 36, int max_w = 800);
// std::vector<pr::PlateInfo> plateDetectionRough(cv::Mat
// InputImage,int min_w= 60,int max_h = 400);
// std::vector<pr::PlateInfo>
// plateDetectionRoughByMultiScaleEdge(cv::Mat InputImage);
private:
cv::CascadeClassifier cascade;
};
} // namespace pr
#endif // HYPERPR_PLATEDETECTION_H

@ -0,0 +1,94 @@
//
// Created by Jack Yu on 20/09/2017.
//
#ifndef HYPERPR_PLATEINFO_H
#define HYPERPR_PLATEINFO_H
#include <opencv2/opencv.hpp>
namespace pr {
typedef std::vector<cv::Mat> Character;
enum PlateColor { BLUE, YELLOW, WHITE, GREEN, BLACK, UNKNOWN };
enum CharType { CHINESE, LETTER, LETTER_NUMS, INVALID };
class PlateInfo {
public:
std::vector<std::pair<CharType, cv::Mat>> plateChars;
std::vector<std::pair<CharType, cv::Mat>> plateCoding;
float confidence = 0;
PlateInfo(const cv::Mat &plateData, std::string plateName, cv::Rect plateRect,
PlateColor plateType) {
licensePlate = plateData;
name = plateName;
ROI = plateRect;
Type = plateType;
}
PlateInfo(const cv::Mat &plateData, cv::Rect plateRect,
PlateColor plateType) {
licensePlate = plateData;
ROI = plateRect;
Type = plateType;
}
PlateInfo(const cv::Mat &plateData, cv::Rect plateRect) {
licensePlate = plateData;
ROI = plateRect;
}
PlateInfo() {}
cv::Mat getPlateImage() { return licensePlate; }
void setPlateImage(cv::Mat plateImage) { licensePlate = plateImage; }
cv::Rect getPlateRect() { return ROI; }
void setPlateRect(cv::Rect plateRect) { ROI = plateRect; }
cv::String getPlateName() { return name; }
void setPlateName(cv::String plateName) { name = plateName; }
int getPlateType() { return Type; }
void appendPlateChar(const std::pair<CharType, cv::Mat> &plateChar) {
plateChars.push_back(plateChar);
}
void appendPlateCoding(const std::pair<CharType, cv::Mat> &charProb) {
plateCoding.push_back(charProb);
}
std::string decodePlateNormal(std::vector<std::string> mappingTable) {
std::string decode;
for (auto plate : plateCoding) {
float *prob = (float *)plate.second.data;
if (plate.first == CHINESE) {
decode += mappingTable[std::max_element(prob, prob + 31) - prob];
confidence += *std::max_element(prob, prob + 31);
}
else if (plate.first == LETTER) {
decode += mappingTable[std::max_element(prob + 41, prob + 65) - prob];
confidence += *std::max_element(prob + 41, prob + 65);
}
else if (plate.first == LETTER_NUMS) {
decode += mappingTable[std::max_element(prob + 31, prob + 65) - prob];
confidence += *std::max_element(prob + 31, prob + 65);
} else if (plate.first == INVALID) {
decode += '*';
}
}
name = decode;
confidence /= 7;
return decode;
}
private:
cv::Mat licensePlate;
cv::Rect ROI;
std::string name;
PlateColor Type;
};
} // namespace pr
#endif // HYPERPR_PLATEINFO_H

@ -0,0 +1,35 @@
#ifndef HYPERPR_PLATESEGMENTATION_H
#define HYPERPR_PLATESEGMENTATION_H
#include "PlateInfo.h"
#include "opencv2/opencv.hpp"
#include <opencv2/dnn.hpp>
namespace pr {
class PlateSegmentation {
public:
const int PLATE_NORMAL = 6;
const int PLATE_NORMAL_GREEN = 7;
const int DEFAULT_WIDTH = 20;
PlateSegmentation(std::string phototxt, std::string caffemodel);
PlateSegmentation() {}
void segmentPlatePipline(PlateInfo &plateInfo, int stride,
std::vector<cv::Rect> &Char_rects);
void segmentPlateBySlidingWindows(cv::Mat &plateImage, int windowsWidth,
int stride, cv::Mat &respones);
void templateMatchFinding(const cv::Mat &respones, int windowsWidth,
std::pair<float, std::vector<int>> &candidatePts);
void refineRegion(cv::Mat &plateImage, const std::vector<int> &candidatePts,
const int padding, std::vector<cv::Rect> &rects);
void ExtractRegions(PlateInfo &plateInfo, std::vector<cv::Rect> &rects);
cv::Mat classifyResponse(const cv::Mat &cropped);
private:
cv::dnn::Net net;
};
} // namespace pr
#endif // HYPERPR_PLATESEGMENTATION_H

@ -0,0 +1,22 @@
//
// Created by Jack Yu on 20/10/2017.
//
#ifndef HYPERPR_RECOGNIZER_H
#define HYPERPR_RECOGNIZER_H
#include "PlateInfo.h"
#include "opencv2/dnn.hpp"
namespace pr {
typedef cv::Mat label;
class GeneralRecognizer {
public:
virtual label recognizeCharacter(cv::Mat character) = 0;
// virtual cv::Mat SegmentationFreeForSinglePlate(cv::Mat plate) =
// 0;
void SegmentBasedSequenceRecognition(PlateInfo &plateinfo);
void SegmentationFreeSequenceRecognition(PlateInfo &plateInfo);
};
} // namespace pr
#endif // HYPERPR_RECOGNIZER_H

@ -0,0 +1,27 @@
//
// Created by Jack Yu on 28/11/2017.
//
#ifndef HYPERPR_SEGMENTATIONFREERECOGNIZER_H
#define HYPERPR_SEGMENTATIONFREERECOGNIZER_H
#include "Recognizer.h"
namespace pr {
class SegmentationFreeRecognizer {
public:
const int CHAR_INPUT_W = 14;
const int CHAR_INPUT_H = 30;
const int CHAR_LEN = 84;
SegmentationFreeRecognizer(std::string prototxt, std::string caffemodel);
std::pair<std::string, float>
SegmentationFreeForSinglePlate(cv::Mat plate,
std::vector<std::string> mapping_table);
private:
cv::dnn::Net net;
};
} // namespace pr
#endif // HYPERPR_SEGMENTATIONFREERECOGNIZER_H

@ -0,0 +1,49 @@
#ifndef HYPERLPR_NODE_H
#define HYPERLPR_NODE_H
#include "Pipeline.h"
#include "ros/ros.h"
#include "sensor_msgs/CompressedImage.h"
#include "sensor_msgs/image_encodings.h"
#include "std_msgs/String.h"
#include "std_msgs/Float32.h"
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <iostream>
class SubPuber
{
private:
ros::NodeHandle nodeHandle;
ros::Subscriber cameraImageSub;
ros::Publisher recognitionResultPub;
ros::Publisher recognitionConfidencePub;
ros::Publisher recognizedImagePub;
pr::PipelinePR prc;
public:
// 模型路径从launch文件中获得用于寻找模型
std::string modelPath;
void SendPath();
SubPuber(){
// 获取相机话题
std::string cameraTopic;
ros::param::get("CameraTopic", cameraTopic);
// 订阅相机话题此处默认为realsense彩色图像的topic
cameraImageSub = nodeHandle.subscribe(cameraTopic, 1, &SubPuber::PlateRecognitionCallback, this);
// 发布识别结果
recognitionResultPub = nodeHandle.advertise<std_msgs::String>("/recognition_result", 1);
// 发布识别置信度
recognitionConfidencePub = nodeHandle.advertise<std_msgs::Float32>("/recognition_confidence", 1);
// 发布框选出车牌后的图像
recognizedImagePub = nodeHandle.advertise<sensor_msgs::Image>("/recognized_image", 1);
}
void PlateRecognitionCallback(const sensor_msgs::ImageConstPtr &cameraImage);
};
#endif

@ -0,0 +1,107 @@
//
// Created by Jack Yu on 26/10/2017.
//
#ifndef HYPERPR_NIBLACKTHRESHOLD_H
#define HYPERPR_NIBLACKTHRESHOLD_H
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
using namespace cv;
enum LocalBinarizationMethods {
BINARIZATION_NIBLACK =
0, //!< Classic Niblack binarization. See @cite Niblack1985 .
BINARIZATION_SAUVOLA = 1, //!< Sauvola's technique. See @cite Sauvola1997 .
BINARIZATION_WOLF = 2, //!< Wolf's technique. See @cite Wolf2004 .
BINARIZATION_NICK = 3 //!< NICK technique. See @cite Khurshid2009 .
};
void niBlackThreshold(InputArray _src, OutputArray _dst, double maxValue,
int type, int blockSize, double k,
int binarizationMethod) {
// Input grayscale image
Mat src = _src.getMat();
CV_Assert(src.channels() == 1);
CV_Assert(blockSize % 2 == 1 && blockSize > 1);
if (binarizationMethod == BINARIZATION_SAUVOLA) {
CV_Assert(src.depth() == CV_8U);
}
type &= THRESH_MASK;
// Compute local threshold (T = mean + k * stddev)
// using mean and standard deviation in the neighborhood of each pixel
// (intermediate calculations are done with floating-point precision)
Mat test;
Mat thresh;
{
// note that: Var[X] = E[X^2] - E[X]^2
Mat mean, sqmean, variance, stddev, sqrtVarianceMeanSum;
double srcMin, stddevMax;
boxFilter(src, mean, CV_32F, Size(blockSize, blockSize), Point(-1, -1),
true, BORDER_REPLICATE);
sqrBoxFilter(src, sqmean, CV_32F, Size(blockSize, blockSize), Point(-1, -1),
true, BORDER_REPLICATE);
variance = sqmean - mean.mul(mean);
sqrt(variance, stddev);
switch (binarizationMethod) {
case BINARIZATION_NIBLACK:
thresh = mean + stddev * static_cast<float>(k);
break;
case BINARIZATION_SAUVOLA:
thresh = mean.mul(1. + static_cast<float>(k) * (stddev / 128.0 - 1.));
break;
case BINARIZATION_WOLF:
minMaxIdx(src, &srcMin, NULL);
minMaxIdx(stddev, NULL, &stddevMax);
thresh =
mean - static_cast<float>(k) *
(mean - srcMin - stddev.mul(mean - srcMin) / stddevMax);
break;
case BINARIZATION_NICK:
sqrt(variance + sqmean, sqrtVarianceMeanSum);
thresh = mean + static_cast<float>(k) * sqrtVarianceMeanSum;
break;
default:
CV_Error(CV_StsBadArg, "Unknown binarization method");
break;
}
thresh.convertTo(thresh, src.depth());
thresh.convertTo(test, src.depth());
//
// cv::imshow("imagex",test);
// cv::waitKey(0);
}
// Prepare output image
_dst.create(src.size(), src.type());
Mat dst = _dst.getMat();
CV_Assert(src.data != dst.data); // no inplace processing
// Apply thresholding: ( pixel > threshold ) ? foreground : background
Mat mask;
switch (type) {
case THRESH_BINARY: // dst = (src > thresh) ? maxval : 0
case THRESH_BINARY_INV: // dst = (src > thresh) ? 0 : maxval
compare(src, thresh, mask, (type == THRESH_BINARY ? CMP_GT : CMP_LE));
dst.setTo(0);
dst.setTo(maxValue, mask);
break;
case THRESH_TRUNC: // dst = (src > thresh) ? thresh : src
compare(src, thresh, mask, CMP_GT);
src.copyTo(dst);
thresh.copyTo(dst, mask);
break;
case THRESH_TOZERO: // dst = (src > thresh) ? src : 0
case THRESH_TOZERO_INV: // dst = (src > thresh) ? 0 : src
compare(src, thresh, mask, (type == THRESH_TOZERO ? CMP_GT : CMP_LE));
dst.setTo(0);
src.copyTo(dst, mask);
break;
default:
CV_Error(CV_StsBadArg, "Unknown threshold type");
break;
}
}
#endif // HYPERPR_NIBLACKTHRESHOLD_H

@ -0,0 +1,7 @@
<launch>
<param name="ModelPath" value="$(find hyperlpr)/model" />
<param name="CameraTopic" value="/camera/color/image_raw" />
<node pkg="hyperlpr" type="hyperlpr_node" name="hyperlpr_node" output="screen"/>
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find hyperlpr)/launch/hyperlpr.rviz" />
</launch>

@ -0,0 +1,133 @@
Panels:
- Class: rviz/Displays
Help Height: 138
Name: Displays
Property Tree Widget:
Expanded:
- /Global Options1
- /Status1
Splitter Ratio: 0.5
Tree Height: 1090
- Class: rviz/Selection
Name: Selection
- Class: rviz/Tool Properties
Expanded:
- /2D Pose Estimate1
- /2D Nav Goal1
- /Publish Point1
Name: Tool Properties
Splitter Ratio: 0.5886790156364441
- Class: rviz/Views
Expanded:
- /Current View1
Name: Views
Splitter Ratio: 0.5
- Class: rviz/Time
Name: Time
SyncMode: 0
SyncSource: Image
Preferences:
PromptSaveOnExit: true
Toolbars:
toolButtonStyle: 2
Visualization Manager:
Class: ""
Displays:
- Alpha: 0.5
Cell Size: 1
Class: rviz/Grid
Color: 160; 160; 164
Enabled: true
Line Style:
Line Width: 0.029999999329447746
Value: Lines
Name: Grid
Normal Cell Count: 0
Offset:
X: 0
Y: 0
Z: 0
Plane: XY
Plane Cell Count: 10
Reference Frame: <Fixed Frame>
Value: true
- Class: rviz/Image
Enabled: true
Image Topic: /recognized_image
Max Value: 1
Median window: 5
Min Value: 0
Name: Image
Normalize Range: true
Queue Size: 2
Transport Hint: raw
Unreliable: false
Value: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
Default Light: true
Fixed Frame: map
Frame Rate: 30
Name: root
Tools:
- Class: rviz/Interact
Hide Inactive Objects: true
- Class: rviz/MoveCamera
- Class: rviz/Select
- Class: rviz/FocusCamera
- Class: rviz/Measure
- Class: rviz/SetInitialPose
Theta std deviation: 0.2617993950843811
Topic: /initialpose
X std deviation: 0.5
Y std deviation: 0.5
- Class: rviz/SetGoal
Topic: /move_base_simple/goal
- Class: rviz/PublishPoint
Single click: true
Topic: /clicked_point
Value: true
Views:
Current:
Class: rviz/Orbit
Distance: 10
Enable Stereo Rendering:
Stereo Eye Separation: 0.05999999865889549
Stereo Focal Distance: 1
Swap Stereo Eyes: false
Value: false
Field of View: 0.7853981852531433
Focal Point:
X: 0
Y: 0
Z: 0
Focal Shape Fixed Size: true
Focal Shape Size: 0.05000000074505806
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.009999999776482582
Pitch: 0.785398006439209
Target Frame: <Fixed Frame>
Yaw: 0.785398006439209
Saved: ~
Window Geometry:
Displays:
collapsed: false
Height: 1600
Hide Left Dock: false
Hide Right Dock: false
Image:
collapsed: false
QMainWindow State: 000000ff00000000fd0000000400000000000001f70000053afc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b000000b000fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000b0fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000006e0000053a0000018200fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c0000026100000001000009d10000053afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fc0000006e0000053a0000016c01000039fa000000000100000002fb0000000a0049006d0061006700650100000000ffffffff0000009100fffffffb0000000a0056006900650077007301000012110000015f0000015f00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e1000001970000000300000c000000005efc0100000002fb0000000800540069006d0065010000000000000c00000006dc00fffffffb0000000800540069006d00650100000000000004500000000000000000000000200000053a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Time:
collapsed: false
Tool Properties:
collapsed: false
Views:
collapsed: false
Width: 3072
X: 904
Y: 2934

@ -0,0 +1,123 @@
input: "data"
input_dim: 1
input_dim: 1
input_dim: 30
input_dim: 14
layer {
name: "conv2d_1"
type: "Convolution"
bottom: "data"
top: "conv2d_1"
convolution_param {
num_output: 32
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "activation_1"
type: "ReLU"
bottom: "conv2d_1"
top: "activation_1"
}
layer {
name: "max_pooling2d_1"
type: "Pooling"
bottom: "activation_1"
top: "max_pooling2d_1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
pad: 0
}
}
layer {
name: "conv2d_2"
type: "Convolution"
bottom: "max_pooling2d_1"
top: "conv2d_2"
convolution_param {
num_output: 64
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "activation_2"
type: "ReLU"
bottom: "conv2d_2"
top: "activation_2"
}
layer {
name: "max_pooling2d_2"
type: "Pooling"
bottom: "activation_2"
top: "max_pooling2d_2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
pad: 0
}
}
layer {
name: "conv2d_3"
type: "Convolution"
bottom: "max_pooling2d_2"
top: "conv2d_3"
convolution_param {
num_output: 128
bias_term: true
pad: 0
kernel_size: 2
stride: 1
}
}
layer {
name: "activation_3"
type: "ReLU"
bottom: "conv2d_3"
top: "activation_3"
}
layer {
name: "flatten_1"
type: "Flatten"
bottom: "activation_3"
top: "flatten_1"
}
layer {
name: "dense_1"
type: "InnerProduct"
bottom: "flatten_1"
top: "dense_1"
inner_product_param {
num_output: 256
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "dense_1"
top: "relu2"
}
layer {
name: "dense2"
type: "InnerProduct"
bottom: "relu2"
top: "dense2"
inner_product_param {
num_output: 65
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "dense2"
top: "prob"
}

@ -0,0 +1,95 @@
input: "data"
input_dim: 1
input_dim: 3
input_dim: 16
input_dim: 66
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
convolution_param {
num_output: 10
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer {
name: "max_pooling2d_3"
type: "Pooling"
bottom: "conv1"
top: "max_pooling2d_3"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
pad: 0
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "max_pooling2d_3"
top: "conv2"
convolution_param {
num_output: 16
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "conv3"
type: "Convolution"
bottom: "conv2"
top: "conv3"
convolution_param {
num_output: 32
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "flatten_2"
type: "Flatten"
bottom: "conv3"
top: "flatten_2"
}
layer {
name: "dense"
type: "InnerProduct"
bottom: "flatten_2"
top: "dense"
inner_product_param {
num_output: 2
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "dense"
top: "dense"
}

@ -0,0 +1,454 @@
input: "data"
input_dim: 1
input_dim: 3
input_dim: 160
input_dim: 40
layer {
name: "conv0"
type: "Convolution"
bottom: "data"
top: "conv0"
convolution_param {
num_output: 32
bias_term: true
pad_h: 1
pad_w: 1
kernel_h: 3
kernel_w: 3
stride_h: 1
stride_w: 1
}
}
layer {
name: "bn0"
type: "BatchNorm"
bottom: "conv0"
top: "bn0"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "bn0_scale"
type: "Scale"
bottom: "bn0"
top: "bn0"
scale_param {
bias_term: true
}
}
layer {
name: "relu0"
type: "ReLU"
bottom: "bn0"
top: "bn0"
}
layer {
name: "pool0"
type: "Pooling"
bottom: "bn0"
top: "pool0"
pooling_param {
pool: MAX
kernel_h: 2
kernel_w: 2
stride_h: 2
stride_w: 2
pad_h: 0
pad_w: 0
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "pool0"
top: "conv1"
convolution_param {
num_output: 64
bias_term: true
pad_h: 1
pad_w: 1
kernel_h: 3
kernel_w: 3
stride_h: 1
stride_w: 1
}
}
layer {
name: "bn1"
type: "BatchNorm"
bottom: "conv1"
top: "bn1"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "bn1_scale"
type: "Scale"
bottom: "bn1"
top: "bn1"
scale_param {
bias_term: true
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "bn1"
top: "bn1"
}
layer {
name: "pool1"
type: "Pooling"
bottom: "bn1"
top: "pool1"
pooling_param {
pool: MAX
kernel_h: 2
kernel_w: 2
stride_h: 2
stride_w: 2
pad_h: 0
pad_w: 0
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
convolution_param {
num_output: 128
bias_term: true
pad_h: 1
pad_w: 1
kernel_h: 3
kernel_w: 3
stride_h: 1
stride_w: 1
}
}
layer {
name: "bn2"
type: "BatchNorm"
bottom: "conv2"
top: "bn2"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "bn2_scale"
type: "Scale"
bottom: "bn2"
top: "bn2"
scale_param {
bias_term: true
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "bn2"
top: "bn2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "bn2"
top: "pool2"
pooling_param {
pool: MAX
kernel_h: 2
kernel_w: 2
stride_h: 2
stride_w: 2
pad_h: 0
pad_w: 0
}
}
layer {
name: "conv2d_1"
type: "Convolution"
bottom: "pool2"
top: "conv2d_1"
convolution_param {
num_output: 256
bias_term: true
pad_h: 0
pad_w: 0
kernel_h: 1
kernel_w: 5
stride_h: 1
stride_w: 1
}
}
layer {
name: "batch_normalization_1"
type: "BatchNorm"
bottom: "conv2d_1"
top: "batch_normalization_1"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "batch_normalization_1_scale"
type: "Scale"
bottom: "batch_normalization_1"
top: "batch_normalization_1"
scale_param {
bias_term: true
}
}
layer {
name: "activation_1"
type: "ReLU"
bottom: "batch_normalization_1"
top: "batch_normalization_1"
}
layer {
name: "conv2d_2"
type: "Convolution"
bottom: "batch_normalization_1"
top: "conv2d_2"
convolution_param {
num_output: 256
bias_term: true
pad_h: 3
pad_w: 0
kernel_h: 7
kernel_w: 1
stride_h: 1
stride_w: 1
}
}
layer {
name: "conv2d_3"
type: "Convolution"
bottom: "batch_normalization_1"
top: "conv2d_3"
convolution_param {
num_output: 256
bias_term: true
pad_h: 2
pad_w: 0
kernel_h: 5
kernel_w: 1
stride_h: 1
stride_w: 1
}
}
layer {
name: "conv2d_4"
type: "Convolution"
bottom: "batch_normalization_1"
top: "conv2d_4"
convolution_param {
num_output: 256
bias_term: true
pad_h: 1
pad_w: 0
kernel_h: 3
kernel_w: 1
stride_h: 1
stride_w: 1
}
}
layer {
name: "conv2d_5"
type: "Convolution"
bottom: "batch_normalization_1"
top: "conv2d_5"
convolution_param {
num_output: 256
bias_term: true
pad_h: 0
pad_w: 0
kernel_h: 1
kernel_w: 1
stride_h: 1
stride_w: 1
}
}
layer {
name: "batch_normalization_2"
type: "BatchNorm"
bottom: "conv2d_2"
top: "batch_normalization_2"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "batch_normalization_2_scale"
type: "Scale"
bottom: "batch_normalization_2"
top: "batch_normalization_2"
scale_param {
bias_term: true
}
}
layer {
name: "batch_normalization_3"
type: "BatchNorm"
bottom: "conv2d_3"
top: "batch_normalization_3"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "batch_normalization_3_scale"
type: "Scale"
bottom: "batch_normalization_3"
top: "batch_normalization_3"
scale_param {
bias_term: true
}
}
layer {
name: "batch_normalization_4"
type: "BatchNorm"
bottom: "conv2d_4"
top: "batch_normalization_4"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "batch_normalization_4_scale"
type: "Scale"
bottom: "batch_normalization_4"
top: "batch_normalization_4"
scale_param {
bias_term: true
}
}
layer {
name: "batch_normalization_5"
type: "BatchNorm"
bottom: "conv2d_5"
top: "batch_normalization_5"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "batch_normalization_5_scale"
type: "Scale"
bottom: "batch_normalization_5"
top: "batch_normalization_5"
scale_param {
bias_term: true
}
}
layer {
name: "activation_2"
type: "ReLU"
bottom: "batch_normalization_2"
top: "batch_normalization_2"
}
layer {
name: "activation_3"
type: "ReLU"
bottom: "batch_normalization_3"
top: "batch_normalization_3"
}
layer {
name: "activation_4"
type: "ReLU"
bottom: "batch_normalization_4"
top: "batch_normalization_4"
}
layer {
name: "activation_5"
type: "ReLU"
bottom: "batch_normalization_5"
top: "batch_normalization_5"
}
layer {
name: "concatenate_1"
type: "Concat"
bottom: "batch_normalization_2"
bottom: "batch_normalization_3"
bottom: "batch_normalization_4"
bottom: "batch_normalization_5"
top: "concatenate_1"
concat_param {
axis: 1
}
}
layer {
name: "conv_1024_11"
type: "Convolution"
bottom: "concatenate_1"
top: "conv_1024_11"
convolution_param {
num_output: 1024
bias_term: true
pad_h: 0
pad_w: 0
kernel_h: 1
kernel_w: 1
stride_h: 1
stride_w: 1
}
}
layer {
name: "batch_normalization_6"
type: "BatchNorm"
bottom: "conv_1024_11"
top: "batch_normalization_6"
batch_norm_param {
moving_average_fraction: 0.99
eps: 0.001
}
}
layer {
name: "batch_normalization_6_scale"
type: "Scale"
bottom: "batch_normalization_6"
top: "batch_normalization_6"
scale_param {
bias_term: true
}
}
layer {
name: "activation_6"
type: "ReLU"
bottom: "batch_normalization_6"
top: "batch_normalization_6"
}
layer {
name: "conv_class_11"
type: "Convolution"
bottom: "batch_normalization_6"
top: "conv_class_11"
convolution_param {
num_output: 84
bias_term: true
pad_h: 0
pad_w: 0
kernel_h: 1
kernel_w: 1
stride_h: 1
stride_w: 1
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "conv_class_11"
top: "prob"
}

@ -0,0 +1,114 @@
input: "data"
input_dim: 1
input_dim: 1
input_dim: 22
input_dim: 22
layer {
name: "conv2d_12"
type: "Convolution"
bottom: "data"
top: "conv2d_12"
convolution_param {
num_output: 16
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "activation_18"
type: "ReLU"
bottom: "conv2d_12"
top: "activation_18"
}
layer {
name: "max_pooling2d_10"
type: "Pooling"
bottom: "activation_18"
top: "max_pooling2d_10"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
pad: 0
}
}
layer {
name: "conv2d_13"
type: "Convolution"
bottom: "max_pooling2d_10"
top: "conv2d_13"
convolution_param {
num_output: 16
bias_term: true
pad: 0
kernel_size: 3
stride: 1
}
}
layer {
name: "activation_19"
type: "ReLU"
bottom: "conv2d_13"
top: "activation_19"
}
layer {
name: "max_pooling2d_11"
type: "Pooling"
bottom: "activation_19"
top: "max_pooling2d_11"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
pad: 0
}
}
layer {
name: "flatten_6"
type: "Flatten"
bottom: "max_pooling2d_11"
top: "flatten_6"
}
layer {
name: "dense_9"
type: "InnerProduct"
bottom: "flatten_6"
top: "dense_9"
inner_product_param {
num_output: 256
}
}
layer {
name: "dropout_9"
type: "Dropout"
bottom: "dense_9"
top: "dropout_9"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "activation_20"
type: "ReLU"
bottom: "dropout_9"
top: "activation_20"
}
layer {
name: "dense_10"
type: "InnerProduct"
bottom: "activation_20"
top: "dense_10"
inner_product_param {
num_output: 3
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "dense_10"
top: "prob"
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,58 @@
<?xml version="1.0"?>
<package format="2">
<name>hyperlpr</name>
<version>0.0.0</version>
<description>The hyperlpr package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="bssn@todo.todo">bssn</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>TODO</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/hyperlpr</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>

@ -0,0 +1,21 @@
//
// Created by Jack Yu on 21/10/2017.
//
#include "../include/CNNRecognizer.h"
namespace pr {
CNNRecognizer::CNNRecognizer(std::string prototxt, std::string caffemodel) {
net = cv::dnn::readNetFromCaffe(prototxt, caffemodel);
}
label CNNRecognizer::recognizeCharacter(cv::Mat charImage) {
if (charImage.channels() == 3)
cv::cvtColor(charImage, charImage, cv::COLOR_BGR2GRAY);
cv::Mat inputBlob = cv::dnn::blobFromImage(
charImage, 1 / 255.0, cv::Size(CHAR_INPUT_W, CHAR_INPUT_H),
cv::Scalar(0, 0, 0), false);
net.setInput(inputBlob, "data");
return net.forward();
}
} // namespace pr

@ -0,0 +1,104 @@
//
// Created by Jack Yu on 02/10/2017.
//
#include <../include/FastDeskew.h>
namespace pr {
const int ANGLE_MIN = 30;
const int ANGLE_MAX = 150;
const int PLATE_H = 36;
const int PLATE_W = 136;
int angle(float x, float y) { return atan2(x, y) * 180 / 3.1415; }
std::vector<float> avgfilter(std::vector<float> angle_list, int windowsSize) {
std::vector<float> angle_list_filtered(angle_list.size() - windowsSize + 1);
for (int i = 0; i < angle_list.size() - windowsSize + 1; i++) {
float avg = 0.00f;
for (int j = 0; j < windowsSize; j++) {
avg += angle_list[i + j];
}
avg = avg / windowsSize;
angle_list_filtered[i] = avg;
}
return angle_list_filtered;
}
void drawHist(std::vector<float> seq) {
cv::Mat image(300, seq.size(), CV_8U);
image.setTo(0);
for (int i = 0; i < seq.size(); i++) {
float l = *std::max_element(seq.begin(), seq.end());
int p = int(float(seq[i]) / l * 300);
cv::line(image, cv::Point(i, 300), cv::Point(i, 300 - p),
cv::Scalar(255, 255, 255));
}
cv::imshow("vis", image);
}
cv::Mat correctPlateImage(cv::Mat skewPlate, float angle, float maxAngle) {
cv::Mat dst;
cv::Size size_o(skewPlate.cols, skewPlate.rows);
int extend_padding = 0;
extend_padding =
static_cast<int>(skewPlate.rows * tan(cv::abs(angle) / 180 * 3.14));
cv::Size size(skewPlate.cols + extend_padding, skewPlate.rows);
float interval = abs(sin((angle / 180) * 3.14) * skewPlate.rows);
cv::Point2f pts1[4] = {cv::Point2f(0, 0), cv::Point2f(0, size_o.height),
cv::Point2f(size_o.width, 0),
cv::Point2f(size_o.width, size_o.height)};
if (angle > 0) {
cv::Point2f pts2[4] = {cv::Point2f(interval, 0),
cv::Point2f(0, size_o.height),
cv::Point2f(size_o.width, 0),
cv::Point2f(size_o.width - interval, size_o.height)};
cv::Mat M = cv::getPerspectiveTransform(pts1, pts2);
cv::warpPerspective(skewPlate, dst, M, size);
} else {
cv::Point2f pts2[4] = {cv::Point2f(0, 0),
cv::Point2f(interval, size_o.height),
cv::Point2f(size_o.width - interval, 0),
cv::Point2f(size_o.width, size_o.height)};
cv::Mat M = cv::getPerspectiveTransform(pts1, pts2);
cv::warpPerspective(skewPlate, dst, M, size, cv::INTER_CUBIC);
}
return dst;
}
cv::Mat fastdeskew(cv::Mat skewImage, int blockSize) {
const int FILTER_WINDOWS_SIZE = 5;
std::vector<float> angle_list(180);
memset(angle_list.data(), 0, angle_list.size() * sizeof(int));
cv::Mat bak;
skewImage.copyTo(bak);
if (skewImage.channels() == 3)
cv::cvtColor(skewImage, skewImage, cv::COLOR_RGB2GRAY);
if (skewImage.channels() == 1) {
cv::Mat eigen;
cv::cornerEigenValsAndVecs(skewImage, eigen, blockSize, 5);
for (int j = 0; j < skewImage.rows; j += blockSize) {
for (int i = 0; i < skewImage.cols; i += blockSize) {
float x2 = eigen.at<cv::Vec6f>(j, i)[4];
float y2 = eigen.at<cv::Vec6f>(j, i)[5];
int angle_cell = angle(x2, y2);
angle_list[(angle_cell + 180) % 180] += 1.0;
}
}
}
std::vector<float> filtered = avgfilter(angle_list, 5);
int maxPos = std::max_element(filtered.begin(), filtered.end()) -
filtered.begin() + FILTER_WINDOWS_SIZE / 2;
if (maxPos > ANGLE_MAX)
maxPos = (-maxPos + 90 + 180) % 180;
if (maxPos < ANGLE_MIN)
maxPos -= 90;
maxPos = 90 - maxPos;
cv::Mat deskewed = correctPlateImage(bak, static_cast<float>(maxPos), 60.0f);
return deskewed;
}
} // namespace pr

@ -0,0 +1,165 @@
#include "FineMapping.h"
namespace pr {
const int FINEMAPPING_H = 60;
const int FINEMAPPING_W = 140;
const int PADDING_UP_DOWN = 30;
void drawRect(cv::Mat image, cv::Rect rect) {
cv::Point p1(rect.x, rect.y);
cv::Point p2(rect.x + rect.width, rect.y + rect.height);
cv::rectangle(image, p1, p2, cv::Scalar(0, 255, 0), 1);
}
FineMapping::FineMapping(std::string prototxt, std::string caffemodel) {
net = cv::dnn::readNetFromCaffe(prototxt, caffemodel);
}
cv::Mat FineMapping::FineMappingHorizon(cv::Mat FinedVertical, int leftPadding,
int rightPadding) {
cv::Mat inputBlob = cv::dnn::blobFromImage(
FinedVertical, 1 / 255.0, cv::Size(66, 16), cv::Scalar(0, 0, 0), false);
net.setInput(inputBlob, "data");
cv::Mat prob = net.forward();
int front = static_cast<int>(prob.at<float>(0, 0) * FinedVertical.cols);
int back = static_cast<int>(prob.at<float>(0, 1) * FinedVertical.cols);
front -= leftPadding;
if (front < 0)
front = 0;
back += rightPadding;
if (back > FinedVertical.cols - 1)
back = FinedVertical.cols - 1;
cv::Mat cropped = FinedVertical.colRange(front, back).clone();
return cropped;
}
std::pair<int, int> FitLineRansac(std::vector<cv::Point> pts, int zeroadd = 0) {
std::pair<int, int> res;
if (pts.size() > 2) {
cv::Vec4f line;
cv::fitLine(pts, line, cv::DIST_HUBER, 0, 0.01, 0.01);
float vx = line[0];
float vy = line[1];
float x = line[2];
float y = line[3];
int lefty = static_cast<int>((-x * vy / vx) + y);
int righty = static_cast<int>(((136 - x) * vy / vx) + y);
res.first = lefty + PADDING_UP_DOWN + zeroadd;
res.second = righty + PADDING_UP_DOWN + zeroadd;
return res;
}
res.first = zeroadd;
res.second = zeroadd;
return res;
}
cv::Mat FineMapping::FineMappingVertical(cv::Mat InputProposal, int sliceNum,
int upper, int lower,
int windows_size) {
cv::Mat PreInputProposal;
cv::Mat proposal;
cv::resize(InputProposal, PreInputProposal,
cv::Size(FINEMAPPING_W, FINEMAPPING_H));
if (InputProposal.channels() == 3)
cv::cvtColor(PreInputProposal, proposal, cv::COLOR_BGR2GRAY);
else
PreInputProposal.copyTo(proposal);
// this will improve some sen
cv::Mat kernal = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(1, 3));
float diff = static_cast<float>(upper - lower);
diff /= static_cast<float>(sliceNum - 1);
cv::Mat binary_adaptive;
std::vector<cv::Point> line_upper;
std::vector<cv::Point> line_lower;
int contours_nums = 0;
for (int i = 0; i < sliceNum; i++) {
std::vector<std::vector<cv::Point>> contours;
float k = lower + i * diff;
cv::adaptiveThreshold(proposal, binary_adaptive, 255,
cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY,
windows_size, k);
cv::Mat draw;
binary_adaptive.copyTo(draw);
cv::findContours(binary_adaptive, contours, cv::RETR_EXTERNAL,
cv::CHAIN_APPROX_SIMPLE);
for (auto contour : contours) {
cv::Rect bdbox = cv::boundingRect(contour);
float lwRatio = bdbox.height / static_cast<float>(bdbox.width);
int bdboxAera = bdbox.width * bdbox.height;
if ((lwRatio > 0.7 && bdbox.width * bdbox.height > 100 &&
bdboxAera < 300) ||
(lwRatio > 3.0 && bdboxAera < 100 && bdboxAera > 10)) {
cv::Point p1(bdbox.x, bdbox.y);
cv::Point p2(bdbox.x + bdbox.width, bdbox.y + bdbox.height);
line_upper.push_back(p1);
line_lower.push_back(p2);
contours_nums += 1;
}
}
}
if (contours_nums < 41) {
cv::bitwise_not(InputProposal, InputProposal);
cv::Mat kernal =
cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(1, 5));
cv::Mat bak;
cv::resize(InputProposal, bak, cv::Size(FINEMAPPING_W, FINEMAPPING_H));
cv::erode(bak, bak, kernal);
if (InputProposal.channels() == 3)
cv::cvtColor(bak, proposal, cv::COLOR_BGR2GRAY);
else
proposal = bak;
int contours_nums = 0;
for (int i = 0; i < sliceNum; i++) {
std::vector<std::vector<cv::Point>> contours;
float k = lower + i * diff;
cv::adaptiveThreshold(proposal, binary_adaptive, 255,
cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY,
windows_size, k);
cv::Mat draw;
binary_adaptive.copyTo(draw);
cv::findContours(binary_adaptive, contours, cv::RETR_EXTERNAL,
cv::CHAIN_APPROX_SIMPLE);
for (auto contour : contours) {
cv::Rect bdbox = cv::boundingRect(contour);
float lwRatio = bdbox.height / static_cast<float>(bdbox.width);
int bdboxAera = bdbox.width * bdbox.height;
if ((lwRatio > 0.7 && bdbox.width * bdbox.height > 120 &&
bdboxAera < 300) ||
(lwRatio > 3.0 && bdboxAera < 100 && bdboxAera > 10)) {
cv::Point p1(bdbox.x, bdbox.y);
cv::Point p2(bdbox.x + bdbox.width, bdbox.y + bdbox.height);
line_upper.push_back(p1);
line_lower.push_back(p2);
contours_nums += 1;
}
}
}
}
cv::Mat rgb;
cv::copyMakeBorder(PreInputProposal, rgb, PADDING_UP_DOWN, PADDING_UP_DOWN, 0,
0, cv::BORDER_REPLICATE);
std::pair<int, int> A;
std::pair<int, int> B;
A = FitLineRansac(line_upper, -1);
B = FitLineRansac(line_lower, 1);
int leftyB = A.first;
int rightyB = A.second;
int leftyA = B.first;
int rightyA = B.second;
int cols = rgb.cols;
int rows = rgb.rows;
std::vector<cv::Point2f> corners(4);
corners[0] = cv::Point2f(cols - 1, rightyA);
corners[1] = cv::Point2f(0, leftyA);
corners[2] = cv::Point2f(cols - 1, rightyB);
corners[3] = cv::Point2f(0, leftyB);
std::vector<cv::Point2f> corners_trans(4);
corners_trans[0] = cv::Point2f(136, 36);
corners_trans[1] = cv::Point2f(0, 36);
corners_trans[2] = cv::Point2f(136, 0);
corners_trans[3] = cv::Point2f(0, 0);
cv::Mat transform = cv::getPerspectiveTransform(corners, corners_trans);
cv::Mat quad = cv::Mat::zeros(36, 136, CV_8UC3);
cv::warpPerspective(rgb, quad, transform, quad.size());
return quad;
}
} // namespace pr

@ -0,0 +1,82 @@
//
// Created by Jack Yu on 23/10/2017.
//
#include "Pipeline.h"
namespace pr {
const int HorizontalPadding = 4;
PipelinePR::PipelinePR(){};
PipelinePR::~PipelinePR() {
delete plateDetection;
delete fineMapping;
delete plateSegmentation;
delete generalRecognizer;
delete segmentationFreeRecognizer;
}
void PipelinePR::initialize(std::string detector_filename,
std::string finemapping_prototxt,
std::string finemapping_caffemodel,
std::string segmentation_prototxt,
std::string segmentation_caffemodel,
std::string charRecognization_proto,
std::string charRecognization_caffemodel,
std::string segmentationfree_proto,
std::string segmentationfree_caffemodel) {
plateDetection = new PlateDetection(detector_filename);
fineMapping = new FineMapping(finemapping_prototxt, finemapping_caffemodel);
plateSegmentation =
new PlateSegmentation(segmentation_prototxt, segmentation_caffemodel);
generalRecognizer =
new CNNRecognizer(charRecognization_proto, charRecognization_caffemodel);
segmentationFreeRecognizer = new SegmentationFreeRecognizer(
segmentationfree_proto, segmentationfree_caffemodel);
}
std::vector<PlateInfo> PipelinePR::RunPiplineAsImage(cv::Mat plateImage,
int method) {
std::vector<PlateInfo> results;
std::vector<pr::PlateInfo> plates;
plateDetection->plateDetectionRough(plateImage, plates, 36, 700);
for (pr::PlateInfo plateinfo : plates) {
cv::Mat image_finemapping = plateinfo.getPlateImage();
image_finemapping = fineMapping->FineMappingVertical(image_finemapping);
image_finemapping = pr::fastdeskew(image_finemapping, 5);
// Segmentation-based
if (method == SEGMENTATION_BASED_METHOD) {
image_finemapping = fineMapping->FineMappingHorizon(image_finemapping, 2,
HorizontalPadding);
cv::resize(image_finemapping, image_finemapping,
cv::Size(136 + HorizontalPadding, 36));
plateinfo.setPlateImage(image_finemapping);
std::vector<cv::Rect> rects;
plateSegmentation->segmentPlatePipline(plateinfo, 1, rects);
plateSegmentation->ExtractRegions(plateinfo, rects);
cv::copyMakeBorder(image_finemapping, image_finemapping, 0, 0, 0, 20,
cv::BORDER_REPLICATE);
plateinfo.setPlateImage(image_finemapping);
generalRecognizer->SegmentBasedSequenceRecognition(plateinfo);
plateinfo.decodePlateNormal(pr::CH_PLATE_CODE);
}
// Segmentation-free
else if (method == SEGMENTATION_FREE_METHOD) {
image_finemapping = fineMapping->FineMappingHorizon(
image_finemapping, 4, HorizontalPadding + 3);
cv::resize(image_finemapping, image_finemapping,
cv::Size(136 + HorizontalPadding, 36));
plateinfo.setPlateImage(image_finemapping);
std::pair<std::string, float> res =
segmentationFreeRecognizer->SegmentationFreeForSinglePlate(
plateinfo.getPlateImage(), pr::CH_PLATE_CODE);
plateinfo.confidence = res.second;
plateinfo.setPlateName(res.first);
}
results.push_back(plateinfo);
}
return results;
}
} // namespace pr

@ -0,0 +1,31 @@
#include "../include/PlateDetection.h"
#include "util.h"
namespace pr {
PlateDetection::PlateDetection(std::string filename_cascade) {
cascade.load(filename_cascade);
};
void PlateDetection::plateDetectionRough(cv::Mat InputImage,
std::vector<pr::PlateInfo> &plateInfos,
int min_w, int max_w) {
cv::Mat processImage;
cv::cvtColor(InputImage, processImage, cv::COLOR_BGR2GRAY);
std::vector<cv::Rect> platesRegions;
cv::Size minSize(min_w, min_w / 4);
cv::Size maxSize(max_w, max_w / 4);
cascade.detectMultiScale(processImage, platesRegions, 1.1, 3,
cv::CASCADE_SCALE_IMAGE, minSize, maxSize);
for (auto plate : platesRegions) {
int zeroadd_w = static_cast<int>(plate.width * 0.30);
int zeroadd_h = static_cast<int>(plate.height * 2);
int zeroadd_x = static_cast<int>(plate.width * 0.15);
int zeroadd_y = static_cast<int>(plate.height * 1);
plate.x -= zeroadd_x;
plate.y -= zeroadd_y;
plate.height += zeroadd_h;
plate.width += zeroadd_w;
cv::Mat plateImage = util::cropFromImage(InputImage, plate);
PlateInfo plateInfo(plateImage, plate);
plateInfos.push_back(plateInfo);
}
}
} // namespace pr

@ -0,0 +1,305 @@
//
// Created by Jack Yu on 16/10/2017.
//
#include "../include/PlateSegmentation.h"
#include "../include/niBlackThreshold.h"
namespace pr {
PlateSegmentation::PlateSegmentation(std::string prototxt,
std::string caffemodel) {
net = cv::dnn::readNetFromCaffe(prototxt, caffemodel);
}
cv::Mat PlateSegmentation::classifyResponse(const cv::Mat &cropped) {
cv::Mat inputBlob = cv::dnn::blobFromImage(
cropped, 1 / 255.0, cv::Size(22, 22), cv::Scalar(0, 0, 0), false);
net.setInput(inputBlob, "data");
return net.forward();
}
void drawHist(float *seq, int size, const char *name) {
cv::Mat image(300, size, CV_8U);
image.setTo(0);
float *start = seq;
float *end = seq + size;
float l = *std::max_element(start, end);
for (int i = 0; i < size; i++) {
int p = int(float(seq[i]) / l * 300);
cv::line(image, cv::Point(i, 300), cv::Point(i, 300 - p),
cv::Scalar(255, 255, 255));
}
cv::resize(image, image, cv::Size(600, 100));
cv::imshow(name, image);
}
inline void computeSafeMargin(int &val, const int &rows) {
val = std::min(val, rows);
val = std::max(val, 0);
}
cv::Rect boxFromCenter(const cv::Point center, int left, int right, int top,
int bottom, cv::Size bdSize) {
cv::Point p1(center.x - left, center.y - top);
cv::Point p2(center.x + right, center.y + bottom);
p1.x = std::max(0, p1.x);
p1.y = std::max(0, p1.y);
p2.x = std::min(p2.x, bdSize.width - 1);
p2.y = std::min(p2.y, bdSize.height - 1);
cv::Rect rect(p1, p2);
return rect;
}
cv::Rect boxPadding(cv::Rect rect, int left, int right, int top, int bottom,
cv::Size bdSize) {
cv::Point center(rect.x + (rect.width >> 1), rect.y + (rect.height >> 1));
int rebuildLeft = (rect.width >> 1) + left;
int rebuildRight = (rect.width >> 1) + right;
int rebuildTop = (rect.height >> 1) + top;
int rebuildBottom = (rect.height >> 1) + bottom;
return boxFromCenter(center, rebuildLeft, rebuildRight, rebuildTop,
rebuildBottom, bdSize);
}
void PlateSegmentation::refineRegion(cv::Mat &plateImage,
const std::vector<int> &candidatePts,
const int padding,
std::vector<cv::Rect> &rects) {
int w = candidatePts[5] - candidatePts[4];
int cols = plateImage.cols;
int rows = plateImage.rows;
for (int i = 0; i < candidatePts.size(); i++) {
int left = 0;
int right = 0;
if (i == 0) {
left = candidatePts[i];
right = left + w + padding;
} else {
left = candidatePts[i] - padding;
right = left + w + padding * 2;
}
computeSafeMargin(right, cols);
computeSafeMargin(left, cols);
cv::Rect roi(left, 0, right - left, rows - 1);
cv::Mat roiImage;
plateImage(roi).copyTo(roiImage);
if (i >= 1) {
cv::Mat roi_thres;
// cv::threshold(roiImage,roi_thres,0,255,cv::THRESH_OTSU|cv::THRESH_BINARY);
niBlackThreshold(roiImage, roi_thres, 255, cv::THRESH_BINARY, 15, 0.27,
BINARIZATION_NIBLACK);
std::vector<std::vector<cv::Point>> contours;
cv::findContours(roi_thres, contours, cv::RETR_LIST,
cv::CHAIN_APPROX_SIMPLE);
cv::Point boxCenter(roiImage.cols >> 1, roiImage.rows >> 1);
cv::Rect final_bdbox;
cv::Point final_center;
int final_dist = INT_MAX;
for (auto contour : contours) {
cv::Rect bdbox = cv::boundingRect(contour);
cv::Point center(bdbox.x + (bdbox.width >> 1),
bdbox.y + (bdbox.height >> 1));
int dist = (center.x - boxCenter.x) * (center.x - boxCenter.x);
if (dist < final_dist and bdbox.height > rows >> 1) {
final_dist = dist;
final_center = center;
final_bdbox = bdbox;
}
}
// rebuild box
if (final_bdbox.height / static_cast<float>(final_bdbox.width) > 3.5 &&
final_bdbox.width * final_bdbox.height < 10)
final_bdbox = boxFromCenter(final_center, 8, 8, (rows >> 1) - 3,
(rows >> 1) - 2, roiImage.size());
else {
if (i == candidatePts.size() - 1)
final_bdbox = boxPadding(final_bdbox, padding / 2, padding,
padding / 2, padding / 2, roiImage.size());
else
final_bdbox = boxPadding(final_bdbox, padding, padding, padding,
padding, roiImage.size());
// std::cout<<final_bdbox<<std::endl;
// std::cout<<roiImage.size()<<std::endl;
#ifdef DEBUG
cv::imshow("char_thres", roi_thres);
cv::imshow("char", roiImage(final_bdbox));
cv::waitKey(0);
#endif
}
final_bdbox.x += left;
rects.push_back(final_bdbox);
//
} else {
rects.push_back(roi);
}
// else
// {
//
// }
// cv::GaussianBlur(roiImage,roiImage,cv::Size(7,7),3);
//
// cv::imshow("image",roiImage);
// cv::waitKey(0);
}
}
void avgfilter(float *angle_list, int size, int windowsSize) {
float *filterd = new float[size];
for (int i = 0; i < size; i++)
filterd[i] = angle_list[i];
// memcpy(filterd,angle_list,size);
cv::Mat kernal_gaussian = cv::getGaussianKernel(windowsSize, 3, CV_32F);
float *kernal = (float *)kernal_gaussian.data;
// kernal+=windowsSize;
int r = windowsSize / 2;
for (int i = 0; i < size; i++) {
float avg = 0.00f;
for (int j = 0; j < windowsSize; j++) {
if (i + j - r > 0 && i + j + r < size - 1)
avg += filterd[i + j - r] * kernal[j];
}
// avg = avg / windowsSize;
angle_list[i] = avg;
}
delete filterd;
}
void PlateSegmentation::templateMatchFinding(
const cv::Mat &respones, int windowsWidth,
std::pair<float, std::vector<int>> &candidatePts) {
int rows = respones.rows;
int cols = respones.cols;
float *data = (float *)respones.data;
float *engNum_prob = data;
float *false_prob = data + cols;
float *ch_prob = data + cols * 2;
avgfilter(engNum_prob, cols, 5);
avgfilter(false_prob, cols, 5);
std::vector<int> candidate_pts(7);
int cp_list[7];
float loss_selected = -10;
for (int start = 0; start < 20; start += 2)
for (int width = windowsWidth - 5; width < windowsWidth + 5; width++) {
for (int interval = windowsWidth / 2; interval < windowsWidth;
interval++) {
int cp1_ch = start;
int cp2_p0 = cp1_ch + width;
int cp3_p1 = cp2_p0 + width + interval;
int cp4_p2 = cp3_p1 + width;
int cp5_p3 = cp4_p2 + width + 1;
int cp6_p4 = cp5_p3 + width + 2;
int cp7_p5 = cp6_p4 + width + 2;
int md1 = (cp1_ch + cp2_p0) >> 1;
int md2 = (cp2_p0 + cp3_p1) >> 1;
int md3 = (cp3_p1 + cp4_p2) >> 1;
int md4 = (cp4_p2 + cp5_p3) >> 1;
int md5 = (cp5_p3 + cp6_p4) >> 1;
int md6 = (cp6_p4 + cp7_p5) >> 1;
if (cp7_p5 >= cols)
continue;
float loss =
ch_prob[cp1_ch] * 3 -
(false_prob[cp3_p1] + false_prob[cp4_p2] + false_prob[cp5_p3] +
false_prob[cp6_p4] + false_prob[cp7_p5]);
if (loss > loss_selected) {
loss_selected = loss;
cp_list[0] = cp1_ch;
cp_list[1] = cp2_p0;
cp_list[2] = cp3_p1;
cp_list[3] = cp4_p2;
cp_list[4] = cp5_p3;
cp_list[5] = cp6_p4;
cp_list[6] = cp7_p5;
}
}
}
candidate_pts[0] = cp_list[0];
candidate_pts[1] = cp_list[1];
candidate_pts[2] = cp_list[2];
candidate_pts[3] = cp_list[3];
candidate_pts[4] = cp_list[4];
candidate_pts[5] = cp_list[5];
candidate_pts[6] = cp_list[6];
candidatePts.first = loss_selected;
candidatePts.second = candidate_pts;
};
void PlateSegmentation::segmentPlateBySlidingWindows(cv::Mat &plateImage,
int windowsWidth,
int stride,
cv::Mat &respones) {
cv::Mat plateImageGray;
cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
int padding = plateImage.cols - 136;
int height = plateImage.rows - 1;
int width = plateImage.cols - 1 - padding;
for (int i = 0; i < width - windowsWidth + 1; i += stride) {
cv::Rect roi(i, 0, windowsWidth, height);
cv::Mat roiImage = plateImageGray(roi);
cv::Mat response = classifyResponse(roiImage);
respones.push_back(response);
}
respones = respones.t();
}
void PlateSegmentation::segmentPlatePipline(PlateInfo &plateInfo, int stride,
std::vector<cv::Rect> &Char_rects) {
cv::Mat plateImage = plateInfo.getPlateImage(); // get src image .
cv::Mat plateImageGray;
cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
// do binarzation
std::pair<float, std::vector<int>> sections; // segment points variables .
cv::Mat respones; // three response of every sub region from origin image .
segmentPlateBySlidingWindows(plateImage, DEFAULT_WIDTH, 1, respones);
templateMatchFinding(respones, DEFAULT_WIDTH / stride, sections);
for (int i = 0; i < sections.second.size(); i++) {
sections.second[i] *= stride;
}
refineRegion(plateImageGray, sections.second, 5, Char_rects);
}
void PlateSegmentation::ExtractRegions(PlateInfo &plateInfo,
std::vector<cv::Rect> &rects) {
cv::Mat plateImage = plateInfo.getPlateImage();
for (int i = 0; i < rects.size(); i++) {
cv::Mat charImage;
plateImage(rects[i]).copyTo(charImage);
if (charImage.channels())
cv::cvtColor(charImage, charImage, cv::COLOR_BGR2GRAY);
cv::equalizeHist(charImage, charImage);
std::pair<CharType, cv::Mat> char_instance;
if (i == 0) {
char_instance.first = CHINESE;
} else if (i == 1) {
char_instance.first = LETTER;
} else {
char_instance.first = LETTER_NUMS;
}
char_instance.second = charImage;
plateInfo.appendPlateChar(char_instance);
}
}
} // namespace pr

@ -0,0 +1,22 @@
//
// Created by Jack Yu on 22/10/2017.
//
#include "../include/Recognizer.h"
namespace pr {
void GeneralRecognizer::SegmentBasedSequenceRecognition(PlateInfo &plateinfo) {
for (auto char_instance : plateinfo.plateChars) {
std::pair<CharType, cv::Mat> res;
if (char_instance.second.rows * char_instance.second.cols > 40) {
label code_table = recognizeCharacter(char_instance.second);
res.first = char_instance.first;
code_table.copyTo(res.second);
plateinfo.appendPlateCoding(res);
} else {
res.first = INVALID;
plateinfo.appendPlateCoding(res);
}
}
}
} // namespace pr

@ -0,0 +1,87 @@
//
// Created by Jack Yu on 28/11/2017.
//
#include "../include/SegmentationFreeRecognizer.h"
namespace pr {
SegmentationFreeRecognizer::SegmentationFreeRecognizer(std::string prototxt,
std::string caffemodel) {
net = cv::dnn::readNetFromCaffe(prototxt, caffemodel);
}
inline int judgeCharRange(int id) { return id < 31 || id > 63; }
std::pair<std::string, float>
decodeResults(cv::Mat code_table, std::vector<std::string> mapping_table,
float thres) {
cv::MatSize mtsize = code_table.size;
int sequencelength = mtsize[2];
int labellength = mtsize[1];
cv::transpose(code_table.reshape(1, 1).reshape(1, labellength), code_table);
std::string name = "";
std::vector<int> seq(sequencelength);
std::vector<std::pair<int, float>> seq_decode_res;
for (int i = 0; i < sequencelength; i++) {
float *fstart = ((float *)(code_table.data) + i * labellength);
int id = std::max_element(fstart, fstart + labellength) - fstart;
seq[i] = id;
}
float sum_confidence = 0;
int plate_lenghth = 0;
for (int i = 0; i < sequencelength; i++) {
if (seq[i] != labellength - 1 && (i == 0 || seq[i] != seq[i - 1])) {
float *fstart = ((float *)(code_table.data) + i * labellength);
float confidence = *(fstart + seq[i]);
std::pair<int, float> pair_(seq[i], confidence);
seq_decode_res.push_back(pair_);
}
}
int i = 0;
if (seq_decode_res.size() > 1 && judgeCharRange(seq_decode_res[0].first) &&
judgeCharRange(seq_decode_res[1].first)) {
i = 2;
int c = seq_decode_res[0].second < seq_decode_res[1].second;
name += mapping_table[seq_decode_res[c].first];
sum_confidence += seq_decode_res[c].second;
plate_lenghth++;
}
for (; i < seq_decode_res.size(); i++) {
name += mapping_table[seq_decode_res[i].first];
sum_confidence += seq_decode_res[i].second;
plate_lenghth++;
}
std::pair<std::string, float> res;
res.second = sum_confidence / plate_lenghth;
res.first = name;
return res;
}
std::string decodeResults(cv::Mat code_table,
std::vector<std::string> mapping_table) {
cv::MatSize mtsize = code_table.size;
int sequencelength = mtsize[2];
int labellength = mtsize[1];
cv::transpose(code_table.reshape(1, 1).reshape(1, labellength), code_table);
std::string name = "";
std::vector<int> seq(sequencelength);
for (int i = 0; i < sequencelength; i++) {
float *fstart = ((float *)(code_table.data) + i * labellength);
int id = std::max_element(fstart, fstart + labellength) - fstart;
seq[i] = id;
}
for (int i = 0; i < sequencelength; i++) {
if (seq[i] != labellength - 1 && (i == 0 || seq[i] != seq[i - 1]))
name += mapping_table[seq[i]];
}
return name;
}
std::pair<std::string, float>
SegmentationFreeRecognizer::SegmentationFreeForSinglePlate(
cv::Mat Image, std::vector<std::string> mapping_table) {
cv::transpose(Image, Image);
cv::Mat inputBlob =
cv::dnn::blobFromImage(Image, 1 / 255.0, cv::Size(40, 160));
net.setInput(inputBlob, "data");
cv::Mat char_prob_mat = net.forward();
return decodeResults(char_prob_mat, mapping_table, 0.00);
}
} // namespace pr

@ -0,0 +1,48 @@
#include "hyperlpr_node.h"
void SubPuber::SendPath(){
prc.initialize(modelPath+"/cascade.xml",
modelPath+"/HorizonalFinemapping.prototxt",
modelPath+"/HorizonalFinemapping.caffemodel",
modelPath+"/Segmentation.prototxt",
modelPath+"/Segmentation.caffemodel",
modelPath+"/CharacterRecognization.prototxt",
modelPath+"/CharacterRecognization.caffemodel",
modelPath+"/SegmenationFree-Inception.prototxt",
modelPath+"/SegmenationFree-Inception.caffemodel");
}
void SubPuber::PlateRecognitionCallback(const sensor_msgs::ImageConstPtr &cameraImage)
{
cv::Mat img = cv_bridge::toCvShare(cameraImage, "bgr8")->image;
//使用端到端模型模型进行识别 识别结果将会保存在res里面
std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(img, pr::SEGMENTATION_FREE_METHOD);
for(auto st:res) {
if(st.confidence>0.75) {
std_msgs::String recognitionResult;
recognitionResult.data = st.getPlateName();
std_msgs::Float32 recognitionConfidence;
recognitionConfidence.data = st.confidence;
// 使用ROS_INFO输出中文车牌与置信度
setlocale(LC_CTYPE, "zh_CN.utf8");
ROS_INFO("Plate found: %s, confidence is: %f", recognitionResult.data.c_str(), recognitionConfidence.data);
// 获取车牌位置
cv::Rect region = st.getPlateRect();
// 框选出车牌位置
cv::rectangle(img, cv::Point(region.x,region.y),cv::Point(region.x+region.width,region.y+region.height),cv::Scalar(255,255,0),2);
// 将识别结果作为一个topic发布
recognitionResultPub.publish(recognitionResult);
// 将识别置信度作为一个topic发布
recognitionConfidencePub.publish(recognitionConfidence);
}
}
// 将框选出当前帧所有车牌位置的图像作为一个topic发布
sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", img).toImageMsg();
recognizedImagePub.publish(*msg);
}

@ -0,0 +1,17 @@
#include "hyperlpr_node.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "hyperlpr_node");
// 使用一个类完成订阅 识别 发布
SubPuber plateRecognizer;
// 获取模型路径
ros::param::get("ModelPath", plateRecognizer.modelPath);
// 发送路径,初始化图像处理管线
plateRecognizer.SendPath();
ros::spin();
return 0;
}

@ -0,0 +1,64 @@
//
// Created by Jack Yu on 04/04/2017.
//
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
namespace util {
template <class T> void swap(T &a, T &b) {
T c(a);
a = b;
b = c;
}
template <class T> T min(T &a, T &b) { return a > b ? b : a; }
cv::Mat cropFromImage(const cv::Mat &image, cv::Rect rect) {
int w = image.cols - 1;
int h = image.rows - 1;
rect.x = std::max(rect.x, 0);
rect.y = std::max(rect.y, 0);
rect.height = std::min(rect.height, h - rect.y);
rect.width = std::min(rect.width, w - rect.x);
cv::Mat temp(rect.size(), image.type());
cv::Mat cropped;
temp = image(rect);
temp.copyTo(cropped);
return cropped;
}
cv::Mat cropBox2dFromImage(const cv::Mat &image, cv::RotatedRect rect) {
cv::Mat M, rotated, cropped;
float angle = rect.angle;
cv::Size rect_size(rect.size.width, rect.size.height);
if (rect.angle < -45.) {
angle += 90.0;
swap(rect_size.width, rect_size.height);
}
M = cv::getRotationMatrix2D(rect.center, angle, 1.0);
cv::warpAffine(image, rotated, M, image.size(), cv::INTER_CUBIC);
cv::getRectSubPix(rotated, rect_size, rect.center, cropped);
return cropped;
}
cv::Mat calcHist(const cv::Mat &image) {
cv::Mat hsv;
std::vector<cv::Mat> hsv_planes;
cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
cv::split(hsv, hsv_planes);
cv::Mat hist;
int histSize = 256;
float range[] = {0, 255};
const float *histRange = {range};
cv::calcHist(&hsv_planes[0], 1, 0, cv::Mat(), hist, 1, &histSize, &histRange,
true, true);
return hist;
}
float computeSimilir(const cv::Mat &A, const cv::Mat &B) {
cv::Mat histA, histB;
histA = calcHist(A);
histB = calcHist(B);
return cv::compareHist(histA, histB, CV_COMP_CORREL);
}
} // namespace util

@ -49,12 +49,15 @@ A此项目来源于作者早期的研究和调试代码代码缺少一定
- [Android配置教程](https://www.jianshu.com/p/94784c3bf2c1)
- [python配置教程](https://www.jianshu.com/p/7ab673abeaae)
- [Linux下C++配置教程](https://blog.csdn.net/lu_linux/article/details/88707421)
- [ROS包配置教程](./Prj-ROS/README.md)
- [带UI界面的工程](https://pan.baidu.com/s/1cNWpK6)(感谢群内小伙伴的工作)。
- [端到端(多标签分类)训练代码](https://github.com/LCorleone/hyperlpr-train_e2e)(感谢群内小伙伴的工作)。
- [端到端(CTC)训练代码](https://github.com/armaab/hyperlpr-train)(感谢群内小伙伴工作)。
### 更新
- [增加了ROS包](
https://github.com/BSSNBSSN/HyperLPR-ROS) (2022.09.17)
- 更新了Android实现增加实时扫描接口 (2019.07.24)
- 更新Windows版本的Visual Studio 2015 工程至端到端模型2019.07.03
- 更新基于端到端的IOS车牌识别工程。(2018.11.13)

Loading…
Cancel
Save