A10-3588 License Plate Recognition: Model Conversion and NPU Execution
- willow1371
- Jun 3
- 4 min read
Introduction
This document provides a detailed guide on converting the License Plate Recognition model and performing inference on the A10 - 3588 NPU.
License Plate Recognition consists of four main parts.
Firstly, there is license plate detection. We utilize the YOLOv8n - pose algorithm to detect license plates.
Secondly, license plate localization is carried out. This step precisely determines the position of the detected license plate within the image.
Thirdly, character recognition is performed. The Densenet Ctc model is employed to recognize the characters on the license plate.
Finally, the VGG16 model is used to identify the color of the license plate.
Convert Model
Lpd(YOLOV8n-pose)
Before training, please modify as follows.
For YOLOv8-pose/nets/nn.py.
self.fpn = DarkFPN(width, depth) img_dummy = torch.zeros(1, 3, 256, 256) - self.head = Head(num_classes, (17, 3), (width[3], width[4], width[5])) + self.head = Head(num_classes, (4, 3), (width[3], width[4], width[5])) self.head.stride = torch.tensor([256 / x.shape[-2] for x in self.forwar d(img_dummy)[0]]) self.stride = self.head.stride self.head.initialize_biases()For YOLOv8-pose/utils/args.yaml.
flip_lr: 0.5000 # image flip left-right (probability) mosaic: 1.00000 # image mosaic (probability) mix_up: 0.00000 # image mix-up (probability)
-kpt_shape: [ 17, 3 ] # number of key-points, number of dims (2 for x,y or 3 for x,y,visible) +kpt_shape: [ 4, 3 ] # number of key-points, number of dims (2 for x,y or 3 for x,y,visible) -flip_index: [ 0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15 ] +flip_index: [ 0, 1, 2, 3 ] # classes names:
- 0: person + 0: license_plateFor YOLOv8-pose/utils/util.py.
-KPT_SIGMA = numpy.array([.26, .25, .25,
- .35, .35, .79,
- .79, .72, .72,
- .62, .62, 1.07,
- 1.07, .87, .87,
- .89, .89]) / 10.0 +KPT_SIGMA = numpy.array([0.25, 0.25, 0.25, 0.25])After training, modify YOLOv8-pose/nets/nn.py as follows.
def forward(self, x): x_det = self.detect_box(x) x_kpt = self.detect_kpt(x) if self.training: return x_det, x_kpt - return torch.cat([x_det, self.decode_kpt(x_kpt)], dim=1) + output = [] + print(x_det.shape) + print(x_kpt[0].shape) + output.append((torch.cat([x_det[:, :, :6400], x_kpt[0]], dim=1)+).view(1, 1, -1, 80, 80).permute(0, 1, 3, 4, 2)) + output.append((torch.cat([x_det[:, :, 6400:8000], x_kpt[1]], di+m=1)).view(1, 1, -1, 40, 40).permute(0, 1, 3, 4, 2)) + output.append((torch.cat([x_det[:, :, 8000:], x_kpt[2]], dim=1)+).view(1, 1, -1, 20, 20).permute(0, 1, 3, 4, 2)) + return output def detect_box(self, x): x_det = [] shape = x[0].shape[0] for i in range(self.nl): x_det.append(torch.cat((self.box[i](x[i]), self.cls[i](x[i])), dim=1 )) if self.training: return x_det self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x_det, self.stride, 0.5)) x_cat = torch.cat([i.view(shape, self.no, -1) for i in x_det], dim=2) + return x_cat box, cls = x_cat.split((self.ch * 4, self.nc), 1) a, b = self.dfl(box).chunk(2, 1) a = self.anchors.unsqueeze(0) - a b = self.anchors.unsqueeze(0) + b box = torch.cat(((a + b) / 2, b - a), dim=1) return torch.cat((box * self.strides, cls.sigmoid()), dim=1)def detect_kpt(self, x): x_kpt = [] shape = x[0].shape[0] for i in range(self.nl): kpt = self.kpt[i](x[i]) kpt = kpt.view(shape, self.num_kpt, -1) x_kpt.append(kpt)
- return torch.cat(x_kpt, dim=-1) + return x_kptCreate export.py and write in follow code.
from nets import nn import torch import yaml from utils.util import load_weight model_path = "weights/epoch_397.pt" save_path = "yolov8_pose.onnx" with open('utils/args.yaml', errors='ignore') as f: params = yaml.safe_load(f) model = nn.yolo_v8_n(len(params['names'])) model = load_weight(model_path, model) # print(model) img = torch.zeros(1, 3, 640, 640) torch.onnx.export(model, img, save_path, verbose=False, opset_version=12, input_names=['images'])Run export.py to convert model from pt to onnx.
python export.pyDownload the convert tool.
Create a docker.
docker pull yanwyb/npu:v1 $ docker run -it --name {name} -v $(pwd):/home/khadas/npu \ -v /etc/localtime:/etc/localtime:ro \ -v /etc/timezone:/etc/timezone:ro \
-v $HOME/.ccache:/home/khadas/.ccache --privilege d \ --device=/dev/loop-control:/dev/loop-control \ --device=/dev/loop0:/dev/loop0 --cap-add SYS_ADMIN yanwyb/npu:v1Install dependencies.
cd rknn-toolkit2sudo apt-get install python3 python3-dev python3-pip sudo apt-get install libxslt1-dev zlib1g-dev libglib2.0 libsm6 libgl1-mesa-glx libprotobuf-dev gcc cmake pip3 install -r doc/requirements_cp38-*.txt pip3 install packages/rknn_toolkit2-*-cp38-cp38-linux_x86_64.whlModify test.py as follows.
# Create RKNN object rknn = RKNN(verbose=True) # pre-process config print('--> Config model') rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588') print('done') # Load ONNX model print('--> Loading model') ret = rknn.load_onnx(model=ONNX_MODEL) if ret != 0: print('Load model failed!') exit(ret) print('done') # Build model print('--> Building model') ret = rknn.build(do_quantization=False, dataset=DATASET) if ret != 0: print('Build model failed!') exit(ret) print('done') # Export RKNN model print('--> Export rknn model') ret = rknn.export_rknn(RKNN_MODEL) if ret != 0: print('Export rknn model failed!') exit(ret) print('done')Run test.py.
python3 test.pyLpr(Densenet Ctc)
git clone https://github.com/YCG09/chinese_ocr.gitBefore converting the model, we suggest that you convert Keras model into ONNX model first. The converting code is as follows.
import onnx from keras.models import * import keras import keras2onnx from train import get_model import densenet basemodel, model = get_model(32, 88) basemodel.load_weights("models/weights_densenet-32-0.40.h5") onnx_model = keras2onnx.convert_keras(basemodel, basemodel.name, target_opset=12) onnx_model.graph.input[0].type.tensor_type.shape.dim[0].dim_value = int(1) onnx_model.graph.input[0].type.tensor_type.shape.dim[1].dim_value = int(1) onnx_model.graph.input[0].type.tensor_type.shape.dim[2].dim_value = int(32) onnx_model.graph.input[0].type.tensor_type.shape.dim[3].dim_value = int(280) onnx_model.graph.output[0].type.tensor_type.shape.dim[0].dim_value = int(1) onnx_model.graph.node.remove(onnx_model.graph.node[0]) onnx_model.graph.node[0].input[0] = "the_input" onnx.save_model(onnx_model, "./densenet_ctc.onnx")
Put densenet_ctc model and quantized picture in densenet_ctc folder.
cd densenet_ctc cp -r {path}/densenet_ctc.onnx ./Modify the parameters of test.py, including mean values, normalized values, the path to the Densenet-CTC model, and quantized images.

