Android OpenCV集成摄像头图片动态识别车牌号
最近两天开发一个使用OpenCV集成的一个识别车牌号的项目,困难重重,总结一下相关经验,以及开发注意事项;
一、开发环境:
Android Studio 个人版本 3.1.4
NDK下载:14b
CMake:Android Studio SDK Tools中下载
参考资料:https://github.com/zeusees/HyperLPR 集成有冲突未解决;
很实用的一个Dmeo以这个为例
https://blog.csdn.net/u011686167/article/details/79029765
楼主人很好,给我解答疑问;附上博主Demo下载地址:
https://download.csdn.net/download/u011686167/10899892
集成中遇到的问题:
一、环境配置的问题:
NDK:尝试使用最新版本,但是一直有冲突,出现问题,14b使用兼容性比较好
CMake:
OpenCV类库: openCVLibrary330
二、项目集成问题:
(1)下载模型文件替换和倒入assets/pr下面的文件,报错如下:
"/storage/emulated/0/pr/HorizonalFinemapping.prototxt") in bool cv::dnn::ReadProtoFromTextFile(
const char*,google::protobuf::Message*), file /build/master_pack-android/opencv/modules/dnn/src
/caffe/caffe_io.cpp, line 1113
(2)添加摄像头权限
(3)问题:只能横向识别车牌号,纵向不能识别,并且相机方向不对:
解决相机显示正常:
参考资料 https://blog.csdn.net/u010112268/article/details/80420454
将下图文件中的 deliverAndDrawFrame 方法
修改为以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | protected void deliverAndDrawFrame(CvCameraViewFrame frame){ Mat modified; if (mListener != null ) { modified = mListener.onCameraFrame(frame); } else { modified = frame.rgba(); } boolean bmpValid = true ; if (modified!= null ) { try { Utils.matToBitmap(modified,mCacheBitmap); } catch (Exceptione) { Log.e(TAG, "Mattype: " + modified); Log.e(TAG, "Bitmaptype: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight()); Log.e(TAG, "Utils.matToBitmap()throws an exception: " +e.getMessage()); bmpValid = false ; } } if (bmpValid&& mCacheBitmap != null ) { Canvas canvas =getHolder().lockCanvas(); if (canvas!= null ) { canvas.drawColor(0,android.graphics.PorterDuff.Mode.CLEAR); /*if (BuildConfig.DEBUG) Log.d(TAG, "mStretchvalue: " + mScale); if (mScale != 0) { canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(),mCacheBitmap.getHeight()), newRect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2), (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2), (int)((canvas.getWidth() -mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()), (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 +mScale*mCacheBitmap.getHeight())), null); } else { canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(),mCacheBitmap.getHeight()), newRect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight()- mCacheBitmap.getHeight()) / 2, (canvas.getWidth() -mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(), (canvas.getHeight()- mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null); }*/ /*----------------------------修改预览旋转90度问题--------------------------------*/ canvas.rotate(90,0,0); float scale= canvas.getWidth() / ( float )mCacheBitmap.getHeight(); float scale2= canvas.getHeight() / ( float )mCacheBitmap.getWidth(); if (scale2> scale){ scale = scale2; } if (scale!= 0) { canvas.scale(scale,scale,0,0); } canvas.drawBitmap(mCacheBitmap, 0, -mCacheBitmap.getHeight(), null ); /*----------------------------修改预览旋转90度问题--------------------------------*/ if (mFpsMeter != null ) { mFpsMeter.measure(); mFpsMeter.draw(canvas, 20, 30); } getHolder().unlockCanvasAndPost(canvas); } } } |
解决图片不能纵向识别方法:参考资料 https://blog.csdn.net/hujiameihuxu/article/details/78810100
图片角度转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | Mat matRotateClockWise90(Mat src) { if (src.empty()) { qDebug()<< "RorateMat src is empty!" ; } // 矩阵转置 transpose(src, src); //0: 沿X轴翻转; >0: 沿Y轴翻转; <0: 沿X轴和Y轴翻转 flip(src, src, 1); // 翻转模式,flipCode == 0垂直翻转(沿X轴翻转),flipCode>0水平翻转(沿Y轴翻转),flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°) return src; } Mat matRotateClockWise180(Mat src) //顺时针180 { if (src.empty()) { qDebug() << "RorateMat src is empty!" ; } //0: 沿X轴翻转; >0: 沿Y轴翻转; <0: 沿X轴和Y轴翻转 flip(src, src, 0); // 翻转模式,flipCode == 0垂直翻转(沿X轴翻转),flipCode>0水平翻转(沿Y轴翻转),flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°) flip(src, src, 1); return src; //transpose(src, src);// 矩阵转置 } Mat matRotateClockWise270(Mat src) //顺时针270 { if (src.empty()) { qDebug() << "RorateMat src is empty!" ; } // 矩阵转置 //transpose(src, src); //0: 沿X轴翻转; >0: 沿Y轴翻转; <0: 沿X轴和Y轴翻转 transpose(src, src); // 翻转模式,flipCode == 0垂直翻转(沿X轴翻转),flipCode>0水平翻转(沿Y轴翻转),flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°) flip(src, src, 0); return src; } Mat myRotateAntiClockWise90(Mat src) //逆时针90° { if (src.empty()) { qDebug()<< "mat is empty!" ; } transpose(src, src); flip(src, src, 0); |
进行转化:
本人Demo代码地址以及模型地址:https://gitee.com/anan9303/PrjAndroid.git
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!