充电站项目mavic pro自动控制app源码 在上一篇文章中写了怎样训练出cascade.xml文件,这一篇讲解怎样在android app里使用级联分类器进行多目标检测。
首先要做的是在app中导入opencv4android的SDK。opencv4android SDK
配置环境:OpenCV On Android环境配置指南
本项目采用动态链接库,免去安装额外的OpenCV Manager App。
图像处理与识别部分在src的application下的PictureHandle里。
使用步骤:
1.把cascade.xml文件放入res下的raw文件夹里。
2.在activity里加载opencv库:
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 @Override protected void onResume() { super.onResume(); //load OpenCV engine and init OpenCV library if(!OpenCVLoader.initDebug()) { OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, getApplicationContext(), mLoaderCallback); Log.d(TAG, "Internal OpenCV library not found. Using Opencv Manager for initialization"); }else { Log.d(TAG, "OpenCV library found inside package. Using it!"); mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); } }
3.设置链接到opencv库的回调函数mLoaderCallback。在该回调函数里,我们加载cascade.xml文件,并对CascadeClassifier做初始化:
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 @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: isInit = true; try { // load cascade file from application resources InputStream is = context.getResources().openRawResource(R.raw.cascade1); File cascade1Dir = context.getDir("cascade1", Context.MODE_PRIVATE); mCascade1File = new File(cascade1Dir, "cascade1.xml"); FileOutputStream os = new FileOutputStream(mCascade1File); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } is.close(); os.close(); // load cascade file from application resources InputStream ise = context.getResources().openRawResource(R.raw.cascade2); File cascade2Dir = context.getDir("cascade2", Context.MODE_PRIVATE); mCascade2File = new File(cascade2Dir, "cascade2.xml"); FileOutputStream ose = new FileOutputStream(mCascade2File); while ((bytesRead = ise.read(buffer)) != -1) { ose.write(buffer, 0, bytesRead); } ise.close(); ose.close(); mJavaDetector1 = new CascadeClassifier(mCascade1File.getAbsolutePath()); if (mJavaDetector1.empty()) { Log.e(TAG, "Failed to load cascade classifier"); mJavaDetector1 = null; } else Log.i(TAG, "Loaded cascade classifier from " + mCascade1File.getAbsolutePath()); mJavaDetector2 = new CascadeClassifier(mCascade2File.getAbsolutePath()); if (mJavaDetector2.empty()) { Log.e(TAG, "Failed to load cascade classifier"); mJavaDetector2 = null; } else { Log.i(TAG, "Loaded cascade classifier from " + mCascade2File.getAbsolutePath()); } cascade1Dir.delete(); cascade2Dir.delete(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "Failed to load cascade. Exception thrown: " + e); } break; default: isInit = false; super.onManagerConnected(status); break; } }
4.新建的Picture_Detector1类。mavic pro的摄像头传回到app上的预览画面使用安卓texture,我们先从texture里取出一帧图片,保存到bitmap类的变量里。然后将bitmap转为opencv的mat类型数据,接着将图片转为灰度图像,传入detectMultiScale方法里检测目标。 下面是新建的Picture_Detector1类,开一个线程处理图像:
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 public class Picture_Detector1 implements Runnable{ private Bitmap bitmap; private Handler UIHandler; private Rect[] result; public void begin() { new Thread(this).start(); } public Picture_Detector1(Bitmap bitmap,Handler UIHandler){ this.bitmap = bitmap; this.UIHandler = UIHandler; } @Override public void run() { Mat srcimg = new Mat(); Utils.bitmapToMat(bitmap, srcimg); Imgproc.cvtColor(srcimg, srcimg,Imgproc.COLOR_RGBA2GRAY); result = detector1(srcimg); Message msg = new Message(); Bundle b = new Bundle();// 存放数据 b.putSerializable("picturedetector1", result); msg.setData(b); UIHandler.sendMessage(msg); } }
图像处理函数为 result = detector1(srcimg); 函数代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public Rect[] detector1(Mat srcimg) { if (mAbsoluteTargetSize == 0) { int height = srcimg.rows(); if (Math.round(height * mRelativeTargetSize) > 0) { mAbsoluteTargetSize = Math.round(height * mRelativeTargetSize); } } MatOfRect targets = new MatOfRect(); if (mDetectorType == JAVA_DETECTOR) { if (mJavaDetector1 != null) mJavaDetector1.detectMultiScale(srcimg, targets, 1.1, 6, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE new Size(mAbsoluteTargetSize, mAbsoluteTargetSize), new Size()); } else { Log.e(TAG, "Detection method is not selected!"); } return targets.toArray(); }
CascadeClassifier为级联分类器检测类,使用Adaboost的方法,提取LBP\HOG\HAAR特征进行目标检测,加载traincascade进行训练的分类器。 detectMultiscale函数为多尺度多目标检测: detectMultiScale能够实现多尺度检测,但多尺度检测是通过缩放图像来完成的。多尺度:通常搜索目标的模板尺寸大小是固定的,但是不同图片大小不同,所以目标对象的大小也是不定的,所以多尺度即不断缩放图片大小(缩放到与模板匹配),通过模板滑动窗函数搜索匹配;同一副图片可能在不同尺度下都得到匹配值,所以多尺度检测函数detectMultiscale是多尺度合并的结果。 多目标:通过检测符合模板匹配对象,可得到多个目标,均输出到objects向量里面。 detectMultiScale函数参数解释: const Mat, —Mat类型的图像,待检测图片,一般为灰度图像加快检测速度。 Rect[],—检测得到的被检测物体的矩形框向量组;为输出量 doublescaleFactor, —图像缩放因子,必须大于1,表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%;一般设置为1.1 。 intminNeighbors,—构成检测目标的相邻矩形的最小个数(默认为3个)。如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。 如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框, 这种设定值一般用在用户自定义对检测结果的组合程序上。 int flags, —要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,如果设置为CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域。 Size minObjectSize, —最小检测窗口大小 Size maxObjectSize)—最大检测窗口大小(默认是图像大小) 5.在activity里调用Picture_Detector: 新建myhandler,用于处理从线程中接收的数据:
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 private Rect[] targets1Array = null; private Rect[] targets2Array = null; private MyHandler1 myHandler1; private MyHandler2 myHandler2; @Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_camera); super.onCreate(savedInstanceState); initUI(); myHandler1 = new MyHandler1(); myHandler2 = new MyHandler2(); } class MyHandler1 extends Handler { public MyHandler1() { } public MyHandler1(Looper L) { super(L); } // 子类必须重写此方法,接受数据 @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); // 此处可以更新UI Bundle b = msg.getData(); targets1Array = (Rect[]) b.getSerializable("picturedetector1"); if(targets1Array.length > 0){ CameraActivity.this.runOnUiThread(new Runnable() { @Override public void run() { mTrackingImage1.setX(targets1Array[0].x); mTrackingImage1.setY(targets1Array[0].y); mTrackingImage1.getLayoutParams().width = targets1Array[0].width; mTrackingImage1.getLayoutParams().height = targets1Array[0].height; mTrackingImage1.requestLayout(); mTrackingImage1.setVisibility(View.VISIBLE); } }); }else { CameraActivity.this.runOnUiThread(new Runnable() { @Override public void run() { mTrackingImage1.setVisibility(View.INVISIBLE); } }); } } }
重写onSurfaceTextureUpdated回调函数,用于实时图像处理
1 2 3 4 5 6 7 @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { super.onSurfaceTextureUpdated(surface); Bitmap bitmap = mVideoSurface.getBitmap(); picturehandle.new Picture_Detector1(bitmap,myHandler1).begin(); picturehandle.new Picture_Detector2(bitmap,myHandler2).begin(); }
本文标题: opencv cascade.xml在安卓APP中的使用
文章作者: Mr Bluyee
发布时间: 2018-07-05
最后更新: 2019-07-15
原始链接: https://www.mrbluyee.com/2018/07/05/opencv-cascade-xml%E5%9C%A8%E5%AE%89%E5%8D%93APP%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/
版权声明: The author owns the copyright, please indicate the source reproduced.