When you have run test.py successfully, you will find an RKNN model in your save path.
Lpc(VGG16)
The code uses Keras. So, we should convert the model from h5 to pb. Here is the tool we use.
Download the Edge2 conversion tool.
Modify the parameters of test.py, including mean values, normalized values, the VGG-16 model path, and quantized images.

When you have run test.py successfully, you will find an RKNN model in your save path.
Run NPU
Connect A10-3588 and pull npu code in A10-3588.
git clone https://github.com/khadas/a10-3588-npu cd a10-3588-npu/C++Install dependencies. If you use Yocto, you do not need to do this step.
sudo apt update sudo apt install cmake libopencv-devThe demo is to recognize Chinese license plates. So you should install a Chinese font library first.
sudo mkdir -p /usr/share/fonts/my_fonts sudo cp -r license_plate_recognition/data/simfang.ttf /usr/share/fonts/my_fonts cd /usr/share/fonts/my_fonts sudo apt install mkfontscale mkfontscaleThen reboot Edge2. After reboot, check installation success.
fc-list :lang=zhCopy lpd, lpr and lpc models to license_plate_recognition/data/model.
bash build.sh cd install/license_plate_recognition ./license_plate_recognition data/model/lpd.rknn data/model/lpr.rknn data/model/lpc.rknn ./data/img/粤 AA63D8.pngThe camera input demo is the license_plate_recognition_cap.
bash build.sh cd install/license_plate_recognition_cap ./license_plate_recognition_cap data/model/lpd.rknn data/model/lpr.rknn data/model/lpc.rknn 33
Comments