由于网上的车牌模型已经有很多,但是对于某些自己特定的场景的图片,可能没有很好的数据集,所以可以将这些模型拿自己的数据集再次进行训练,但是训练后的部署又是一个问题
本文章用于记录yolov5模型进行一系列的转换最后部署安卓的学习笔记
模型来源:模型来源
项目来源:安卓项目
目录
问题:在采用网上别人的模型的时候,发现特定的场景,识别率低
识别效果就不太理想,所以我们可以多采集我们需求的特定场景数据,然后对模型进行训练;
一.yolo转onnx
一般使用项目的export文件就可以对模型进行转换,教程过多不再赘述
我们得到best.onnx后,就可以对其转为ncnn
二.onnx转ncnn
这里就采用转换工具对模型进行转换
一键转换 Caffe, ONNX, TensorFlow 到 NCNN, MNN, Tengine (convertmodel.com)
获取到ncnn模型后,就可以开始进行安卓的部署了
三.安卓的部署
首先先下载示例的安卓案例,将我们新的模型放置在assets中
接下来要修改cpp文件
在文件中找到模型的名称填写位置,有两处
修改前:
修改后:
然后直接点击plateNcnn.param,进行参数的修改
ctrl+f,搜索关键词Reshape,然后改为图中圈中的部分,全部改为-1,防止出现很多框的情况
然后记录下三个Rermute圈中的值
分别为output,1202,1219
记录input的值
为images
接下来进入cpp文件中,修改对应的参数
ctrl+f : ex.input(
ex.input(
会有四个需要修改的地方,分别对应的改为我们刚刚记录的值,结果如下
修改好这四个位置后,就算部署成功了
四.在一个新项目中配置该模型
创建项目
对项目进行基础的配置,写定让用户选择本地的图片,调用函数识别
public class MainActivity extends AppCompatActivity {
public static int REQUEST_CODE_PICK = 1;
PlateRecognition plateRecognition = new PlateRecognition();
private TextView tv_result;
private ImageView iv_image;
private Bitmap bitmap;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean init = plateRecognition.init(getAssets());
Log.e("onCreate", "onCreate: "+init );
tv_result = findViewById(R.id.tv_result);
iv_image = findViewById(R.id.iv_image);
findViewById(R.id.btn_choose).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE_PICK);
}
});
findViewById(R.id.btn_begin).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (bitmap != null){
YoloV5Ncnn.Obj[] detect = plateRecognition.detect(bitmap, true);
if (detect != null && detect.length != 0){
tv_result.setText(detect[0].label+"");
}else {
tv_result.setText("未识别出结果");
}
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data != null && requestCode == REQUEST_CODE_PICK) {
/*获取用户选择的相片*/
Uri imageUri = data.getData();
try {
bitmap = decodeUri(imageUri);
iv_image.setImageBitmap(bitmap);
//这里获取到用户选择的图像,下面可以进行其他的操作
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
}
decodeUri这个函数可以参考安卓项目中的
将原项目的YoloV5Ncnn和PlateRecognizer两个类直接拿过来
然后将示例案例的jni,assets文件夹复制过来,注意位置
由于包名的不同,在cpp中,我们还需要对应函数的方法签名(不想改签名的也能自己创建一个与原项目一致的包名,然后将这两个类放置进去)
如:
例如我的包名是:com.yang.modelTest
然后给app的清单文件中加入获取手机文件的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
操作好后就可以运行app了
结果如下
相关报错的解决方法:
1.若出现如图报错
则是四个需要修改的参数填写错误
2.若出现车牌检测到,但是文字识别很奇怪
那就是车牌四角的坐标矫正出现问题,在cpp文件中的该位置,将四角的坐标进行更换即可
3. java.lang.UnsatisfiedLinkError: dlopen failed: library "libyolov5ncnn.so" not found
这是表示cmake生成的库找不到,理论上按照操作后,是应该会生成一个对应的库的,检查在app的gradle中是否添加以下的内容
externalNativeBuild {
cmake {
version "3.10.2"
path file('src/main/jni/CMakeLists.txt')
}
}
然后看看
在 Android 项目中,CMake 生成的库通常位于以下位置
<project_dir>/app/build/intermediates/cmake/debug/obj/<abi>/lib<library_name>.so
该路径下,是否存在该库,若是不存在,则证明生成失败
4.No implementation found for com...
对应的方法,包名必须对应上,如
JNIEXPORT jobjectArray JNICALL Java_com_yang_modeltest_YoloV5Ncnn_Detect(JNIEnv* env, jobject thiz, jobject bitmap, jboolean use_gpu)
则必须在包名com.yang.modeltest下面的YoloV5Ncnn类中的一个方法Detect
在可以修改源代码的情况下,可以根据自己的包名修改,若是拿不到源代码的情况下,就需要根据源代码添加包名,将该类放置进行(自己总结的解决方法,若是有其他的方法麻烦告知一下谢谢)