1. add ocr license

2. add hyper-lpr function
3. add text filter
4. add commits
This commit is contained in:
UnknownObject
2022-11-13 23:41:47 +08:00
parent 254ef1e18b
commit f9bb7d9487
116 changed files with 23682 additions and 354 deletions
@@ -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"
}
Binary file not shown.
@@ -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
+64
View File
@@ -61,6 +61,15 @@ add_library( # Sets the name of the library.
opencv_support.cpp
traffic_light.cpp)
add_library( # Sets the name of the library.
opencv_support
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
opencv_support.cpp)
add_library( # Sets the name of the library.
qr_code_decode
@@ -97,6 +106,16 @@ add_library( # Sets the name of the library.
opencv_support.cpp
car_license.cpp)
add_library( # Sets the name of the library.
car_license_reco_ocr
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
opencv_support.cpp
car_license_ocr.cpp)
add_library( # Sets the name of the library.
traffic_sign_reco
@@ -128,6 +147,24 @@ add_library( # Sets the name of the library.
# Provides a relative path to your source file(s).
main_car_aes.cpp)
add_library( # Sets the name of the library.
lib_hyper_lpr
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
opencv_support.cpp
lib_hyper_lpr/src/CNNRecognizer.cpp
lib_hyper_lpr/src/FastDeskew.cpp
lib_hyper_lpr/src/FineMapping.cpp
lib_hyper_lpr/src/Pipeline.cpp
lib_hyper_lpr/src/PlateDetection.cpp
lib_hyper_lpr/src/PlateSegmentation.cpp
lib_hyper_lpr/src/Recognizer.cpp
lib_hyper_lpr/src/SegmentationFreeRecognizer.cpp
lib_hyper_lpr/javaWarpper.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
@@ -163,6 +200,15 @@ target_link_libraries( # Specifies the target library.
${OpenCV_LIBS}
${log-lib})
target_link_libraries( # Specifies the target library.
opencv_support
${OpenCV_LIBS}
${jnigraphics-lib}
# Links the target library to the log library
# included in the NDK.
${log-lib})
target_link_libraries( # Specifies the target library.
traffic_light
@@ -199,6 +245,15 @@ target_link_libraries( # Specifies the target library.
# included in the NDK.
${log-lib})
target_link_libraries( # Specifies the target library.
car_license_reco_ocr
${OpenCV_LIBS}
${jnigraphics-lib}
# Links the target library to the log library
# included in the NDK.
${log-lib})
target_link_libraries( # Specifies the target library.
traffic_sign_reco
@@ -223,3 +278,12 @@ target_link_libraries( # Specifies the target library.
# Links the target library to the log library
# included in the NDK.
${log-lib})
target_link_libraries( # Specifies the target library.
lib_hyper_lpr
# Links the target library to the log library
# included in the NDK.
${OpenCV_LIBS}
${jnigraphics-lib}
${log-lib})
+15 -7
View File
@@ -3,11 +3,14 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//基于模板匹配的车牌识别
#include "car_license.h"
#include "debug_logger.h"
namespace uns
{
//统计有效像素点个数
int CarLicense::PixCount(cv::Mat image)
{
int count = 0;
@@ -23,6 +26,7 @@ namespace uns
return -1;
}
//获取车牌区域
bool CarLicense::Get_License_ROI(cv::Mat src, Car::License& License_ROI)
{
cv::Mat gray;
@@ -61,6 +65,7 @@ namespace uns
return true;
}
//读取存储的模板图片
bool CarLicense::Read_Data(std::string filename, std::vector<cv::Mat>& dataset)
{
dataset.clear();
@@ -69,7 +74,6 @@ namespace uns
std::string this_file = filename;
this_file.push_back(chr_str[i]);
this_file += ".jpg";
LOGW("Reading: %s", this_file.c_str());
cv::Mat image = cv::imread(this_file);
if(image.empty())
return false;
@@ -79,6 +83,7 @@ namespace uns
return true;
}
//获取字符区域
bool CarLicense::Get_Character_ROI(Car::License& License_ROI, std::vector<Car::License>& Character_ROI)
{
cv::Mat gray;
@@ -120,6 +125,7 @@ namespace uns
return true;
}
//进行模板匹配,识别
bool CarLicense::License_Recognition(std::vector<Car::License>& Character_ROI, std::vector<int>& result_index)
{
std::vector<cv::Mat> dataset;
@@ -132,7 +138,9 @@ namespace uns
cv::Mat roi_thresh;
int minCount = 1000000;
cvtColor(Character_ROI[i].mat, roi_gray, cv::COLOR_BGR2GRAY);
threshold(roi_gray, roi_thresh, 50, 255, cv::THRESH_BINARY);
// cv::imwrite("/sdcard/MainCar/gray_"+std::to_string(i)+".jpg",roi_gray);
threshold(roi_gray, roi_thresh, 100, 255, cv::THRESH_BINARY);
// cv::imwrite("/sdcard/MainCar/thresh_"+std::to_string(i)+".jpg",roi_thresh);
for (int j = 0; j < dataset.size(); j++)
{
cv::Mat dst;
@@ -140,7 +148,7 @@ namespace uns
cv::Mat temp_thresh;
cvtColor(dataset[j], temp_gray, cv::COLOR_BGR2GRAY);
threshold(temp_gray, temp_thresh, 50, 255, cv::THRESH_BINARY);
absdiff(roi_thresh, temp_thresh, dst); //计算两张图片的像素差,以此判断两张图片是否相同
absdiff(roi_thresh, temp_thresh, dst); //计算两张图片的像素差,以此判断两张图片是否相同
int count = PixCount(dst);
if (count < minCount)
{
@@ -153,6 +161,7 @@ namespace uns
return true;
}
//识别车牌,步骤:识别车牌区域->获取文字区域->模板匹配识别文字
std::string CarLicense::RecognizeCarLicense(cv::Mat img)
{
std::string result;
@@ -165,10 +174,6 @@ namespace uns
if (Get_Character_ROI(License_ROI, Character_ROI))
{
LOGI("Char_ROI OK, Size: %d", Character_ROI.size());
for(int i=0;i<Character_ROI.size();i++)
{
cv::imwrite("/sdcard/MainCar/"+std::to_string(i)+".jpg",Character_ROI[i].mat);
}
std::vector<int> result_index;
if (License_Recognition(Character_ROI, result_index))
{
@@ -188,12 +193,14 @@ namespace uns
return result;
}
//设置外部存储路径
void CarLicense::SetExternalImagePath(std::string path)
{
external_image_path = path;
}
};
//自检函数到导出
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_CarLicenseTest(JNIEnv *env, jclass _this)
{
@@ -201,6 +208,7 @@ jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_CarLicenseTest(JNIEn
return env->NewStringUTF(version.c_str());
}
//识别函数导出
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_CarLicense_RecognizeLicense(JNIEnv *env, jclass _this, jobject image, jstring external_path)
{
+2
View File
@@ -8,6 +8,8 @@
#define CAR_LICENSE_RECO_VERSION "1.0.0"
//基于模板匹配的车牌识别
#include <jni.h>
#include <iostream>
#include "public_types.h"
+135
View File
@@ -0,0 +1,135 @@
//
// Created by UnknownObject on 2022/11/12.
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//基于OCR的车牌识别
#include "car_license_ocr.h"
//切除图片上1/3(可能包含日光灯灯高亮度区域)
cv::Mat uns::CarID_OCR::CutImageHead(const cv::Mat &img)
{
cv::Rect rect(0, 100, img.size().width, img.size().height - 100);
return img(rect);
}
//切除图片边缘(除去由于轮廓外接矩形造成的额外无效内容)
cv::Rect uns::CarID_OCR::CutRectEdges(const cv::Rect &rect)
{
if (rect.size().area() <= 100)
return rect;
return cv::Rect(rect.tl().x + 10, rect.tl().y + 10, rect.size().width - 20,
rect.size().height - 10);
}
//获取图片中面积最大的轮廓的外接矩形
cv::Rect uns::CarID_OCR::GetMaxRect(const cv::Mat &img)
{
cv::Rect max_rect;
double max_rect_size = 0;
std::vector<cv::Vec4i> hierarchy;
std::vector<std::vector<cv::Point>> contours;
cv::findContours(img, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE); //轮廓查找
for (auto &contour: contours) //检测所找到的轮廓
{
double area = cv::contourArea(cv::Mat(contour));
if (area > (img.size().area() / 2.0))
continue;
if (area > max_rect_size)
{
max_rect_size = area;
max_rect = CutRectEdges(cv::boundingRect(contour));
}
}
return max_rect;
}
//根据亮度获取TFT显示器的显示屏区域
cv::Mat uns::CarID_OCR::GetScreenArea(const cv::Mat &img)
{
cv::Mat hsv;
if (img.empty())
return hsv;
cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
cv::Mat chn_v(hsv.size(), CV_8UC1);
for (int r = 0; r < hsv.rows; r++)
{
for (int c = 0; c < hsv.cols; c++)
{
uchar v = hsv.at<cv::Vec3b>(r, c)[2];
chn_v.at<uchar>(r, c) = (v >= 240 ? 0 : 255);
}
}
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(11, 11));
cv::morphologyEx(chn_v, chn_v, cv::MORPH_CLOSE, kernel);
cv::Rect max_validate_rect = GetMaxRect(chn_v);
if (max_validate_rect.size().area() == 0)
return {};
else
{
try
{
return img(max_validate_rect);
}
catch (...)
{
return {};
}
}
}
//色彩处理(灰度 + 二值化)
cv::Mat uns::CarID_OCR::ColorImprove(const cv::Mat &img)
{
cv::Mat gray;
if (img.empty())
return gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::threshold(gray, gray, 185, 255, cv::THRESH_BINARY);
return gray;
}
//整合后的图片处理类
cv::Mat uns::CarID_OCR::ProcessImage(const cv::Mat &img)
{
cv::Mat cih, gsa, ci;
cih = CutImageHead(img);
if (!cih.empty())
cv::imwrite("/sdcard/MainCar/cih.jpg", cih);
LOGI("CIH Validate");
gsa = GetScreenArea(cih);
if (!gsa.empty())
cv::imwrite("/sdcard/MainCar/gsa.jpg", gsa);
LOGI("GSA Validate");
ci = ColorImprove(gsa);
if (!ci.empty())
cv::imwrite("/sdcard/MainCar/ci.jpg", ci);
LOGI("CI Validate");
return ci;
}
//图片处理函数导出
extern "C" JNIEXPORT
jobject JNICALL
Java_com_uns_maincar_cpp_1interface_CarLicense_RecognizeLicenseOCR(JNIEnv *env, jclass _this,
jobject image)
{
cv::Mat source;
if (!BitmapToMat(env, image, source))
return nullptr;
else
{
uns::CarID_OCR car_license;
cv::Mat img = car_license.ProcessImage(source);
if (img.empty())
{
LOGI("Image Is Empty");
return nullptr;
}
jobject bmp = GenerateBitmap(env, img.cols, img.rows);
MatToBitmap(env, img, bmp);
return bmp;
}
}
+38
View File
@@ -0,0 +1,38 @@
//
// Created by UnknownObject on 2022/11/12.
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
#ifndef MAINCAR_CAR_LICENSE_OCR_H
#define MAINCAR_CAR_LICENSE_OCR_H
//基于OCR的车牌识别
#include <jni.h>
#include "debug_logger.h"
#include "opencv_support.h"
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
namespace uns
{
class CarID_OCR
{
private:
cv::Mat CutImageHead(const cv::Mat &img);
cv::Rect CutRectEdges(const cv::Rect &rect);
cv::Rect GetMaxRect(const cv::Mat &img);
cv::Mat GetScreenArea(const cv::Mat &img);
cv::Mat ColorImprove(const cv::Mat &img);
public:
cv::Mat ProcessImage(const cv::Mat &img);
};
};
#endif //MAINCAR_CAR_LICENSE_OCR_H
+5 -1
View File
@@ -3,15 +3,19 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//颜色识别
#include "color_reco.h"
namespace uns
{
//“远大于”函数
bool ColorReco::MuchLarger(int a, int b, double rate)
{
return (a >= (b * rate));
}
//识别颜色,逐像素便利并统计出现次数最多的颜色作为最终结果
std::string ColorReco::RecoColor(const cv::Mat& img)
{
int max_count = 0;
@@ -44,7 +48,7 @@ namespace uns
}
for (auto& ele : counter)
{
if ((ele.first == std_color_white) || (ele.first == std_color_yellow))
if ((ele.first == std_color_white)/* || (ele.first == std_color_yellow)*/)
continue;
if (ele.second > max_count)
{
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_COLOR_RECO_H
#define MAINCAR_COLOR_RECO_H
//颜色识别
#define COLOR_RECO_VERSION "1.0.0"
#include <map>
+3
View File
@@ -3,10 +3,13 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//调试用的日志输出
#ifndef MAINCAR_DEBUG_LOGGER_H
#define MAINCAR_DEBUG_LOGGER_H
#include <android/log.h>
#define LOG_TAG __FILE__
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+4 -2
View File
@@ -3,6 +3,8 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//NDK和OpenCVC++)的自检
#include <jni.h>
#include <iostream>
#include <opencv2/core.hpp>
@@ -10,8 +12,8 @@
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_NDKTest(JNIEnv *env, jclass _this)
{
std::string check = "C++ NDK Check: Success";
return env->NewStringUTF(check.c_str());
std::string check = "C++ NDK Check: Success";
return env->NewStringUTF(check.c_str());
}
extern "C" JNIEXPORT
+19 -4
View File
@@ -3,22 +3,26 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//图片预处理,用于形状颜色、交通标志等的图像预处理
#include "image_processor.h"
#include "debug_logger.h"
namespace uns
{
//“远大于”函数
bool ImageProcessor::MuchLarger(int a, int b, double rate)
{
return (a >= (b * rate));
}
//使用边缘检测获取包含屏幕的最大矩形
Shapes::Rectangle ImageProcessor::GetScreenRect(const cv::Mat& img)
{
int thresh = 50, N = 5;
cv::Mat dst, gray_one, gray;
std::vector<cv::Vec4i> hierarchy;
Shapes::Rectangle result{ 0,0 }, temp;
Shapes::Rectangle result{0, 0 }, temp;
std::vector<std::vector<cv::Point>> contours;
gray_one = cv::Mat(img.size(), CV_8U);
cv::medianBlur(img, dst, 9); //滤波增强边缘检测
@@ -56,6 +60,7 @@ namespace uns
return result;
}
//填充边缘(主要应对外接矩形对轮廓外的无效区域的裁切)
cv::Mat ImageProcessor::FillEdges(const cv::Mat& img, int edge_width)
{
cv::Mat result(img.size(), img.type());
@@ -65,7 +70,7 @@ namespace uns
{
if ((r <= edge_width) || (c <= edge_width))
{
result.at<cv::Vec3b>(r, c)[0] = 0;
result.at<cv::Vec3b>(r, c)[0] = 255;
result.at<cv::Vec3b>(r, c)[1] = 255;
result.at<cv::Vec3b>(r, c)[2] = 255;
}
@@ -80,6 +85,7 @@ namespace uns
return result;
}
//图像裁切
cv::Mat ImageProcessor::CutScreenImage(const cv::Mat& img, const Shapes::Rectangle& rect)
{
if(rect.area() == 0)
@@ -88,6 +94,7 @@ namespace uns
return img(sub_image);
}
//根据四个顶点计算矩形
Shapes::Rectangle ImageProcessor::CalcRectangle(const std::vector<cv::Point>& four_points)
{
int width = lround(sqrtf(powf((four_points[0].x - four_points[1].x), 2) + powf((four_points[0].y - four_points[1].y), 2)));
@@ -95,6 +102,7 @@ namespace uns
return uns::Shapes::Rectangle{ height, width, four_points };
}
//三个点确定一个角,计算角度
double ImageProcessor::CalcAngle(const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt0)
{
double dx1 = pt1.x - pt0.x;
@@ -104,6 +112,7 @@ namespace uns
return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
}
//交通标志用的图像颜色纯化函数
cv::Mat ImageProcessor::FixImageBUG_TF(cv::Mat img, double rate)
{
cv::Mat result(img.rows, img.cols, img.type());
@@ -135,6 +144,7 @@ namespace uns
return result;
}
//形状颜色用的图像纯化函数
Images::TwoImages ImageProcessor::FixImageBUG(cv::Mat img, double rate)
{
cv::Mat color_result, shape_result;
@@ -170,7 +180,7 @@ namespace uns
else if (MuchLarger(color.GetR(), color.GetB(), rate) && MuchLarger(color.GetG(), color.GetB(), rate))
{
color_result.at<cv::Vec3b>(r, c) = sample_colors.at("yellow");
shape_result.at<cv::Vec3b>(r, c) = sample_colors.at("white");
shape_result.at<cv::Vec3b>(r, c) = sample_colors.at("black");
}
else if (MuchLarger(color.GetR(), color.GetG(), rate) && MuchLarger(color.GetB(), color.GetG(), rate))
{
@@ -185,13 +195,14 @@ namespace uns
else
{
color_result.at<cv::Vec3b>(r, c) = sample_colors.at("white");
shape_result.at<cv::Vec3b>(r, c) = sample_colors.at("black");
shape_result.at<cv::Vec3b>(r, c) = sample_colors.at("white");
}
}
}
return { color_result,shape_result };
}
//获取屏幕区域
cv::Mat ImageProcessor::GetScreenFromImage(const cv::Mat& img)
{
cv::Mat temp;
@@ -200,6 +211,7 @@ namespace uns
return FillEdges(CutScreenImage(img, GetScreenRect(temp)), 20); //获取形状,裁切,填充边缘
}
//根据矩形裁切图片
cv::Mat ImageProcessor::CutRect(const cv::Mat& img, Shapes::Rectangle rect)
{
int top = 99999, left = 99999, bottom = 0, right = 0;
@@ -227,6 +239,7 @@ namespace uns
return result;
}
//根据原型裁切图片
cv::Mat ImageProcessor::CutCircle(const cv::Mat& image, Shapes::Circle circle)
{
cv::Mat result(circle.radius * 2, circle.radius * 2, image.type(), cv::Scalar(0, 255, 255));
@@ -245,6 +258,7 @@ namespace uns
return result;
}
//根据三角形裁切图片
cv::Mat ImageProcessor::CutTriangle(const cv::Mat& img, Shapes::Triangle triangle)
{
int top = 99999, left = 99999, bottom = 0, right = 0;
@@ -273,6 +287,7 @@ namespace uns
}
};
//导出的自检函数
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_ImageProcessorTest(JNIEnv *env, jclass _this)
{
+2
View File
@@ -8,6 +8,8 @@
#define IMAGE_PROCESSOR_VERSION "1.0.0"
//图片预处理,用于形状颜色、交通标志等的图像预处理
#include <map>
#include <jni.h>
#include <cmath>
@@ -0,0 +1,29 @@
//
// Created by 庾金科 on 21/10/2017.
//
#ifndef SWIFTPR_CNNRECOGNIZER_H
#define SWIFTPR_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;
};
}
#endif //SWIFTPR_CNNRECOGNIZER_H
@@ -0,0 +1,20 @@
//
// Created by 庾金科 on 22/09/2017.
//
#ifndef SWIFTPR_FASTDESKEW_H
#define SWIFTPR_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);
}//namepace pr
#endif //SWIFTPR_FASTDESKEW_H
@@ -0,0 +1,36 @@
//
// Created by 庾金科 on 22/09/2017.
//
#ifndef SWIFTPR_FINEMAPPING_H
#define SWIFTPR_FINEMAPPING_H
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.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;
};
}
#endif //SWIFTPR_FINEMAPPING_H
@@ -0,0 +1,58 @@
//
// Created by 庾金科 on 22/10/2017.
//
#ifndef SWIFTPR_PIPLINE_H
#define SWIFTPR_PIPLINE_H
#include "PlateDetection.h"
#include "PlateSegmentation.h"
#include "CNNRecognizer.h"
#include "PlateInfo.h"
#include "FastDeskew.h"
#include "FineMapping.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(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
);
~PipelinePR();
std::vector<std::string> plateRes;
std::vector<PlateInfo> RunPiplineAsImage(cv::Mat plateImage, int method);
};
}
#endif //SWIFTPR_PIPLINE_H
@@ -0,0 +1,40 @@
//
// Created by 庾金科 on 20/09/2017.
//
#ifndef SWIFTPR_PLATEDETECTION_H
#define SWIFTPR_PLATEDETECTION_H
#include <opencv2/opencv.hpp>
#include <vector>
#include "PlateInfo.h"
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);
public:
cv::CascadeClassifier cascade;
};
}// namespace pr
#endif //SWIFTPR_PLATEDETECTION_H
@@ -0,0 +1,161 @@
//
// Created by 庾金科 on 20/09/2017.
//
#ifndef SWIFTPR_PLATEINFO_H
#define SWIFTPR_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);
}
// cv::Mat getPlateChars(int id) {
// if(id<PlateChars.size())
// return PlateChars[id];
// }
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);
// std::cout<<*std::max_element(prob,prob+31)<<std::endl;
}
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);
// std::cout<<*std::max_element(prob+31,prob+65)<<std::endl;
}
else if (plate.first == INVALID)
{
decode += '*';
}
}
name = decode;
confidence /= 7;
return decode;
}
private:
cv::Mat licensePlate;
cv::Rect ROI;
std::string name;
PlateColor Type;
};
}
#endif //SWIFTPR_PLATEINFO_H
@@ -0,0 +1,55 @@
//
// Created by 庾金科 on 16/10/2017.
//
#ifndef SWIFTPR_PLATESEGMENTATION_H
#define SWIFTPR_PLATESEGMENTATION_H
#include "opencv2/opencv.hpp"
#include "opencv2/dnn.hpp"
#include "PlateInfo.h"
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;
// RefineRegion()
};
}//namespace pr
#endif //SWIFTPR_PLATESEGMENTATION_H
@@ -0,0 +1,29 @@
//
// Created by 庾金科 on 20/10/2017.
//
#ifndef SWIFTPR_RECOGNIZER_H
#define SWIFTPR_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);
};
}
#endif //SWIFTPR_RECOGNIZER_H
@@ -0,0 +1,33 @@
//
// Created by 庾金科 on 28/11/2017.
//
#ifndef SWIFTPR_SEGMENTATIONFREERECOGNIZER_H
#define SWIFTPR_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;
};
}
#endif //SWIFTPR_SEGMENTATIONFREERECOGNIZER_H
@@ -0,0 +1,112 @@
//
// Created by 庾金科 on 26/10/2017.
//
#ifndef SWIFTPR_NIBLACKTHRESHOLD_H
#define SWIFTPR_NIBLACKTHRESHOLD_H
#include <opencv2/opencv.hpp>
#include <opencv2/core/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 //SWIFTPR_NIBLACKTHRESHOLD_H
@@ -0,0 +1,284 @@
#include <jni.h>
#include <string>
#include "include/Pipeline.h"
#include <android/log.h>
#include <android/bitmap.h>
#include <opencv2/opencv.hpp>
#include "../opencv_support.h"
using namespace cv;
#define LOG_TAG "System.out"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
jobject mat_to_bitmap(JNIEnv *env, Mat &src, bool needPremultiplyAlpha, jobject bitmap_config)
{
jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
jmethodID mid = env->GetStaticMethodID(java_bitmap_class,
"createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
mid, src.size().width, src.size().height,
bitmap_config);
AndroidBitmapInfo info;
void *pixels = 0;
try
{
//validate
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
CV_Assert(pixels);
//type mat
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888)
{
Mat tmp(info.height, info.width, CV_8UC4, pixels);
if (src.type() == CV_8UC1)
{
cvtColor(src, tmp, COLOR_GRAY2RGBA);
}
else if (src.type() == CV_8UC3)
{
cvtColor(src, tmp, COLOR_RGB2RGBA);
}
else if (src.type() == CV_8UC4)
{
if (needPremultiplyAlpha)
{
cvtColor(src, tmp, COLOR_RGBA2mRGBA);
}
else
{
src.copyTo(tmp);
}
}
}
else
{
Mat tmp(info.height, info.width, CV_8UC2, pixels);
if (src.type() == CV_8UC1)
{
cvtColor(src, tmp, COLOR_GRAY2BGR565);
}
else if (src.type() == CV_8UC3)
{
cvtColor(src, tmp, COLOR_RGB2BGR565);
}
else if (src.type() == CV_8UC4)
{
cvtColor(src, tmp, COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return bitmap;
}
catch (cv::Exception e)
{
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("org/opencv/core/CvException");
if (!je) je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
return bitmap;
}
catch (...)
{
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
return bitmap;
}
}
std::string jstring2str(JNIEnv *env, jstring jstr)
{
char *rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char *) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
std::string stemp(rtn);
free(rtn);
return stemp;
}
extern "C" {
JNIEXPORT jlong JNICALL
Java_com_uns_maincar_cpp_1interface_hyperlpr_PlateRecognition_InitPlateRecognizer(
JNIEnv *env, jclass obj,
jstring detector_filename,
jstring finemapping_prototxt, jstring finemapping_caffemodel,
jstring segmentation_prototxt, jstring segmentation_caffemodel,
jstring charRecognization_proto, jstring charRecognization_caffemodel,
jstring segmentationfree_proto, jstring segmentationfree_caffemodel)
{
std::string detector_path = jstring2str(env, detector_filename);
std::string finemapping_prototxt_path = jstring2str(env, finemapping_prototxt);
std::string finemapping_caffemodel_path = jstring2str(env, finemapping_caffemodel);
std::string segmentation_prototxt_path = jstring2str(env, segmentation_prototxt);
std::string segmentation_caffemodel_path = jstring2str(env, segmentation_caffemodel);
std::string charRecognization_proto_path = jstring2str(env, charRecognization_proto);
std::string charRecognization_caffemodel_path = jstring2str(env, charRecognization_caffemodel);
std::string segmentationfree_proto_path = jstring2str(env, segmentationfree_proto);
std::string segmentationfree_caffemodel_path = jstring2str(env, segmentationfree_caffemodel);
pr::PipelinePR *PR = new pr::PipelinePR(detector_path,
finemapping_prototxt_path, finemapping_caffemodel_path,
segmentation_prototxt_path,
segmentation_caffemodel_path,
charRecognization_proto_path,
charRecognization_caffemodel_path,
segmentationfree_proto_path,
segmentationfree_caffemodel_path);
return (jlong) PR;
}
JNIEXPORT jstring JNICALL
Java_com_uns_maincar_cpp_1interface_hyperlpr_PlateRecognition_SimpleRecognization(
JNIEnv *env, jclass obj,
jlong matPtr, jlong object_pr)
{
pr::PipelinePR *PR = (pr::PipelinePR *) object_pr;
cv::Mat &mRgb = *(cv::Mat *) matPtr;
cv::Mat rgb;
cv::cvtColor(mRgb, rgb, cv::COLOR_RGBA2BGR);
//1表示SEGMENTATION_BASED_METHOD在方法里有说明
std::vector<pr::PlateInfo> list_res = PR->RunPiplineAsImage(rgb, pr::SEGMENTATION_FREE_METHOD);
// std::vector<pr::PlateInfo> list_res= PR->RunPiplineAsImage(rgb,1);
std::string concat_results;
for (auto one: list_res)
{
//可信度
if (one.confidence > 0.7)
concat_results += one.getPlateName() + ",";
}
concat_results = concat_results.substr(0, concat_results.size() - 1);
return env->NewStringUTF(concat_results.c_str());
}
JNIEXPORT jstring JNICALL
Java_com_uns_maincar_cpp_1interface_hyperlpr_PlateRecognition_EasyRecognization(
JNIEnv *env, jclass obj,
jobject image, jlong object_pr)
{
auto *PR = (pr::PipelinePR *) object_pr;
cv::Mat rgb;
BitmapToMat(env, image, rgb);
//1表示SEGMENTATION_BASED_METHOD在方法里有说明
std::vector<pr::PlateInfo> list_res = PR->RunPiplineAsImage(rgb, pr::SEGMENTATION_FREE_METHOD);
// std::vector<pr::PlateInfo> list_res= PR->RunPiplineAsImage(rgb,1);
std::string concat_results;
for (auto one: list_res)
{
//可信度
if (one.confidence > 0.7)
concat_results += one.getPlateName() + ",";
}
concat_results = concat_results.substr(0, concat_results.size() - 1);
return env->NewStringUTF(concat_results.c_str());
}
/**
* 车牌号的详细信息
* @param env
* @param obj
* @param matPtr
* @param object_pr
* @return
*/
JNIEXPORT jobject JNICALL
Java_com_uns_maincar_cpp_1interface_hyperlpr_PlateRecognition_PlateInfoRecognization(
JNIEnv *env, jobject obj,
jlong matPtr, jlong object_pr)
{
jclass plateInfo_class = env->FindClass("com/uns/maincar/cpp_interface/hyperlpr/PlateInfo");
jmethodID mid = env->GetMethodID(plateInfo_class, "<init>", "()V");
jobject plateInfoObj = env->NewObject(plateInfo_class, mid);
pr::PipelinePR *PR = (pr::PipelinePR *) object_pr;
cv::Mat &mRgb = *(cv::Mat *) matPtr;
cv::Mat rgb;
cv::cvtColor(mRgb, rgb, cv::COLOR_RGBA2BGR);
//1表示SEGMENTATION_BASED_METHOD在方法里有说明
std::vector<pr::PlateInfo> list_res = PR->RunPiplineAsImage(rgb, pr::SEGMENTATION_FREE_METHOD);
std::string concat_results;
pr::PlateInfo plateInfo;
for (auto one: list_res)
{
//可信度
if (one.confidence > 0.7)
{
plateInfo = one;
//车牌号
jfieldID fid_plate_name = env->GetFieldID(plateInfo_class, "plateName",
"Ljava/lang/String;");
env->SetObjectField(plateInfoObj, fid_plate_name,
env->NewStringUTF(plateInfo.getPlateName().c_str()));
//识别区域
Mat src = plateInfo.getPlateImage();
jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap$Config");
jmethodID bitmap_mid = env->GetStaticMethodID(java_bitmap_class,
"nativeToConfig",
"(I)Landroid/graphics/Bitmap$Config;");
jobject bitmap_config = env->CallStaticObjectMethod(java_bitmap_class, bitmap_mid, 5);
jfieldID fid_bitmap = env->GetFieldID(plateInfo_class, "bitmap",
"Landroid/graphics/Bitmap;");
jobject _bitmap = mat_to_bitmap(env, src, false, bitmap_config);
env->SetObjectField(plateInfoObj, fid_bitmap, _bitmap);
return plateInfoObj;
}
}
return plateInfoObj;
}
JNIEXPORT void JNICALL
Java_com_uns_maincar_cpp_1interface_hyperlpr_PlateRecognition_ReleasePlateRecognizer(
JNIEnv *env, jclass obj,
jlong object_re)
{
// std::string hello = "Hello from C++";
pr::PipelinePR *PR = (pr::PipelinePR *) object_re;
delete PR;
}
}
@@ -0,0 +1,24 @@
//
// Created by 庾金科 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();
}
}
@@ -0,0 +1,158 @@
//
// Created by 庾金科 on 02/10/2017.
//
#include "../include/FastDeskew.h"
#include <cmath>
#include <vector>
#include <opencv2/core/types.hpp>
#include <opencv2/core/mat.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/opencv.hpp>
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;
// if(angle<0)
extend_padding = static_cast<int>(skewPlate.rows * tan(cv::abs(angle) / 180 * 3.14));
// else
// extend_padding = static_cast<int>(skewPlate.rows/tan(cv::abs(angle)/180* 3.14) );
// std::cout<<"extend:"<<extend_padding<<std::endl;
cv::Size size(skewPlate.cols + extend_padding, skewPlate.rows);
float interval = std::abs(sin((angle / 180) * 3.14) * skewPlate.rows);
// std::cout<<interval<<std::endl;
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,218 @@
//
// Created by 庾金科 on 22/09/2017.
//
#include "../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)
{
// if(FinedVertical.channels()==1)
// cv::cvtColor(FinedVertical,FinedVertical,cv::COLOR_GRAY2BGR);
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));
// cv::imwrite("res/cache/finemapping.jpg",PreInputProposal);
if (InputProposal.channels() == 3)
cv::cvtColor(PreInputProposal, proposal, cv::COLOR_BGR2GRAY);
else
PreInputProposal.copyTo(proposal);
// proposal = PreInputProposal;
// this will improve some sen
cv::Mat kernal = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(1, 3));
// cv::erode(proposal,proposal,kernal);
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::imshow("image",binary_adaptive);
// cv::waitKey(0);
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;
}
}
}
// std:: cout<<"contours_nums "<<contours_nums<<std::endl;
}
cv::Mat rgb;
cv::copyMakeBorder(PreInputProposal, rgb, PADDING_UP_DOWN, PADDING_UP_DOWN, 0, 0,
cv::BORDER_REPLICATE);
// cv::imshow("rgb",rgb);
// cv::waitKey(0);
//
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;
// pts_map1 = np.float32([[cols - 1, rightyA], [0, leftyA],[cols - 1, rightyB], [0, leftyB]])
// pts_map2 = np.float32([[136,36],[0,36],[136,0],[0,0]])
// mat = cv2.getPerspectiveTransform(pts_map1,pts_map2)
// image = cv2.warpPerspective(rgb,mat,(136,36),flags=cv2.INTER_CUBIC)
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;
}
}
@@ -0,0 +1,109 @@
//
// Created by 庾金科 on 23/10/2017.
//
#include "../include/Pipeline.h"
namespace pr
{
const int HorizontalPadding = 4;
PipelinePR::PipelinePR(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);
}
PipelinePR::~PipelinePR()
{
delete plateDetection;
delete fineMapping;
delete plateSegmentation;
delete generalRecognizer;
delete segmentationFreeRecognizer;
}
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));
// cv::imshow("image_finemapping",image_finemapping);
// cv::waitKey(0);
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);
}
// for (auto str:results) {
// std::cout << str << std::endl;
// }
return results;
}//namespace pr
}
@@ -0,0 +1,67 @@
//
// Created by 庾金科 on 20/09/2017.
//
#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(InputImage);
// cv::Mat processImage;
// cv::cvtColor(InputImage,processImage,cv::COLOR_BGR2GRAY);
std::vector<cv::Rect> platesRegions;
// std::vector<PlateInfo> plates;
cv::Size minSize(min_w, min_w / 4);
cv::Size maxSize(max_w, max_w / 4);
// cv::imshow("input",InputImage);
// cv::waitKey(0);
cascade.detectMultiScale(processImage, platesRegions,
1.1, 3, cv::CASCADE_SCALE_IMAGE, minSize, maxSize);
for (auto plate: platesRegions)
{
// extend rects
// x -= w * 0.14
// w += w * 0.28
// y -= h * 0.6
// h += h * 1.1;
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);
}
}
// std::vector<pr::PlateInfo> PlateDetection::plateDetectionRough(cv::Mat InputImage,cv::Rect roi,int min_w,int max_w){
// cv::Mat roi_region = util::cropFromImage(InputImage,roi);
// return plateDetectionRough(roi_region,min_w,max_w);
// }
}//namespace pr
@@ -0,0 +1,433 @@
//
// Created by 庾金科 on 16/10/2017.
//
#include "../include/PlateSegmentation.h"
#include "../include/niBlackThreshold.h"
//#define DEBUG
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::Mat roiImage;
// plateImage.copyTo(roiImage);
cv::Rect roi(left, 0, right - left, rows - 1);
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;
// 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);
// avgfilter(ch_prob,cols,5);
std::vector<int> candidate_pts(7);
#ifdef DEBUG
drawHist(engNum_prob,cols,"engNum_prob");
drawHist(false_prob,cols,"false_prob");
drawHist(ch_prob,cols,"ch_prob");
cv::waitKey(0);
#endif
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]+
// engNum_prob[cp2_p0] +engNum_prob[cp3_p1]+engNum_prob[cp4_p2]+engNum_prob[cp5_p3]+engNum_prob[cp6_p4] +engNum_prob[cp7_p5]
// + (false_prob[md2]+false_prob[md3]+false_prob[md4]+false_prob[md5]+false_prob[md5] + false_prob[md6]);
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::resize(plateImage,plateImage,cv::Size(136,36));
cv::Mat plateImageGray;
cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
int padding = plateImage.cols - 136;
// int padding = 0 ;
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();
// std::pair<float,std::vector<int>> images ;
//
//
// std::cout<<images.first<<" ";
// for(int i = 0 ; i < images.second.size() ; i++)
// {
// std::cout<<images.second[i]<<" ";
//// cv::line(plateImageGray,cv::Point(images.second[i],0),cv::Point(images.second[i],36),cv::Scalar(255,255,255),1); //DEBUG
// }
// int w = images.second[5] - images.second[4];
// cv::line(plateImageGray,cv::Point(images.second[5]+w,0),cv::Point(images.second[5]+w,36),cv::Scalar(255,255,255),1); //DEBUG
// cv::line(plateImageGray,cv::Point(images.second[5]+2*w,0),cv::Point(images.second[5]+2*w,36),cv::Scalar(255,255,255),1); //DEBUG
// RefineRegion(plateImageGray,images.second,5);
// std::cout<<w<<std::endl;
// std::cout<<<<std::endl;
// cv::resize(plateImageGray,plateImageGray,cv::Size(600,100));
}
// void filterGaussian(cv::Mat &respones,float sigma){
//
// }
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;
}
// std::cout<<sections<<std::endl;
refineRegion(plateImageGray, sections.second, 5, Char_rects);
#ifdef DEBUG
for(int i = 0 ; i < sections.second.size() ; i++)
{
std::cout<<sections.second[i]<<" ";
cv::line(plateImageGray,cv::Point(sections.second[i],0),cv::Point(sections.second[i],36),cv::Scalar(255,255,255),1); //DEBUG
}
cv::imshow("plate",plateImageGray);
cv::waitKey(0);
#endif
// cv::waitKey(0);
}
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::imshow("image",charImage);
// cv::waitKey(0);
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,33 @@
//
// Created by 庾金科 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);
}
}
}
}
@@ -0,0 +1,129 @@
//
// Created by 庾金科 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::imshow("imagea",code_table);
// cv::waitKey(0);
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]];
}
std::cout << name;
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);
}
}
+85
View File
@@ -0,0 +1,85 @@
//
// Created by 庾金科 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
@@ -19,6 +19,7 @@
* limitations under the License.
*/
//Google提供的串口通信库
#include <android/log.h>
#include <sys/types.h>
#include <sys/stat.h>
+3
View File
@@ -3,6 +3,9 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//数据处理算法
//这个算法不做额外说明,实际比赛中算法会发生变化
#include "main_car_aes.h"
namespace uns
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_MAIN_CAR_AES_H
#define MAINCAR_MAIN_CAR_AES_H
//数据处理算法
#define MAIN_CAR_AES_VERSION "1.0.0"
#include <jni.h>
+45 -25
View File
@@ -3,33 +3,49 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//OCR的图像预处理算法
#include "ocr_text.h"
bool OCRSupport::PixelCheck(const cv::Vec3b &pixel)
namespace uns
{
if (pixel[0] <= limit)
if (pixel[1] <= limit)
if (pixel[2] <= limit)
return true;
return false;
}
cv::Mat OCRSupport::ConvertImage(const cv::Mat &img)
{
cv::Mat result(img.size(), img.type());
for (int r = 0; r < img.rows; r++)
//像素检查,limit阈值是黑色的浓淡(文字是图片中最黑的部分)
bool OCRSupport::PixelCheck(const cv::Vec3b &pixel)
{
for (int c = 0; c < img.cols; c++)
{
if (PixelCheck(img.at<cv::Vec3b>(r, c)))
result.at<cv::Vec3b>(r, c) = cv::Vec3b(0, 0, 0);
else
result.at<cv::Vec3b>(r, c) = cv::Vec3b(255, 255, 255);
}
if (pixel[0] <= limit)
if (pixel[1] <= limit)
if (pixel[2] <= limit)
return true;
return false;
}
return result;
}
//图片裁切
cv::Mat OCRSupport::CutImageSize(const cv::Mat &img)
{
return img(roi);
}
//图片预处理,将彩色图片转换为黑白图片,确保文字是黑色的
cv::Mat OCRSupport::ConvertImage(const cv::Mat &img)
{
cv::imwrite("/sdcard/MainCar/OCR/ocr_old.jpg", img);
cv::Mat result(img.size(), img.type());
for (int r = 0; r < img.rows; r++)
{
for (int c = 0; c < img.cols; c++)
{
if (PixelCheck(img.at<cv::Vec3b>(r, c)))
result.at<cv::Vec3b>(r, c) = cv::Vec3b(0, 0, 0);
else
result.at<cv::Vec3b>(r, c) = cv::Vec3b(255, 255, 255);
}
}
cv::imwrite("/sdcard/MainCar/OCR/ocr_process.jpg", result);
return result;
}
};
//导出的自检函数
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_OCRTextTest(JNIEnv *env, jclass _this)
{
@@ -37,13 +53,17 @@ jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_OCRTextTest(JNIEnv *
return env->NewStringUTF(version.c_str());
}
//导出的图像预处理函数
extern "C" JNIEXPORT
jobject JNICALL Java_com_uns_maincar_cpp_1interface_OCR_ProcessImage(JNIEnv* env, jclass _this, jobject image)
jobject JNICALL
Java_com_uns_maincar_cpp_1interface_OCR_ProcessImage(JNIEnv *env, jclass _this, jobject image,
jboolean self_test)
{
cv::Mat source;
OCRSupport ocr_supp;
BitmapToMat(env,image,source);
cv::Mat img = ocr_supp.ConvertImage(source);
uns::OCRSupport ocr_supp;
BitmapToMat(env, image, source);
cv::Mat img = (self_test ? ocr_supp.ConvertImage(source) : ocr_supp.ConvertImage(
ocr_supp.CutImageSize(source)));
jobject bmp = GenerateBitmap(env, img.cols, img.rows);
MatToBitmap(env, img, bmp);
return bmp;
+17 -7
View File
@@ -6,19 +6,29 @@
#ifndef MAINCAR_OCR_TEXT_H
#define MAINCAR_OCR_TEXT_H
//OCR的图像预处理算法
#define OCR_TEXT_VERSION "1.0.0"
#include <jni.h>
#include "opencv_support.h"
#include <opencv2/highgui.hpp>
class OCRSupport
namespace uns
{
private:
const int limit = 65;
private:
bool PixelCheck(const cv::Vec3b& pixel);
public:
cv::Mat ConvertImage(const cv::Mat& img);
class OCRSupport
{
private:
const int limit = 90;
cv::Rect roi = cv::Rect(150, 60, 350, 300);
private:
bool PixelCheck(const cv::Vec3b &pixel);
public:
cv::Mat CutImageSize(const cv::Mat &img);
cv::Mat ConvertImage(const cv::Mat &img);
};
};
#endif //MAINCAR_OCR_TEXT_H
+40 -8
View File
@@ -5,13 +5,19 @@
#include "opencv_support.h"
//Java中的Bitmap与OpenCV的Mat互转
//Bitmap转Mat
bool BitmapToMat(JNIEnv *env, jobject obj_bitmap, cv::Mat &matrix)
{
void *bitmapPixels; // Save picture pixel data
AndroidBitmapInfo bitmapInfo; // Save picture parameters
ASSERT_FALSE(AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0); // Get picture parameters
ASSERT_FALSE(bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888 || bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565); // Only ARGB? 8888 and RGB? 565 are supported
ASSERT_FALSE(AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0); // Get picture pixels (lock memory block)
ASSERT_FALSE(AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >=
0); // Get picture parameters
ASSERT_FALSE(bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888 || bitmapInfo.format ==
ANDROID_BITMAP_FORMAT_RGB_565); // Only ARGB? 8888 and RGB? 565 are supported
ASSERT_FALSE(AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >=
0); // Get picture pixels (lock memory block)
ASSERT_FALSE(bitmapPixels);
if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888)
{
@@ -29,7 +35,7 @@ bool BitmapToMat(JNIEnv *env, jobject obj_bitmap, cv::Mat &matrix)
return true;
}
//Mat转Bitmap
bool MatToBitmap(JNIEnv *env, cv::Mat &matrix, jobject obj_bitmap)
{
void *bitmapPixels; // Save picture pixel data
@@ -82,14 +88,40 @@ bool MatToBitmap(JNIEnv *env, cv::Mat &matrix, jobject obj_bitmap)
return true;
}
//创建Bitmap图片
jobject GenerateBitmap(JNIEnv *env, jint width, jint height)
{
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jstring configName = env->NewStringUTF("ARGB_8888");
jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, width, height, bitmapConfig);
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf",
"(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,
valueOfBitmapConfigFunction, configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, width, height,
bitmapConfig);
return newBitmap;
}
//导出的图片保存函数,用于MainActivity中的长按保存函数
extern "C" JNIEXPORT
jboolean JNICALL
Java_com_uns_maincar_gui_MainActivity_SaveImage(JNIEnv *env, jclass _this, jobject image,
jstring time)
{
cv::Mat source;
if (!BitmapToMat(env, image, source))
return false;
std::string time_str = env->GetStringUTFChars(time, 0);
try
{
cv::imwrite("/sdcard/MainCar/saved_" + time_str + ".jpg", source);
return true;
}
catch (...)
{
return false;
}
}
+3
View File
@@ -6,10 +6,13 @@
#ifndef MAINCAR_OPENCV_SUPPORT_H
#define MAINCAR_OPENCV_SUPPORT_H
//Java中的Bitmap与OpenCV的Mat互转
#include <jni.h>
#include <android/bitmap.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#define ASSERT(status, ret) if (!(status)) { return ret; }
#define ASSERT_FALSE(status) ASSERT(status, false)
+2
View File
@@ -3,6 +3,8 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//图片预处理、形状颜色、交通标志、交通灯的类型定义
#include "public_types.h"
namespace uns
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_PUBLIC_TYPES_H
#define MAINCAR_PUBLIC_TYPES_H
//图片预处理、形状颜色、交通标志、交通灯的类型定义
#include <map>
#include <vector>
#include <opencv2/core/core.hpp>
+12 -1
View File
@@ -3,11 +3,14 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//二维码解析前图像处理
#include "qr_code_decode.h"
namespace uns
{
bool QrDecode::SplitMultipleQR(const cv::Mat& img, std::vector<cv::Rect>& rects)
//使用导出的OpenCV库切分多个二维码
bool QrDecode::SplitMultipleQR(const cv::Mat &img, std::vector<cv::Rect> &rects)
{
uns_cv_export::QRCodeDetector qrcode;
std::vector<cv::Point> corners;
@@ -25,22 +28,26 @@ namespace uns
return false;
}
//检查是否有下一个二维码图片
bool QrDecode::HasNextImage()
{
return (current_index < qr_codes.size());
}
//获取下一个二维码图片
cv::Mat QrDecode::GetNextImage()
{
return source_image(qr_codes[current_index++]);
}
//切分并存储二维码
bool QrDecode::SplitQR(const cv::Mat& img)
{
img.copyTo(source_image);
return SplitMultipleQR(img, qr_codes);
}
//清空存储
void QrDecode::Clear()
{
qr_codes.clear();
@@ -50,6 +57,7 @@ namespace uns
uns::QrDecode global_qr_decoder;
//导出的图像处理函数
extern "C" JNIEXPORT
jboolean JNICALL Java_com_uns_maincar_cpp_1interface_QRDecoder_ProcessQR(JNIEnv *env, jclass _this, jobject image)
{
@@ -60,12 +68,14 @@ jboolean JNICALL Java_com_uns_maincar_cpp_1interface_QRDecoder_ProcessQR(JNIEnv
return global_qr_decoder.SplitQR(img_input);
}
//导出的检查是否有下一个二维码函数
extern "C" JNIEXPORT
jboolean JNICALL Java_com_uns_maincar_cpp_1interface_QRDecoder_HasNextQR(JNIEnv *env, jclass _this)
{
return global_qr_decoder.HasNextImage();
}
//导出的获取下一个二维码图片函数
extern "C" JNIEXPORT
jobject JNICALL Java_com_uns_maincar_cpp_1interface_QRDecoder_GetNextQR(JNIEnv* env, jclass _this)
{
@@ -75,6 +85,7 @@ jobject JNICALL Java_com_uns_maincar_cpp_1interface_QRDecoder_GetNextQR(JNIEnv*
return bmp;
}
//导出的强制清空函数
extern "C" JNIEXPORT
void JNICALL Java_com_uns_maincar_cpp_1interface_QRDecoder_ForceClear(JNIEnv *env, jclass _this)
{
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_QR_CODE_DECODE_H
#define MAINCAR_QR_CODE_DECODE_H
//二维码解析前图像处理
#include <jni.h>
#include <vector>
#include <iostream>
+12 -2
View File
@@ -3,11 +3,15 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//形状颜色识别
#include "shape_color_reco.h"
namespace uns
{
bool ShapeColorReco::RecoEverything(const cv::Mat& img, double rate)
//识别所有东西
//预处理,图像切分,使用黑白图像识别形状,使用彩色图像识别颜色
bool ShapeColorReco::RecoEverything(const cv::Mat &img, double rate)
{
if (img.empty())
return false;
@@ -17,11 +21,13 @@ namespace uns
Images::TwoImages temp = image_processor.FixImageBUG(screen, rate);
if (temp.img1.empty() || temp.img2.empty())
return false;
cv::imwrite("/sdcard/MainCar/temp_img1.jpg", temp.img1);
cv::imwrite("/sdcard/MainCar/temp_img2.jpg", temp.img2);
Shapes::Stars stars = shape_reco.GetStars(temp.img2);
Shapes::Rects rects = shape_reco.GetRects(temp.img2);
Shapes::Circles circles = shape_reco.GetCircles(temp.img2);
Shapes::Triangles triangles = shape_reco.GetTriangles(temp.img2);
for (auto& c : circles)
for (auto &c: circles)
{
cv::Mat shape = image_processor.CutCircle(temp.img1, c);
if (shape.empty())
@@ -55,6 +61,7 @@ namespace uns
return true;
}
//查询结果
int ShapeColorReco::LookupRecoResult(Images::ShapeType shape, std::string color)
{
return shape_color_counter[shape][color];
@@ -63,6 +70,7 @@ namespace uns
uns::ShapeColorReco global_shape_color_reco;
//导出的自检函数
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_ShapeColorRecoTest(JNIEnv *env, jclass _this)
{
@@ -70,6 +78,7 @@ jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_ShapeColorRecoTest(J
return env->NewStringUTF(version.c_str());
}
//导出的识别函数
extern "C" JNIEXPORT
jboolean JNICALL Java_com_uns_maincar_cpp_1interface_ShapeColor_RecoEverything(JNIEnv *env, jclass _this, jobject image, jdouble rate)
{
@@ -80,6 +89,7 @@ jboolean JNICALL Java_com_uns_maincar_cpp_1interface_ShapeColor_RecoEverything(J
return global_shape_color_reco.RecoEverything(source, rate);
}
//导出的结果查询函数
extern "C" JNIEXPORT
jint JNICALL Java_com_uns_maincar_cpp_1interface_ShapeColor_LookupRecoResult(JNIEnv *env, jclass _this, jint shape, jstring color)
{
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_SHAPE_COLOR_RECO_H
#define MAINCAR_SHAPE_COLOR_RECO_H
//形状颜色识别
#include <jni.h>
#include "color_reco.h"
#include "shape_reco.h"
+16
View File
@@ -3,10 +3,13 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//形状识别
#include "shape_reco.h"
namespace uns
{
//取三个数中最大的数
int ShapeReco::tri_max(int a, int b, int c)
{
if ((a > b) && (a > c))
@@ -17,6 +20,7 @@ namespace uns
return c;
}
//取三个数中最小的数
int ShapeReco::tri_min(int a, int b, int c)
{
if ((a < b) && (a < c))
@@ -27,6 +31,7 @@ namespace uns
return c;
}
//旋转三角形
Shapes::Triangle ShapeReco::Rotate(const Shapes::Triangle& tri, int r)
{
Shapes::Triangle result;
@@ -51,6 +56,7 @@ namespace uns
return result;
}
//根据四个点计算矩形
Shapes::Rectangle ShapeReco::CalcRectangle(const std::vector<cv::Point>& four_points)
{
int width = lround(sqrtf(powf((four_points[0].x - four_points[1].x), 2) + powf((four_points[0].y - four_points[1].y), 2)));
@@ -58,6 +64,7 @@ namespace uns
return uns::Shapes::Rectangle{ height, width, four_points };
}
//检查两个三角形是否重合/过于靠近
bool ShapeReco::TriangleTooClose(const Shapes::Triangle& tri1, const Shapes::Triangle& tri2)
{
int max_off_1 = CalcTriangleMaxOff(tri1, tri2);
@@ -67,6 +74,7 @@ namespace uns
return (tri_min(max_off_1, max_off_2, std::min(max_off_3, max_off_4)) <= 20);
}
//检查两个矩形是否重合/过于靠近
bool ShapeReco::RectTooClose(const Shapes::Rectangle& rect1, const Shapes::Rectangle& rect2)
{
int top_x_off = abs(rect1.four_points[0].x - rect2.four_points[0].x);
@@ -77,6 +85,7 @@ namespace uns
return (max_off <= 20);
}
//计算两个三角形的最大距离
int ShapeReco::CalcTriangleMaxOff(const Shapes::Triangle& tri1, const Shapes::Triangle& tri2)
{
int a_x_off = abs(tri1.three_points[0].x - tri2.three_points[0].x);
@@ -88,6 +97,7 @@ namespace uns
return std::max(tri_max(a_x_off, b_x_off, c_x_off), tri_max(a_y_off, b_y_off, c_y_off));
}
//计算三个点组成的角度
double ShapeReco::CalcAngle(const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt0)
{
double dx1 = pt1.x - pt0.x;
@@ -97,6 +107,7 @@ namespace uns
return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
}
//识别五角星
Shapes::Stars ShapeReco::GetStars(const cv::Mat& img)
{
int thresh = 50, N = 5;
@@ -145,6 +156,7 @@ namespace uns
return result;
}
//识别矩形
Shapes::Rects ShapeReco::GetRects(const cv::Mat& img)
{
int thresh = 50, N = 5;
@@ -198,6 +210,7 @@ namespace uns
return result;
}
//检查矩形是否是正方形
bool ShapeReco::IsSquare(const Shapes::Rectangle& rect)
{
if (rect.width == rect.height)
@@ -208,6 +221,7 @@ namespace uns
return false;
}
//识别圆形
Shapes::Circles ShapeReco::GetCircles(const cv::Mat& img)
{
cv::Mat gray;
@@ -231,6 +245,7 @@ namespace uns
return result;
}
//识别三角形
Shapes::Triangles ShapeReco::GetTriangles(const cv::Mat& img)
{
Shapes::Triangles result, temp;
@@ -279,6 +294,7 @@ namespace uns
}
};
//导出的自检函数
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_ShapeRecoTest(JNIEnv *env, jclass _this)
{
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_SHAPE_RECO_H
#define MAINCAR_SHAPE_RECO_H
//形状识别
#define SHAPE_RECO_VERSION "1.0.0"
#include <map>
+72 -183
View File
@@ -3,238 +3,125 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//交通灯识别
#include "traffic_light.h"
namespace uns
{
int TrafficLight::GetCircle(cv::Mat& img)
{
cv::Mat gray;
cvtColor(img, gray, cv::COLOR_BGR2GRAY);
GaussianBlur(gray, gray, cv::Size(9, 9), 2, 2); //平滑滤波
//检测圆形
std::vector<cv::Vec3f> circles;
double dp = 2.5; //
double minDist = 10; //两个圆心之间的最小距离
double param1 = 100; //Canny边缘检测的较大阈值
double param2 = 100; //累加器阈值
int min_radius = 20; //圆形半径的最小值
int max_radius = 200; //圆形半径的最大值
HoughCircles(gray, circles, cv::HOUGH_GRADIENT, dp, minDist, param1, param2,min_radius, max_radius);
int circle_r_max = 0;
cv::Point max_circle_center = cv::Point(0, 0);
for (size_t i = 0; i < circles.size(); i++)
{
int radius = cvRound(circles[i][2]);
//circle(img, Point(cvRound(circles[i][0]), cvRound(circles[i][1])), radius, Scalar(0, 0, 0), 4, 8, 0);
if (radius > circle_r_max)
{
circle_r_max = radius;
max_circle_center = cv::Point(cvRound(circles[i][0]), cvRound(circles[i][1]));
}
}
CutImage(img, circle_r_max, cv::Point(max_circle_center.y, max_circle_center.x));
return 0;
}
bool TrafficLight::CheckRGB(cv::Vec3b point)
{
if (point[0] < 240)
if (point[1] < 240)
if (point[2] < 240)
return true;
return false;
}
//取三个数中的最大值
int TrafficLight::tri_max(int a, int b, int c)
{
return std::max(std::max(a, b), c);
}
void TrafficLight::ImageProcess(cv::Mat& image)
//根据亮度获取亮起的灯的位置
cv::Mat TrafficLight::GetLight(const cv::Mat &img)
{
for (int i = 0; i < image.rows; i++)
cv::Mat hsv;
cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
cv::Mat chn_v(hsv.size(), CV_8UC1);
for (int r = 0; r < hsv.rows; r++)
{
for (int j = 0; j < image.cols; j++)
for (int c = 0; c < hsv.cols; c++)
{
if (CheckRGB(image.at<cv::Vec3b>(i, j)))
{
image.at<cv::Vec3b>(i, j)[0] = 255;
image.at<cv::Vec3b>(i, j)[1] = 255;
image.at<cv::Vec3b>(i, j)[2] = 255;
}
uchar v = hsv.at<cv::Vec3b>(r, c)[2];
chn_v.at<uchar>(r, c) = (v >= 250 ? 0 : 255);
}
}
return;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(11, 11));
cv::morphologyEx(chn_v, chn_v, cv::MORPH_CLOSE, kernel);
cv::Rect max_validate_rect = GetMaxRect(chn_v);
if (max_validate_rect.size().area() == 0)
return cv::Mat();
return img(max_validate_rect);
}
bool TrafficLight::ApproximatelyEqual(int a, int b, int offset)
//获取图片中面积最大的轮廓的外接矩形
cv::Rect TrafficLight::GetMaxRect(const cv::Mat &img)
{
if (a == b)
return true;
if (((a + offset) >= b) && ((a - offset) <= b))
return true;
else
return false;
}
void TrafficLight::CutImage(cv::Mat& image,int radius,cv::Point center)
{
cv::Mat result(radius * 2, radius * 2, image.type(), cv::Scalar(0, 0, 0));
cv::Point start(center.x - radius, center.y - radius);
int x_end = center.x + radius;
int y_end = center.y + radius;
for (int i = start.x; i < x_end; i++)
cv::Rect max_rect;
double max_rect_size = 0;
std::vector<cv::Vec4i> hierarchy;
std::vector<std::vector<cv::Point>> contours;
cv::findContours(img, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE); //轮廓查找
for (auto &contour: contours) //检测所找到的轮廓
{
for (int j = start.y; j < y_end; j++)
double area = cv::contourArea(cv::Mat(contour));
if (area > (img.size().area() / 2.0))
continue;
if (area > max_rect_size)
{
result.at<cv::Vec3b>(i - start.x, j - start.y)[0] = image.at<cv::Vec3b>(i, j)[0];
result.at<cv::Vec3b>(i - start.x, j - start.y)[1] = image.at<cv::Vec3b>(i, j)[1];
result.at<cv::Vec3b>(i - start.x, j - start.y)[2] = image.at<cv::Vec3b>(i, j)[2];
max_rect_size = area;
max_rect = cv::boundingRect(contour);
}
}
result.copyTo(image);
return;
}
void TrafficLight::CountMax(cv::Vec3b data, int& rmax, int& gmax, int& bmax)
{
if ((data[0] == data[1]) && (data[1] == data[2]))
return;
uchar _max = tri_max(data[0], data[1], data[2]);
if (_max == data[0])
bmax++;
if (_max == data[1])
gmax++;
if (_max == data[2])
rmax++;
return;
}
Light::TL_COLOR TrafficLight::GetColor(cv::Mat &image, int offset)
{
ImageProcess(image);
GetCircle(image);
int rmax_count = 0, gmax_count = 0, bmax_count = 0;
for (int i = 0; i < image.rows; i++)
for (int j = 0; j < image.cols; j++)
CountMax(image.at<cv::Vec3b>(i, j), rmax_count, gmax_count, bmax_count);
if (ApproximatelyEqual(rmax_count, gmax_count, offset))
return Light::TL_COLOR::Yellow;
int max_max = tri_max(rmax_count, gmax_count, bmax_count);
if (max_max == rmax_count)
return Light::TL_COLOR::Red;
else if ((max_max == gmax_count) || (max_max == bmax_count))
return Light::TL_COLOR::Green;
return Light::TL_COLOR::Yellow;
return max_rect;
}
//“远大于”函数
bool TrafficLight::MuchLarger(int a, int b, double rate)
{
return (a >= (b * rate));
}
void TrafficLight::CountColor(const cv::Mat &img, double rate, int &r_cnt, int &g_cnt, int &y_cnt)
{
r_cnt = 0;
g_cnt = 0;
y_cnt = 0;
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
cv::Vec3b color = img.at<cv::Vec3b>(i, j);
if (MuchLarger(color[2], color[1], rate) && MuchLarger(color[2], color[1], rate))
r_cnt++;
else if (MuchLarger(color[1], color[0], rate) && MuchLarger(color[1], color[2], rate))
g_cnt++;
else if (ApproximatelyEqual(color[1], color[2], rate))
y_cnt++;
}
}
}
Light::TL_COLOR TrafficLight::Reco(cv::Mat &img)
{
/*int r = 0, g = 0, y = 0;
GetCircle(img);
CountColor(img, 2.5, r, g, y);
int max = tri_max(r, g, y);
if (max == r)
return Light::TL_COLOR::Red;
else if (max == g)
return Light::TL_COLOR::Green;
else
return Light::TL_COLOR::Yellow;*/
ImageProcess(img);
if(img.empty())
return Light::TL_COLOR::Null;
img = SplitImage(img);
if(img.empty())
return Light::TL_COLOR::Null;
return GetImageColor(img, 1.3);
}
cv::Mat TrafficLight::SplitImage(const cv::Mat &img)
{
int max_image_area = 0;
cv::Rect max_image_rect;
uns::Images::Contour approx;
uns::Images::Contours contours;
cv::Mat shape_image(img.size(), CV_8UC1);
for (int r = 0; r < img.rows; r++)
{
for (int c = 0; c < img.cols; c++)
{
cv::Vec3b color = img.at<cv::Vec3b>(r, c);
if ((color[0] > 240) && (color[1] > 240) && (color[2] > 240))
shape_image.at<uchar>(r, c) = 255;
else
shape_image.at<uchar>(r, c) = 0;
}
}
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(11, 11));
cv::morphologyEx(shape_image, shape_image, cv::MORPH_CLOSE, kernel);
findContours(shape_image, contours, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE); //轮廓查找
for (const auto& contour : contours)
{
if (cv::contourArea(contour) >= (img.total() / 2.0))
continue;
cv::Rect target_rect = boundingRect(contour);
if (target_rect.area() > max_image_area)
max_image_rect = target_rect;
}
return img(max_image_rect);
}
//识别灯的颜色
Light::TL_COLOR TrafficLight::GetImageColor(const cv::Mat &img, double rate)
{
int cnt_r = 0, cnt_g = 0, cnt_y = 0;
int cnt_g_less = 0, cnt_b_less = 0;
for (int r = 0; r < img.rows; r++)
{
for (int c = 0; c < img.cols; c++)
{
uns::Colors::CVColor color(img.at<cv::Vec3b>(r, c));
if (MuchLarger(color.GetR(), color.GetG(), rate) && MuchLarger(color.GetR(), color.GetB(), rate))
if (MuchLarger(color.GetR(), color.GetG(), rate) &&
MuchLarger(color.GetR(), color.GetB(), rate))
cnt_r++;
else if (MuchLarger(color.GetG(), color.GetR(), rate) && MuchLarger(color.GetG(), color.GetB(), rate))
else if (MuchLarger(color.GetG(), color.GetR(), rate) &&
MuchLarger(color.GetG(), color.GetB(), rate))
cnt_g++;
else if (MuchLarger(color.GetB(), color.GetR(), rate) && MuchLarger(color.GetB(), color.GetG(), rate))
else if (MuchLarger(color.GetB(), color.GetR(), rate) &&
MuchLarger(color.GetB(), color.GetG(), rate))
cnt_g++;
else if (MuchLarger(color.GetR(), color.GetB(), rate) && MuchLarger(color.GetG(), color.GetB(), rate))
else if (MuchLarger(color.GetR(), color.GetB(), rate) &&
MuchLarger(color.GetG(), color.GetB(), rate))
cnt_y++;
if (color.GetG() < 110)
cnt_g_less++;
if (color.GetB() < 110)
cnt_b_less++;
}
}
LOGI("GLess: %d, BLess: %d", cnt_g_less, cnt_b_less);
int cnt_max = tri_max(cnt_r, cnt_g, cnt_y);
if (cnt_max == cnt_r)
return Light::TL_COLOR::Red;
if ((cnt_max == cnt_r) || (cnt_max == cnt_y))
{
if ((cnt_g_less < 50) /*&& (cnt_b_less < 5)*/)
return Light::TL_COLOR::Yellow;
else
return Light::TL_COLOR::Red;
}
else if (cnt_max == cnt_g)
return Light::TL_COLOR::Green;
else
return Light::TL_COLOR::Yellow;
return Light::TL_COLOR::Null;
}
//整合的识别函数
Light::TL_COLOR TrafficLight::Reco(cv::Mat &img)
{
LOGI("Begin Traffic Light");
img = GetLight(img);
if (img.empty())
return Light::TL_COLOR::Null;
LOGI("Traffic Light Finished");
cv::imwrite("/sdcard/MainCar/tlr_finished.jpg", img);
return GetImageColor(img, 1.3);
}
};
//导出的识别函数
extern "C" JNIEXPORT
jint JNICALL
Java_com_uns_maincar_cpp_1interface_TrafficLight_Recognize(JNIEnv *env, jclass _this, jobject image)
@@ -242,6 +129,7 @@ Java_com_uns_maincar_cpp_1interface_TrafficLight_Recognize(JNIEnv *env, jclass _
cv::Mat img;
if(!BitmapToMat(env,image,img))
return 4;
cv::imwrite("/sdcard/MainCar/red.jpg", img);
uns::TrafficLight traffic_light;
switch(traffic_light.Reco(img))
{
@@ -257,6 +145,7 @@ Java_com_uns_maincar_cpp_1interface_TrafficLight_Recognize(JNIEnv *env, jclass _
return 5;
}
//导出的自检函数
extern "C" JNIEXPORT
jstring JNICALL
Java_com_uns_maincar_cpp_1interface_EnvTest_TrafficLightTest(JNIEnv *env, jclass _this)
+11 -12
View File
@@ -6,12 +6,15 @@
#ifndef MAINCAR_TRAFFIC_LIGHT_H
#define MAINCAR_TRAFFIC_LIGHT_H
//交通灯识别
#include <jni.h>
#include <cmath>
#include <string>
#include <iostream>
#include "public_types.h"
#include "opencv_support.h"
#include "debug_logger.h"
#include <opencv2/highgui.hpp>
#define TRAFFIC_LIGHT_RECO_VERSION "1.0.0"
@@ -21,20 +24,16 @@ namespace uns
class TrafficLight
{
private:
int GetCircle(cv::Mat& img);
bool CheckRGB(cv::Vec3b point);
int tri_max(int a, int b, int c);
void ImageProcess(cv::Mat& image);
bool ApproximatelyEqual(int a, int b, int offset);
void CutImage(cv::Mat& image,int radius,cv::Point center);
void CountMax(cv::Vec3b data, int& rmax, int& gmax, int& bmax);
private:
cv::Mat GetLight(const cv::Mat &img);
cv::Rect GetMaxRect(const cv::Mat &img);
bool MuchLarger(int a, int b, double rate);
void CountColor(const cv::Mat& img, double rate, int& r_cnt, int& g_cnt, int& y_cnt);
cv::Mat SplitImage(const cv::Mat& img);
Light::TL_COLOR GetImageColor(const cv::Mat& img, double rate);
public:
Light::TL_COLOR GetColor(cv::Mat &image, int offset);
Light::TL_COLOR GetImageColor(const cv::Mat &img, double rate);
public:
Light::TL_COLOR Reco(cv::Mat &img);
};
+10
View File
@@ -3,11 +3,14 @@
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
//
//交通标志识别
#include "traffic_sign.h"
#include "debug_logger.h"
namespace uns
{
//统计有效的像素数
int TrafficSign::PixCount(cv::Mat image)
{
int count = 0;
@@ -25,6 +28,7 @@ namespace uns
return -1;
}
//获取图片的ROI区域
bool TrafficSign::GetImageROI(const cv::Mat& src, cv::Mat& roi_image)
{
cv::Mat gray;
@@ -61,6 +65,7 @@ namespace uns
return true;
}
//读取存储的模板图片
bool TrafficSign::Read_Data(std::string filename, std::vector<cv::Mat>& dataset)
{
dataset.clear();
@@ -78,6 +83,7 @@ namespace uns
return (dataset.size() == 6);
}
//读取模板图片,匹配识别
int TrafficSign::SignRecognition(const cv::Mat& roi_image, const std::vector<cv::Mat>& dataset)
{
int index = 6;
@@ -108,6 +114,7 @@ namespace uns
return index;
}
//整合的交通标志
int TrafficSign::RecognitionSign(const cv::Mat& source)
{
std::vector<cv::Mat> dataset;
@@ -134,6 +141,7 @@ namespace uns
return SignRecognition(src, dataset);
}
//设置外部存储的路径
void TrafficSign::SetExternalImagePath(std::string path)
{
external_image_storage = path;
@@ -141,6 +149,7 @@ namespace uns
};
//导出的自检函数
extern "C" JNIEXPORT
jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_TrafficSignTest(JNIEnv *env, jclass _this)
{
@@ -148,6 +157,7 @@ jstring JNICALL Java_com_uns_maincar_cpp_1interface_EnvTest_TrafficSignTest(JNIE
return env->NewStringUTF(version.c_str());
}
//导出的识别函数
extern "C" JNIEXPORT
jint JNICALL Java_com_uns_maincar_cpp_1interface_TrafficSign_RecognizeSign(JNIEnv *env, jclass _this, jobject image, jstring external_path)
{
+2
View File
@@ -6,6 +6,8 @@
#ifndef MAINCAR_TRAFFIC_SIGN_H
#define MAINCAR_TRAFFIC_SIGN_H
//交通标志识别
#define TRAFFIC_SIGN_RECO_VERSION "1.0.0"
#include <map>
@@ -7,6 +7,7 @@ package com.uns.maincar.communication;
import com.uns.maincar.constants.Commands;
//指令解析类,解析接收到的指令并验证校验码和帧头帧尾
public class CommandDecoder
{
private byte main_command;
@@ -7,6 +7,7 @@ package com.uns.maincar.communication;
import com.uns.maincar.constants.Commands;
//指令编码类,生成带有标准帧头帧尾和校验码的指令
public class CommandEncoder
{
private final byte[] cmd = new byte[8];
@@ -9,7 +9,6 @@ package com.uns.maincar.communication;
* @apiNote 此接口为通用数据传输类接口,请慎重更改
* @implSpec 此接口应仅具有两个实现类,分别为Wifi及串口通信
*/
public interface DataTransferCore extends Runnable
{
@Override
@@ -23,8 +22,13 @@ public interface DataTransferCore extends Runnable
//接收数据的线程函数
void ThreadReceive();
//发送指定数据
boolean Send(byte[] data);
//发送指定数据,接收不到wait指令(位于数据接收第三位)就再次发送,超时退出
boolean SendEx(byte[] data, byte wait, int timeout);
//在新线程中发送数据
void ThreadSend(byte[] data);
@@ -33,6 +37,7 @@ public interface DataTransferCore extends Runnable
//开启自动重连功能
void EnableAutoReconnect();
//关闭自动重连功能
void DisableAutoReconnect();
@@ -151,6 +151,12 @@ public class SerialPortTransferCore implements DataTransferCore
}
}
@Override
public boolean SendEx(byte[] data, byte wait, int timeout)
{
return false;
}
@Override
public void ThreadSend(byte[] data)
{
@@ -38,6 +38,10 @@ public class WifiTransferCore implements DataTransferCore
private boolean AutoReconnectFlag = false;
//数据接收线程运行标志位
private boolean DataReceivingFlag = false;
//SendEx() 使用的数据回传
private boolean SendExDataBack = false;
//SendEx() 使用的回传数据接收
private byte[] cb_data;
public WifiTransferCore(String IP, int port, Handler data_handler)
{
@@ -46,13 +50,24 @@ public class WifiTransferCore implements DataTransferCore
this.handler = data_handler;
}
private void WifiSleep(int ms)
{
try
{
Thread.sleep(ms);
}
catch (InterruptedException ignored)
{
}
}
@Override
public boolean Connect()
{
try
{
socket = new Socket(IP, port);
if(!socket.isClosed())
if (!socket.isClosed())
{
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
@@ -83,7 +98,10 @@ public class WifiTransferCore implements DataTransferCore
if (dis.read(data, 0, data.length) != 0)
{
Log.i(Flags.CLIENT_TAG, "Wifi Socket Received.");
Message.obtain(handler, Flags.RECEIVED_CAR_DATA, data).sendToTarget();
if (!SendExDataBack)
Message.obtain(handler, Flags.RECEIVED_CAR_DATA, data).sendToTarget();
else
cb_data = data;
}
}
catch (IOException ignored)
@@ -110,6 +128,70 @@ public class WifiTransferCore implements DataTransferCore
}
catch (IOException | NullPointerException e)
{
try
{
socket.close();
}
catch (IOException ignored)
{
}
return false;
}
}
@Override
public boolean SendEx(byte[] data, byte wait, int timeout)
{
try
{
if ((socket != null) && (!socket.isClosed()))
{
dos.write(data, 0, data.length);
dos.flush();
Message.obtain(handler, Flags.PRINT_DATA_ARRAY, data).sendToTarget();
SendExDataBack = true;
cb_data = null;
int current_wait_time = 0;
while (SendExDataBack)
{
WifiSleep(10);
current_wait_time += 10;
if (current_wait_time >= timeout)
{
SendExDataBack = false;
return false;
}
if (cb_data != null)
{
CommandDecoder decoder = new CommandDecoder(cb_data);
if (decoder.GetMainCommand() != wait)
{
cb_data = null;
//重发
dos.write(data, 0, data.length);
dos.flush();
Message.obtain(handler, Flags.PRINT_DATA_ARRAY, data).sendToTarget();
}
else
{
SendExDataBack = false;
return true;
}
}
}
}
SendExDataBack = false;
return false;
}
catch (IOException | NullPointerException e)
{
try
{
socket.close();
}
catch (IOException ignored)
{
}
return false;
}
}
@@ -5,32 +5,43 @@
package com.uns.maincar.constants;
//关于指令的常量值
public class Commands
{
//帧头/帧尾
public static byte FRAME_HEAD_0 = (byte) 0x55;
public static byte FRAME_HEAD_1 = (byte) 0xAA;
public static byte FRAME_END = (byte) 0xBB;
//指令校验失败
public static byte CMD_NOT_MATCH = (byte) 0xEE;
//系统自检状态
public static byte STATUS_SUCCESS = (byte) 0xA1;
public static byte STATUS_FAILED = (byte) 0xB1;
//二维码
public static byte QR_SUCCESS_1 = (byte) 0xA2;
public static byte QR_SUCCESS_2 = (byte) 0xC2;
public static byte QR_FAILED = (byte) 0xB2;
//交通灯
public static byte TRAFFIC_LIGHT_SUCCESS = (byte) 0xA3;
public static byte TRAFFIC_LIGHT_FAILED = (byte) 0xB3;
public static byte TRAFFIC_LIGHT_RED = (byte) 0x01;
public static byte TRAFFIC_LIGHT_GREEN = (byte) 0x02;
public static byte TRAFFIC_LIGHT_YELLOW = (byte) 0x03;
//车牌
public static byte CAR_ID_SUCCESS_FIRST = (byte) 0xA4;
public static byte CAR_ID_SUCCESS_SECOND = (byte) 0xA5;
public static byte CAR_ID_FAILED = (byte) 0xB4;
//形状颜色
public static byte COLOR_SHAPE_SUCCESS = (byte) 0xA6;
public static byte COLOR_SHAPE_FAILED = (byte) 0xB6;
//交通标志
public static byte TRAFFIC_SIGN_SUCCESS = (byte) 0xA7;
public static byte TRAFFIC_SIGN_FAILED = (byte) 0xB7;
public static byte TRAFFIC_SIGN_TYPE_NO_ENTRY = (byte) 0x06;
@@ -40,26 +51,32 @@ public class Commands
public static byte TRAFFIC_SIGN_TYPE_TURN_RIGHT = (byte) 0x03;
public static byte TRAFFIC_SIGN_TYPE_U_TURN = (byte) 0x04;
//TFT显示器下翻一页
public static byte TFT_PAGE_DOWN = (byte) 0xA8;
//OCR(文本识别)
public static byte OCR_TEXT_SUCCESS = (byte) 0xA9;
public static byte OCR_TEXT_FAILED = (byte) 0xB9;
public static byte OCR_TEXT_LENGTH = (byte) 0xC9;
public static byte OCR_TEXT_DATA = (byte) 0xD9;
public static byte OCR_TEXT_FINISH = (byte) 0xE9;
//全自动模式
public static final byte RECEIVE_FULL_AUTO = (byte) 0xA0;
//摄像头预设位置
public static final byte RECEIVE_CAMERA_POS = (byte) 0xA1;
public static final byte RECEIVE_CAMERA_POS1 = 0x01;
public static final byte RECEIVE_CAMERA_POS2 = 0x02;
public static final byte RECEIVE_CAMERA_POS3 = 0x03;
public static final byte RECEIVE_CAMERA_POS4 = 0x04;
//全自动模式使用的接收指令
public static final byte RECEIVE_QR = (byte) 0xA2;
public static final byte RECEIVE_TRAFFIC_LIGHT = (byte) 0xA3;
public static final byte RECEIVE_CAR_ID = (byte) 0xA4;
public static final byte RECEIVE_SHAPE_COLOR = (byte) 0xA5;
public static final byte RECEIVE_TRAFFIC_SIGN = (byte) 0xA6;
public static final byte RECEIVE_TEXT_OCR = (byte) 0xA7;
public static final byte RECEIVE_OCR_DATA_OK = (byte) 0xB7;
}
@@ -5,6 +5,7 @@ package com.uns.maincar.constants;
* Modified by UnknownObject at 2022-09-18
*/
//一些其他的常量值
public class Flags
{
@@ -5,6 +5,7 @@
package com.uns.maincar.constants;
//颜色的枚举类型
public enum GlobalColor
{
RED,
@@ -5,6 +5,7 @@
package com.uns.maincar.constants;
//形状的枚举类型
public enum GlobalShape
{
STAR,
@@ -5,6 +5,7 @@
package com.uns.maincar.constants;
//交通标志的枚举类型
public enum GlobalSignType
{
NoEntry,
@@ -5,26 +5,41 @@
package com.uns.maincar.cpp_interface;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Environment;
import com.uns.maincar.cpp_interface.hyperlpr.DeepAssetUtil;
import com.uns.maincar.cpp_interface.hyperlpr.PlateRecognition;
import com.uns.maincar.tools.TextFilter;
import java.io.File;
//车牌识别类,提供三种不同的识别方式
public class CarLicense
{
static
{
System.loadLibrary("car_license_reco");
System.loadLibrary("car_license_reco_ocr");
}
private static long DAU_Resource_Address = 0;
public static class Result
{
boolean empty;
boolean success;
byte[] chars = new byte[6];
public Result(String cpp_result)
{
if(cpp_result.length() < 6)
if ((cpp_result == null) || cpp_result.equals(""))
{
empty = true;
success = false;
}
else if (cpp_result.length() < 6)
{
chars[0] = 0;
chars[1] = 0;
@@ -32,6 +47,7 @@ public class CarLicense
chars[3] = 0;
chars[4] = 0;
chars[5] = 0;
empty = false;
success = false;
}
else if(cpp_result.length() == 6)
@@ -42,6 +58,7 @@ public class CarLicense
chars[3] = (byte) cpp_result.charAt(3);
chars[4] = (byte) cpp_result.charAt(4);
chars[5] = (byte) cpp_result.charAt(5);
empty = false;
success = true;
}
else
@@ -53,6 +70,7 @@ public class CarLicense
chars[3] = (byte) sub_str.charAt(3);
chars[4] = (byte) sub_str.charAt(4);
chars[5] = (byte) sub_str.charAt(5);
empty = false;
success = true;
}
}
@@ -62,6 +80,11 @@ public class CarLicense
return success;
}
public boolean isEmpty()
{
return empty;
}
public byte[] GetFirstThreeBits()
{
return new byte[]{chars[0], chars[1], chars[2]};
@@ -75,9 +98,37 @@ public class CarLicense
private static native String RecognizeLicense(Bitmap image, String external_path);
private static native Bitmap RecognizeLicenseOCR(Bitmap image);
//使用模板匹配识别车牌
public static Result Recognize(Bitmap image)
{
String path = Environment.getExternalStorageDirectory().getPath() + File.separator + "MainCar" + File.separator + "Standard_Car_License_Image" + File.separator;
return new Result(RecognizeLicense(image, path));
}
//使用OCR识别车牌
public static Result RecognizeByOCR(Bitmap image)
{
Bitmap bmp = RecognizeLicenseOCR(image);
String str_result;
if (bmp == null)
str_result = "";
else
{
TextFilter filter = new TextFilter();
str_result = filter.LetterAndNumber(OCR.SimpleOCR(bmp));
}
return new Result(str_result);
}
//使用HyperLPR库识别车牌
public static Result RecognizeByAI(Bitmap image, Context context)
{
if (DAU_Resource_Address == 0)
DAU_Resource_Address = DeepAssetUtil.initRecognizer(context);
String result = PlateRecognition.EasyRecognization(image, DAU_Resource_Address);
TextFilter filter = new TextFilter();
return new Result(filter.LetterAndNumber(result));
}
}
@@ -5,6 +5,7 @@
package com.uns.maincar.cpp_interface;
//原生库环境检测类
public class EnvTest
{
static
@@ -19,15 +20,25 @@ public class EnvTest
}
private static native String NDKTest();
private static native String OpenCVTest();
private static native String TrafficLightTest();
private static native String ColorRecoTest();
private static native String ShapeRecoTest();
private static native String ImageProcessorTest();
private static native String ShapeColorRecoTest();
private static native String CarLicenseTest();
private static native String TrafficSignTest();
private static native String MainCarAESTest();
private static native String OCRTextTest();
public static String TestNDK()
@@ -7,6 +7,7 @@ package com.uns.maincar.cpp_interface;
import org.jetbrains.annotations.NotNull;
//数据处理算法类,算法实现在原生库中。
public class MainCarAES
{
static
@@ -15,6 +15,7 @@ import com.uns.maincar.R;
import java.io.File;
//静态文本识别
public class OCR
{
static
@@ -22,19 +23,39 @@ public class OCR
System.loadLibrary("ocr_text");
}
private static native Bitmap ProcessImage(Bitmap image);
private static native Bitmap ProcessImage(Bitmap image, boolean self_test);
//自检
public static String SelfTest(Context context)
{
return DecodeImage(BitmapFactory.decodeResource(context.getResources(), R.drawable.ocr_self_test));
TessBaseAPI tessBaseApi = new TessBaseAPI();
String path = Environment.getExternalStorageDirectory().getPath() + File.separator + "MainCar" + File.separator + "OCR" + File.separator;
tessBaseApi.init(path, "chi_sim");
tessBaseApi.setImage(ProcessImage(BitmapFactory.decodeResource(context.getResources(), R.drawable.ocr_self_test), true));
String extractedText = tessBaseApi.getUTF8Text();
tessBaseApi.end();
return extractedText;
}
//静态标志物OCR识别
public static String DecodeImage(Bitmap bitmap)
{
TessBaseAPI tessBaseApi = new TessBaseAPI();
String path = Environment.getExternalStorageDirectory().getPath() + File.separator + "MainCar" + File.separator + "OCR" + File.separator;
tessBaseApi.init(path, "chi_sim");
tessBaseApi.setImage(ProcessImage(bitmap));
tessBaseApi.setImage(ProcessImage(bitmap, false));
String extractedText = tessBaseApi.getUTF8Text();
tessBaseApi.end();
return extractedText;
}
//用于车牌OCR的接口
public static String SimpleOCR(Bitmap bitmap)
{
TessBaseAPI tessBaseApi = new TessBaseAPI();
String path = Environment.getExternalStorageDirectory().getPath() + File.separator + "MainCar" + File.separator + "OCR" + File.separator;
tessBaseApi.init(path, "chi_sim");
tessBaseApi.setImage(bitmap);
String extractedText = tessBaseApi.getUTF8Text();
tessBaseApi.end();
return extractedText;
@@ -10,7 +10,7 @@ import android.graphics.Rect;
import com.zxingcpp.BarcodeReader;
//二维码识别
public class QRDecoder
{
static
@@ -10,6 +10,7 @@ import android.graphics.Bitmap;
import com.uns.maincar.constants.GlobalColor;
import com.uns.maincar.constants.GlobalShape;
//形状颜色识别
public class ShapeColor
{
static
@@ -18,6 +19,7 @@ public class ShapeColor
}
private static native boolean RecoEverything(Bitmap image, double rate);
private static native int LookupRecoResult(int shape, String color);
private static int TranslateShape(GlobalShape shape)
@@ -9,6 +9,7 @@ import android.graphics.Bitmap;
import com.uns.maincar.constants.Flags.TrafficLightColors;
//交通灯识别
public class TrafficLight
{
static
@@ -13,6 +13,7 @@ import com.uns.maincar.constants.GlobalSignType;
import java.io.File;
//交通标志识别
public class TrafficSign
{
static
@@ -0,0 +1,134 @@
/*
* Copyright (c) 2022. UnknownNetworkService Group
* This file is created by UnknownObject at 2022 - 11 - 13
*/
package com.uns.maincar.cpp_interface.hyperlpr;
import android.content.Context;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
//HyperLPR——识别资源类——第三方开源库请勿改动
public class DeepAssetUtil
{
public static final String ApplicationDir = "lpr";
public static final String CASCADE_FILENAME = "cascade.xml";
public static final String FINEMAPPING_PROTOTXT = "HorizonalFinemapping.prototxt";
public static final String FINEMAPPING_CAFFEMODEL = "HorizonalFinemapping.caffemodel";
public static final String SEGMENTATION_PROTOTXT = "Segmentation.prototxt";
public static final String SEGMENTATION_CAFFEMODEL = "Segmentation.caffemodel";
public static final String RECOGNIZATION_PROTOTXT = "CharacterRecognization.prototxt";
public static final String RECOGNIZATION_CAFFEMODEL = "CharacterRecognization.caffemodel";
public static final String FREE_INCEPTION_PROTOTXT = "SegmenationFree-Inception.prototxt";
public static final String FREE_INCEPTION_CAFFEMODEL = "SegmenationFree-Inception.caffemodel";
public static final String SDCARD_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ApplicationDir; //解压文件存放位置
private static void CopyAssets(Context context, String assetDir, String dir)
{
String[] files;
try
{
// 获得Assets一共有几多文件
files = context.getAssets().list(assetDir);
}
catch (IOException e1)
{
return;
}
File mWorkingPath = new File(dir);
// 如果文件路径不存在
if (!mWorkingPath.exists())
{
// 创建文件夹
if (!mWorkingPath.mkdirs())
{
// 文件夹创建不成功时调用
}
}
for (String file : files)
{
try
{
// 根据路径判断是文件夹还是文件
if (!file.contains("."))
{
if (0 == assetDir.length())
{
CopyAssets(context, file, dir + file + "/");
}
else
{
CopyAssets(context, assetDir + "/" + file, dir + "/" + file + "/");
}
continue;
}
File outFile = new File(mWorkingPath, file);
if (outFile.exists())
continue;
InputStream in;
if (0 != assetDir.length())
{
in = context.getAssets().open(assetDir + "/" + file);
}
else
{
in = context.getAssets().open(file);
}
OutputStream out = new FileOutputStream(outFile);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
{
out.write(buf, 0, len);
}
in.close();
out.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
private static void copyFilesFromAssets(Context context)
{
DeepAssetUtil.CopyAssets(context, ApplicationDir, SDCARD_DIR);
}
//初始化识别资源
public static long initRecognizer(Context context)
{
String cascade_filename = SDCARD_DIR + File.separator + CASCADE_FILENAME;
String finemapping_prototxt = SDCARD_DIR + File.separator + FINEMAPPING_PROTOTXT;
String finemapping_caffemodel = SDCARD_DIR + File.separator + FINEMAPPING_CAFFEMODEL;
String segmentation_prototxt = SDCARD_DIR + File.separator + SEGMENTATION_PROTOTXT;
String segmentation_caffemodel = SDCARD_DIR + File.separator + SEGMENTATION_CAFFEMODEL;
String character_prototxt = SDCARD_DIR + File.separator + RECOGNIZATION_PROTOTXT;
String character_caffemodel = SDCARD_DIR + File.separator + RECOGNIZATION_CAFFEMODEL;
String segmentation_free_prototxt = SDCARD_DIR + File.separator + FREE_INCEPTION_PROTOTXT;
String segmentation_free_caffemodel = SDCARD_DIR + File.separator + FREE_INCEPTION_CAFFEMODEL;
copyFilesFromAssets(context);
//调用JNI 加载资源函数
return PlateRecognition.InitPlateRecognizer(
cascade_filename,
finemapping_prototxt, finemapping_caffemodel,
segmentation_prototxt, segmentation_caffemodel,
character_prototxt, character_caffemodel,
segmentation_free_prototxt, segmentation_free_caffemodel);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022. UnknownNetworkService Group
* This file is created by UnknownObject at 2022 - 11 - 13
*/
package com.uns.maincar.cpp_interface.hyperlpr;
import android.graphics.Bitmap;
//HyperLPR——原生库(C++)文件封装
public class PlateRecognition
{
static
{
System.loadLibrary("lib_hyper_lpr");
}
static native long InitPlateRecognizer(String casacde_detection,
String finemapping_prototxt, String finemapping_caffemodel,
String segmentation_prototxt, String segmentation_caffemodel,
String charRecognization_proto, String charRecognization_caffemodel,
String segmentation_free_prototxt, String segmentation_free_caffemodel);
static native void ReleasePlateRecognizer(long object);
public static native String SimpleRecognization(long inputMat, long object);
public static native String EasyRecognization(Bitmap image, long object);
}
@@ -48,6 +48,7 @@ import com.uns.maincar.cpp_interface.TrafficLight;
import com.uns.maincar.cpp_interface.TrafficSign;
import com.uns.maincar.tools.ImageReleaser;
import com.uns.maincar.tools.OCRDataReleaser;
import com.uns.maincar.tools.TextFilter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
@@ -106,105 +107,144 @@ public class MainActivity extends AppCompatActivity
"0xE0", "0xE1", "0xE2", "0xE3", "0xE4", "0xE5", "0xE6", "0xE7", "0xE8", "0xE9", "0xEA", "0xEB", "0xEC", "0xED", "0xEE", "0xEF",
"0xF0", "0xF1", "0xF2", "0xF3", "0xF4", "0xF5", "0xF6", "0xF7", "0xF8", "0xF9", "0xFA", "0xFB", "0xFC", "0xFD", "0xFE", "0xFF"};
static
{
System.loadLibrary("opencv_support");
}
//原生函数导入,用于长按保存的功能
private static native boolean SaveImage(Bitmap img, String time);
@SuppressLint("HandlerLeak")
public MainActivity()
{
//接收内部消息的处理器,用于处理内部消息
recvHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
//处理接收到的图片
//内部消息:收到图片;执行操作:更新GUI上的图片
if (msg.what == Flags.RECEIVED_IMAGE)
pic_received.setImageBitmap(currImage);
//处理接收到的指令
//内部消息:收到主车数据;执行操作:解析指令并执行
if (msg.what == Flags.RECEIVED_CAR_DATA)
{
byte[] recv = (byte[]) msg.obj;
if (recv != null)
{
//打印接收到的指令
ToastLog("RECV: [" + ByteArray2String(recv) + "]", true, false);
//解析指令
CommandDecoder decoder = new CommandDecoder(recv);
if (decoder.CommandReady())
{
ToastLog("Command Decode Ready.", false, false);
Thread th_run_command = new Thread(() -> {
Thread th_run_command = new Thread(() ->
{
switch (decoder.GetMainCommand())
{
//收到全自动指令,返回程序自检状态
case Commands.RECEIVE_FULL_AUTO:
dtc_client.Send(SystemStatusCommand());
break;
//收到QR指令,开始识别二维码,回传识别成功的数据
case Commands.RECEIVE_QR:
byte[] cmd = RecognizeQrCode();
CommandEncoder encoder = new CommandEncoder();
dtc_client.Send(encoder.GenerateCommand(Commands.QR_SUCCESS_1, cmd[0], cmd[1], cmd[2]));
Sleep(500);
dtc_client.Send(encoder.GenerateCommand(Commands.QR_SUCCESS_2, cmd[3], cmd[4], cmd[5]));
if (cmd[2] == Commands.QR_FAILED)
dtc_client.Send(cmd);
else
{
CommandEncoder encoder = new CommandEncoder();
dtc_client.Send(encoder.GenerateCommand(Commands.QR_SUCCESS_1, cmd[0], cmd[1], cmd[2]));
Sleep(500);
dtc_client.Send(encoder.GenerateCommand(Commands.QR_SUCCESS_2, cmd[3], cmd[4], cmd[5]));
}
break;
//收到TRAFFIC_LIGHT指令,开始识别交通灯,回传识别成功的数据
case Commands.RECEIVE_TRAFFIC_LIGHT:
dtc_client.Send(RecognizeTrafficLight());
break;
//收到SHAPE_COLOR指令,开始识别形状颜色,回传识别成功的数据
case Commands.RECEIVE_SHAPE_COLOR:
dtc_client.Send(RecognizeShapeColor());
break;
//收到CAR_ID指令,开始识别车牌号,回传识别成功的数据
case Commands.RECEIVE_CAR_ID:
RecognizeCarID();
break;
//收到TRAFFIC_SIGN指令,开始识别交通标志,回传识别成功的数据
case Commands.RECEIVE_TRAFFIC_SIGN:
dtc_client.Send(RecognizeTrafficSign());
break;
//收到OCR指令,开始识别文本,回传识别成功的数据
case Commands.RECEIVE_TEXT_OCR:
OCRRecognizeText();
break;
//收到未知指令,回传异常指令,表示无法解析当前指令
default:
CommandEncoder error = new CommandEncoder();
dtc_client.Send(error.GenerateCommand(Commands.CMD_NOT_MATCH, (byte) 0x00, (byte) 0x00, (byte) 0x00));
break;
}
});
th_run_command.start();
}
//指令解析失败,回传异常指令,表示无法解析当前指令
else
{
CommandEncoder error = new CommandEncoder();
dtc_client.ThreadSend(error.GenerateCommand(Commands.CMD_NOT_MATCH, (byte) 0x00, (byte) 0x00, (byte) 0x00));
}
}
//收到NULL,输出日志,不做操作
else
ToastLog("NULL Received", true, false);
}
//接收到打印数组的指令,打印数组
//内部消息:打印数组;操作:打印收到的数组
if (msg.what == Flags.PRINT_DATA_ARRAY)
{
byte[] data = (byte[]) msg.obj;
if (data != null)
ToastLog("SEND: [" + ByteArray2String(data) + "]", false, false);
}
//接收到打印日志的指令,打印日志
if(msg.what == Flags.PRINT_SYSTEM_LOG)
//内部消息:打印日志;操作:打印收到的日志
if (msg.what == Flags.PRINT_SYSTEM_LOG)
{
String str = (String)msg.obj;
if(str != null)
String str = (String) msg.obj;
if (str != null)
ToastLog(str, false, false);
}
}
};
}
//处理二维码数据,使用从C++代码中导出的算法
private byte[] ProcessQRData(ArrayList<String> qr_data)
{
return MainCarAES.CalcAES(qr_data.get(0));
}
//获取程序自检指令,根据自检状态返回成功或失败
private byte[] SystemStatusCommand()
{
CommandEncoder encoder = new CommandEncoder();
if(SystemStatus && FileStatus)
if (SystemStatus && FileStatus)
return encoder.GenerateCommand(Commands.STATUS_SUCCESS, (byte) 0, (byte) 0, (byte) 0);
else
return encoder.GenerateCommand(Commands.STATUS_FAILED, (byte) 0, (byte) 0, (byte) 0);
}
//识别二维码
private byte[] RecognizeQrCode()
{
CommandEncoder encoder = new CommandEncoder();
ArrayList<String> qr_result = new ArrayList<>();
if(!QRDecoder.BeginQRDecode(currImage))
if (!QRDecoder.BeginQRDecode(currImage))
return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0);
while(QRDecoder.HasNextCode())
while (QRDecoder.HasNextCode())
qr_result.add(QRDecoder.DecodeNextQR());
if (qr_result.size() <= 0)
return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0);
@@ -215,6 +255,7 @@ public class MainActivity extends AppCompatActivity
}
}
//识别交通灯
private byte[] RecognizeTrafficLight()
{
TrafficLightColors color = TrafficLight.RecognizeTrafficLight(currImage);
@@ -233,6 +274,7 @@ public class MainActivity extends AppCompatActivity
return encoder.GenerateCommand(Commands.TRAFFIC_LIGHT_FAILED, (byte) -1, (byte) -1, (byte) -1);
}
//识别形状颜色
private byte[] RecognizeShapeColor()
{
CommandEncoder encoder = new CommandEncoder();
@@ -241,19 +283,87 @@ public class MainActivity extends AppCompatActivity
else
{
byte a = 0, b = 0, c = 0;
//Add Value to Lookup
//测试用输出
/*ToastLog("C-R-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.RED), false, false);
ToastLog("C-BL-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.BLACK), false, false);
ToastLog("C-G-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.GREEN), false, false);
ToastLog("C-BU-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.BLUE), false, false);
ToastLog("C-C-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.CYAN), false, false);
ToastLog("C-P-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.PURPLE), false, false);
ToastLog("C-W-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.WHITE), false, false);
ToastLog("C-Y-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.YELLOW), false, false);
ToastLog("S-R-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.RED), false, false);
ToastLog("S-BL-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.BLACK), false, false);
ToastLog("S-G-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.GREEN), false, false);
ToastLog("S-BU-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.BLUE), false, false);
ToastLog("S-C-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.CYAN), false, false);
ToastLog("S-P-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.PURPLE), false, false);
ToastLog("S-W-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.WHITE), false, false);
ToastLog("S-Y-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.YELLOW), false, false);
ToastLog("s-R-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.RED), false, false);
ToastLog("s-BL-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.BLACK), false, false);
ToastLog("s-G-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.GREEN), false, false);
ToastLog("s-BU-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.BLUE), false, false);
ToastLog("s-C-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.CYAN), false, false);
ToastLog("s-P-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.PURPLE), false, false);
ToastLog("s-W-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.WHITE), false, false);
ToastLog("s-Y-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.YELLOW), false, false);
ToastLog("R-R-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.RED), false, false);
ToastLog("R-BL-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.BLACK), false, false);
ToastLog("R-G-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.GREEN), false, false);
ToastLog("R-BU-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.BLUE), false, false);
ToastLog("R-C-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.CYAN), false, false);
ToastLog("R-P-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.PURPLE), false, false);
ToastLog("R-W-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.WHITE), false, false);
ToastLog("R-Y-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.YELLOW), false, false);
ToastLog("T-R-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.RED), false, false);
ToastLog("T-BL-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.BLACK), false, false);
ToastLog("T-G-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.GREEN), false, false);
ToastLog("T-BU-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.BLUE), false, false);
ToastLog("T-C-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.CYAN), false, false);
ToastLog("T-P-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.PURPLE), false, false);
ToastLog("T-W-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.WHITE), false, false);
ToastLog("T-Y-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.YELLOW), false, false);*/
return encoder.GenerateCommand(Commands.COLOR_SHAPE_SUCCESS, a, b, c);
}
}
//识别车牌
private void RecognizeCarID()
{
CommandEncoder encoder = new CommandEncoder();
CarLicense.Result result = CarLicense.Recognize(currImage);
if(!result.Success())
// CarLicense.Result result = CarLicense.Recognize(currImage); //通过模板匹配识别车牌
// CarLicense.Result result = CarLicense.RecognizeByOCR(currImage); //通过OCR识别车牌
CarLicense.Result result = CarLicense.RecognizeByAI(currImage, this); //通过AI模型识别车牌
if (!result.Success())
{
//OCR识别的自动重试功能
/*final int OCR_MAX_RETRY = 5;
int retry_time = 0;
do
{
result = CarLicense.RecognizeByOCR(currImage);
Sleep(100);
retry_time++;
}while (result.isEmpty() && (retry_time <= OCR_MAX_RETRY));
if(retry_time > OCR_MAX_RETRY)
dtc_client.Send(encoder.GenerateCommand(Commands.CAR_ID_FAILED, (byte) 0, (byte) 0, (byte) 0));
else
{
encoder.AddMainCommand(Commands.CAR_ID_SUCCESS_FIRST);
encoder.AddData(result.GetFirstThreeBits());
dtc_client.Send(encoder.GenerateCommand());
Sleep(500);
encoder.Clear();
encoder.AddMainCommand(Commands.CAR_ID_SUCCESS_SECOND);
encoder.AddData(result.GetLastThreeBits());
dtc_client.Send(encoder.GenerateCommand());
}*/
dtc_client.Send(encoder.GenerateCommand(Commands.CAR_ID_FAILED, (byte) 0, (byte) 0, (byte) 0));
}
else
{
//车牌信息分两次发送
encoder.AddMainCommand(Commands.CAR_ID_SUCCESS_FIRST);
encoder.AddData(result.GetFirstThreeBits());
dtc_client.Send(encoder.GenerateCommand());
@@ -265,11 +375,12 @@ public class MainActivity extends AppCompatActivity
}
}
//识别交通标志
private byte[] RecognizeTrafficSign()
{
CommandEncoder encoder = new CommandEncoder();
GlobalSignType type = TrafficSign.SignRecognize(currImage);
if(type == GlobalSignType.Failure)
if (type == GlobalSignType.Failure)
return encoder.GenerateCommand(Commands.TRAFFIC_SIGN_FAILED, (byte) 0, (byte) 0, (byte) 0);
else
{
@@ -299,10 +410,12 @@ public class MainActivity extends AppCompatActivity
}
}
//识别静态文本
private void OCRRecognizeText()
{
// String str = OCR.DecodeImage(currImage);
String str = OCR.SelfTest(this);
TextFilter filter = new TextFilter();
String str = filter.RemoveEmptyCharacter(OCR.DecodeImage(currImage));
ToastLog("OCR Result: [" + str + "]", false, true);
byte[] b_str;
try
{
@@ -317,18 +430,23 @@ public class MainActivity extends AppCompatActivity
}
CommandEncoder encoder = new CommandEncoder();
dtc_client.Send(encoder.GenerateCommand(Commands.OCR_TEXT_SUCCESS, (byte) 0x00, (byte) 0x00, (byte) 0x00));
Sleep(500);
Sleep(900);
dtc_client.Send(encoder.GenerateCommand(Commands.OCR_TEXT_LENGTH, (byte) b_str.length, (byte) 0x00, (byte) b_str.length));
for (int i = 0; i < b_str.length; i += 2)
{
Sleep(500);
byte data_2 = ((i + 1) >= b_str.length ? 0x00 : b_str[i + 1]);
byte checksum = (byte) ((b_str[i] + data_2) % 0xFF);
byte main = ((i + 1) < b_str.length ? Commands.OCR_TEXT_DATA : Commands.OCR_TEXT_FINISH);
dtc_client.Send(encoder.GenerateCommand(main, b_str[i], data_2, checksum));
byte main = ((i + 2) < b_str.length ? Commands.OCR_TEXT_DATA : Commands.OCR_TEXT_FINISH);
if (!dtc_client.SendEx(encoder.GenerateCommand(main, b_str[i], data_2, checksum), Commands.RECEIVE_OCR_DATA_OK, 2000))
{
dtc_client.Send(encoder.GenerateCommand(Commands.OCR_TEXT_FAILED, (byte) 0x00, (byte) 0x00, (byte) 0x00));
break;
}
}
}
//数组转字符串,仅用于调试输出
private String ByteArray2String(byte[] arr)
{
StringBuilder msg_str = new StringBuilder();
@@ -337,6 +455,7 @@ public class MainActivity extends AppCompatActivity
return msg_str.toString();
}
//初始化图形界面
private void InitGUI()
{
pic_received = findViewById(R.id.camera_image);
@@ -376,7 +495,7 @@ public class MainActivity extends AppCompatActivity
findViewById(R.id.btn_send).setOnClickListener(view ->
{
Thread th_send = new Thread(() ->
try
{
byte cmd0 = (byte) Integer.parseInt(((EditText) findViewById(R.id.edit_cmd0)).getText().toString(), 16);
byte cmd1 = (byte) Integer.parseInt(((EditText) findViewById(R.id.edit_cmd1)).getText().toString(), 16);
@@ -384,64 +503,95 @@ public class MainActivity extends AppCompatActivity
byte cmd3 = (byte) Integer.parseInt(((EditText) findViewById(R.id.edit_cmd3)).getText().toString(), 16);
CommandEncoder encoder = new CommandEncoder();
byte[] cmd = encoder.GenerateCommand(cmd0, cmd1, cmd2, cmd3);
dtc_client.Send(cmd);
dtc_client.ThreadSend(cmd);
//print debug information
Message.obtain(recvHandler, Flags.PRINT_DATA_ARRAY, cmd).sendToTarget();
});
th_send.start();
}
catch (Exception e)
{
ToastLog("Input Data Invalidate", true, false);
}
});
findViewById(R.id.btn_start_qr).setOnClickListener(view -> {
findViewById(R.id.btn_start_qr).setOnClickListener(view ->
{
ToastLog("QR Code Started", false, false);
ToastLog("QR Result: " + ByteArray2String(RecognizeQrCode()), false, false);
});
findViewById(R.id.btn_start_light).setOnClickListener(view -> {
findViewById(R.id.btn_start_light).setOnClickListener(view ->
{
ToastLog("Traffic Light Started", false, false);
ToastLog("TL Result: " + ByteArray2String(RecognizeTrafficLight()), false, false);
});
findViewById(R.id.btn_start_color_shape).setOnClickListener(view -> {
findViewById(R.id.btn_start_color_shape).setOnClickListener(view ->
{
ToastLog("Color Shape Started", false, false);
ToastLog("CS Result: " + ByteArray2String(RecognizeShapeColor()), false, false);
});
findViewById(R.id.btn_start_car_id).setOnClickListener(view -> {
findViewById(R.id.btn_start_car_id).setOnClickListener(view ->
{
ToastLog("Car ID Started", false, false);
Thread th_debug = new Thread(this::RecognizeCarID);
th_debug.start();
ToastLog("CID Finished", false, false);
});
findViewById(R.id.btn_start_sign).setOnClickListener(view -> {
findViewById(R.id.btn_start_sign).setOnClickListener(view ->
{
ToastLog("Traffic Sign Started", false, false);
ToastLog("TS Result: " + ByteArray2String(RecognizeTrafficSign()), false, false);
});
findViewById(R.id.btn_start_ocr).setOnClickListener(view -> {
findViewById(R.id.btn_start_ocr).setOnClickListener(view ->
{
ToastLog("OCR Started", false, false);
/*ToastLog("OCR Result: " + OCR.DecodeImage(currImage), false, false);*/
Thread th_debug = new Thread(this::OCRRecognizeText);
th_debug.start();
});
findViewById(R.id.btn_tft_page_down).setOnClickListener(view -> {
findViewById(R.id.btn_tft_page_down).setOnClickListener(view ->
{
CommandEncoder encoder = new CommandEncoder();
dtc_client.ThreadSend(encoder.GenerateCommand(Commands.TFT_PAGE_DOWN, (byte) 0, (byte) 0, (byte) 0));
ToastLog("TFT Page Down Command Send.", false, true);
});
findViewById(R.id.btn_movement_control).setOnClickListener(view -> {
findViewById(R.id.btn_movement_control).setOnClickListener(view ->
{
startActivity(new Intent(this, MovementController.class));
});
findViewById(R.id.btn_crash).setOnClickListener(view -> {
findViewById(R.id.btn_crash).setOnClickListener(view ->
{
//崩溃按钮的作用:频繁调试时省去手动退出程序,清理后台的操作,节省时间。
dtc_client.CloseConnection(); //关闭通信
throw new NullPointerException(); //通过异常来崩溃。
dtc_client.DisableAutoReconnect();
dtc_client.CloseConnection(); //关闭通信
throw new NullPointerException(); //通过异常来崩溃。
});
findViewById(R.id.text_toast).setOnLongClickListener(view ->
{
ToastLog("Log Cleared", true, false);
((TextView) view).setText("");
return true;
});
findViewById(R.id.camera_image).setOnLongClickListener(view ->
{
String time = String.valueOf(System.currentTimeMillis());
if (SaveImage(currImage, time))
ToastLog("Image Saved", true, false);
else
ToastLog("Image Save Failure", true, false);
return true;
});
}
//打印日志
@SuppressLint("SetTextI18n")
private void ToastLog(String text, boolean real_toast, boolean on_thread)
{
@@ -450,12 +600,13 @@ public class MainActivity extends AppCompatActivity
if (real_toast)
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
Log.i("ToastBackup", text);
if(on_thread)
if (on_thread)
Message.obtain(recvHandler, Flags.PRINT_SYSTEM_LOG, text);
else
text_toast.setText(text_toast.getText().toString() + "\n" + text);
}
//等待一段时间
private void Sleep(long ms)
{
try
@@ -467,6 +618,7 @@ public class MainActivity extends AppCompatActivity
}
}
//启动寻找摄像头的线程
private void StartCameraImageUpdate(int duration)
{
Thread th_image = new Thread(() ->
@@ -508,10 +660,10 @@ public class MainActivity extends AppCompatActivity
ToastLog(EnvTest.TestMainCarAES(), false, false);
byte[] demo = MainCarAES.CalcAES("ABCDEFGHabcdefgh");
if(demo == null)
ToastLog("AES Status: Error",false,false);
if (demo == null)
ToastLog("AES Status: Error", false, false);
else
ToastLog("AES Status: " + ByteArray2String(demo),false,false);
ToastLog("AES Status: " + ByteArray2String(demo), false, false);
//二维码扫描自检
ToastLog(QRDecoder.SelfTest(BitmapFactory.decodeResource(getResources(), R.drawable.qr_decode_test)), false, false);
@@ -523,20 +675,21 @@ public class MainActivity extends AppCompatActivity
ToastLog("DHCP Server Address: " + IPCar, false, false);
//建立连接
if(CommunicationUsingWifi)
dtc_client = new WifiTransferCore(IPCar,60000, recvHandler);
if (CommunicationUsingWifi)
dtc_client = new WifiTransferCore(IPCar, 60000, recvHandler);
else
dtc_client = new SerialPortTransferCore(SerialPortPath, 115200, recvHandler);
Thread th_connect = new Thread(() -> {
if(dtc_client.Connect())
Thread th_connect = new Thread(() ->
{
if (dtc_client.Connect())
ToastLog("Client Connected", false, true);
else
ToastLog("Client Connect Failed", false, true);
});
th_connect.start();
while(th_connect.isAlive())
while (th_connect.isAlive())
Sleep(10); //Wait for Connection Thread
dtc_client.EnableAutoReconnect(); //启动自动重连
dtc_client.EnableAutoReconnect(); //启动自动重连
//初始化摄像头控制
cameraCommandUtil = new CameraCommandUtil();
@@ -577,17 +730,17 @@ public class MainActivity extends AppCompatActivity
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == permission_request_code)
if (requestCode == permission_request_code)
{
if(resultCode == RESULT_OK)
if (resultCode == RESULT_OK)
{
ImageReleaser releaser = new ImageReleaser(this);
OCRDataReleaser releaser_ocr = new OCRDataReleaser(this);
FileStatus = (releaser.ReleaseAllImage() && releaser_ocr.ReleaseAllFiles());
ToastLog(releaser.toString(), false, false);
ToastLog(releaser_ocr.toString(), false, false);
if(FileStatus)
ToastLog(OCR.SelfTest(this), false, false); //OCR光学字符识别自检
if (FileStatus)
ToastLog(OCR.SelfTest(this), false, false); //OCR光学字符识别自检
}
else
FileStatus = false;
@@ -598,6 +751,7 @@ public class MainActivity extends AppCompatActivity
protected void onDestroy()
{
super.onDestroy();
//Activity销毁时关闭通信
dtc_client.DisableAutoReconnect();
dtc_client.CloseConnection();
}
@@ -24,6 +24,8 @@ import com.uns.maincar.communication.WifiTransferCore;
import com.uns.maincar.constants.Commands;
import com.uns.maincar.constants.Flags;
//主车和从车的移动控制
//实现简单,不太稳定,仅供娱乐和调试用途
public class MovementController extends AppCompatActivity
{
@@ -126,9 +128,10 @@ public class MovementController extends AppCompatActivity
findViewById(R.id.rb_main_car).setOnClickListener(view -> cmd_data[1] = Flags.CMD_PACKET_MAIN_CAR);
findViewById(R.id.rb_main_car).setOnClickListener(view -> cmd_data[1] = Flags.CMD_PACKET_SUB_CAR);
findViewById(R.id.rb_sub_car).setOnClickListener(view -> cmd_data[1] = Flags.CMD_PACKET_SUB_CAR);
findViewById(R.id.btn_run_to_line).setOnClickListener(view -> {
findViewById(R.id.btn_front).setOnClickListener(view ->
{
cmd_data[2] = Flags.CMD_PACKET_MOVE_FORWARD;
cmd_data[3] = (byte) (GetRunSpeed() & 0xFF);
cmd_data[4] = (byte) (GetDistance() & 0xFF);
@@ -5,12 +5,6 @@
package com.uns.maincar.gui;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -20,8 +14,15 @@ import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.uns.maincar.R;
//Android的存储权限的获取
public class PermissionGetter extends AppCompatActivity
{
@@ -18,6 +18,7 @@ import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
//车牌和交通标志的模板图片释放
public class ImageReleaser
{
private final Context context;
@@ -18,6 +18,7 @@ import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
//OCR人工智能模型文件的释放
public class OCRDataReleaser
{
private final Context context;
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2022. UnknownNetworkService Group
* This file is created by UnknownObject at 2022 - 11 - 11
*/
package com.uns.maincar.tools;
import java.util.HashSet;
//文本过滤器,用于过滤OCR和车牌识别等结果中多余的字符
public class TextFilter
{
private final HashSet<Character> empty_char = new HashSet<>();
public TextFilter()
{
empty_char.add('\t');
empty_char.add('\r');
empty_char.add('\n');
empty_char.add(' ');
}
public String RemoveEmptyCharacter(String src)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < src.length(); i++)
{
if (!empty_char.contains(src.charAt(i)))
result.append(src.charAt(i));
}
return result.toString();
}
public String LetterAndNumber(String src)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < src.length(); i++)
{
char ch = src.charAt(i);
if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')) || ((ch >= '0') && (ch <= '9')))
result.append(ch);
}
return result.toString();
}
}
+1 -1
View File
@@ -267,7 +267,7 @@
<TextView
android:id="@+id/text_toast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:fadeScrollbars="false"
android:scrollbars="vertical"
@@ -5,7 +5,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -44,7 +43,7 @@
android:inputType="textPersonName"
android:minHeight="48dp"
android:numeric="integer"
android:text="5000"
android:text="500"
tools:ignore="TouchTargetSizeCheck" />
</LinearLayout>