1. add ocr license
2. add hyper-lpr function 3. add text filter 4. add commits
This commit is contained in:
Binary file not shown.
@@ -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"
|
||||
}
|
||||
Binary file not shown.
@@ -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"
|
||||
}
|
||||
Binary file not shown.
@@ -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
@@ -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})
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#define CAR_LICENSE_RECO_VERSION "1.0.0"
|
||||
|
||||
//基于模板匹配的车牌识别
|
||||
|
||||
#include <jni.h>
|
||||
#include <iostream>
|
||||
#include "public_types.h"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef MAINCAR_COLOR_RECO_H
|
||||
#define MAINCAR_COLOR_RECO_H
|
||||
|
||||
//颜色识别
|
||||
|
||||
#define COLOR_RECO_VERSION "1.0.0"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
|
||||
//
|
||||
|
||||
//NDK和OpenCV(C++)的自检
|
||||
|
||||
#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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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,6 +3,9 @@
|
||||
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
|
||||
//
|
||||
|
||||
//数据处理算法
|
||||
//这个算法不做额外说明,实际比赛中算法会发生变化
|
||||
|
||||
#include "main_car_aes.h"
|
||||
|
||||
namespace uns
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Copyright (c) 2022 UnknownNetworkService. All rights reserved.
|
||||
//
|
||||
|
||||
//图片预处理、形状颜色、交通标志、交通灯的类型定义
|
||||
|
||||
#include "public_types.h"
|
||||
|
||||
namespace uns
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef MAINCAR_PUBLIC_TYPES_H
|
||||
#define MAINCAR_PUBLIC_TYPES_H
|
||||
|
||||
//图片预处理、形状颜色、交通标志、交通灯的类型定义
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef MAINCAR_QR_CODE_DECODE_H
|
||||
#define MAINCAR_QR_CODE_DECODE_H
|
||||
|
||||
//二维码解析前图像处理
|
||||
|
||||
#include <jni.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef MAINCAR_SHAPE_RECO_H
|
||||
#define MAINCAR_SHAPE_RECO_H
|
||||
|
||||
//形状识别
|
||||
|
||||
#define SHAPE_RECO_VERSION "1.0.0"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user