当前位置: 首页 > 编程日记 > 正文

OpenCV4Android开发实录(2): 使用OpenCV3.4.1库实现人脸检测

OpenCV4Android开发实录(2): 使用OpenCV3.3.0库实现人脸检测

转载请声明出处:http://write.blog.csdn.net/postedit/78992490


OpenCV4Android系列

1. OpenCV4Android开发实录(1):移植OpenCV3.3.0库到Android Studio

2.OpenCV4Android开发实录(2): 使用OpenCV3.3.0库实现人脸检测


上一篇文章OpenCV4Android开发实录(1):移植OpenCV3.3.0库到Android Studio大概介绍了下OpenCV库的基本情况,阐述了将OpenCV库移植到Android Studio项目中的具体步骤。本文将在此文的基础上,通过对OpenCV框架中的人脸检测模块做相应介绍,然后实现人脸检测功能。

一、人脸检测模块移植

1.拷贝opencv-3.3.0-android-sdk\OpenCV-android-sdk\samples\face-detection\jni目录到工程app module的main目录下

 

2.修改jni目录下的Android.mk

(1)

[cpp] view plaincopy
  1. #OPENCV_INSTALL_MODULES:=off  
  2. #OPENCV_LIB_TYPE:=SHARED  

 修改为:
[cpp] view plaincopy
  1. OPENCV_INSTALL_MODULES:=on  
  2. OPENCV_LIB_TYPE:=SHARED  
其中,OPENCV_INSTALL_MODULES的作用是在打包apk时加载OpenCV的动态库;OPENCV_LIB_TYPE的作用是指定OpenCV库的类型为动态库。

(2)

[cpp] view plaincopy
  1. ifdef OPENCV_ANDROID_SDK  
  2.    ifneq ("","$(wildcard $(OPENCV_ANDROID_SDK)/OpenCV.mk)")  
  3.    <span style="white-space:pre;">  </span>include ${OPENCV_ANDROID_SDK}/OpenCV.mk  
  4.    else  
  5.    <span style="white-space:pre;">  </span>include ${OPENCV_ANDROID_SDK}/sdk/native/jni/OpenCV.mk  
  6.   endif  
  7.  <span style="white-space:pre;">    </span>include ../../sdk/native/jni/OpenCV.mk  
  8.   endif  

         修改为:
[cpp] view plaincopy
  1. include E:\Environment\opencv-3.3.0-android-sdk\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk  

其中,include包含的就是OpenCV SDK中OpenCV.mk文件所存储的绝对路径。最终Android.mk修改效果如下:


3.修改jni目录下Application.mk。由于在导入OpenCV libs时只拷贝了armeabi 、armeabi-v7a、arm64-v8a,因此这里指定编译平台也为上述三个;修改APP_PLaTFORM版本为android-16(可根据自身情况而定),具体如下:

[cpp] view plaincopy
  1. APP_STL := gnustl_static  
  2. APP_CPPFLAGS := -frtti –fexceptions  
  3.  # 指定编译平台  
  4.  APP_ABI := armeabi armeabi-v7a arm64-v8a  
  5. # 指定Android平台  
  6. APP_PLATFORM := android-16  

4.修改DetectionBasedTracker_jni.h和DetectionBasedTracker_jni.cpp文件,将源文件中所有包含前缀“Java_org_opencv_samples_facedetect_”替换为“Java_com_jiangdg_opencv4android_natives_”,其中com.jiangdg.opencv4android.natives是Java层类DetectionBasedTracker.java所在的包路径,该类包含了人脸检测相关的native方法,否则,在调用自己编译生成的so库时会提示找不到该本地函数错误,以DetectionBasedTracker_jni.h为例:

[cpp] view plaincopy
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class org_opencv_samples_fd_DetectionBasedTracker */  
  4.   
  5. #ifndef _Included_org_opencv_samples_fd_DetectionBasedTracker  
  6. #define _Included_org_opencv_samples_fd_DetectionBasedTracker  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     org_opencv_samples_fd_DetectionBasedTracker 
  12.  * Method:    nativeCreateObject 
  13.  * Signature: (Ljava/lang/String;F)J 
  14.  */  
  15. JNIEXPORT jlong JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeCreateObject  
  16.   (JNIEnv *, jclass, jstring, jint);  
  17.   
  18. /* 
  19.  * Class:     org_opencv_samples_fd_DetectionBasedTracker 
  20.  * Method:    nativeDestroyObject 
  21.  * Signature: (J)V 
  22.  */  
  23. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeDestroyObject  
  24.   (JNIEnv *, jclass, jlong);  
  25.   
  26. /* 
  27.  * Class:     org_opencv_samples_fd_DetectionBasedTracker 
  28.  * Method:    nativeStart 
  29.  * Signature: (J)V 
  30.  */  
  31. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeStart  
  32.   (JNIEnv *, jclass, jlong);  
  33.   
  34. /* 
  35.  * Class:     org_opencv_samples_fd_DetectionBasedTracker 
  36.  * Method:    nativeStop 
  37.  * Signature: (J)V 
  38.  */  
  39. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeStop  
  40.   (JNIEnv *, jclass, jlong);  
  41.   
  42.   /* 
  43.    * Class:     org_opencv_samples_fd_DetectionBasedTracker 
  44.    * Method:    nativeSetFaceSize 
  45.    * Signature: (JI)V 
  46.    */  
  47.   JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeSetFaceSize  
  48.   (JNIEnv *, jclass, jlong, jint);  
  49.   
  50. /* 
  51.  * Class:     org_opencv_samples_fd_DetectionBasedTracker 
  52.  * Method:    nativeDetect 
  53.  * Signature: (JJJ)V 
  54.  */  
  55. JNIEXPORT void JNICALL Java_com_jiangdg_opencv4android_natives_DetectionBasedTracker_nativeDetect  
  56.   (JNIEnv *, jclass, jlong, jlong, jlong);  
  57.   
  58. #ifdef __cplusplus  
  59. }  
  60. #endif  
  61. #endif  

5.打开Android Studio中的Terminal窗口,使用cd命令切换到工程jni目录所在位置,并执行ndk-build命令,然后会自动在工程的app/src/main目录下生成libs和obj目录,其中libs目录存放的是目标动态库libdetection_based_tracker.so。


生成so库:


注意:如果执行ndk-build命令提示命令不存在,说明你的ndk环境变量没有配置好。

6.修改app模块build.gradle中的sourceSets字段,禁止自动调用ndk-build命令,设置目标so的存放路径,代码如下:

[java] view plaincopy
  1. android {  
  2.     compileSdkVersion 25  
  3.     defaultConfig {  
  4.         applicationId "com.jiangdg.opencv4android"  
  5.         minSdkVersion 15  
  6.         targetSdkVersion 25  
  7.         versionCode 1  
  8.         versionName "1.0"  
  9.     }  
  10.     ….// 代码省略  
  11.    sourceSets {  
  12.         main {  
  13.             jni.srcDirs = []                 //禁止自动调用ndk-build命令  
  14.             jniLibs.srcDir 'src/main/libs'  // 设置目标的so存放路径  
  15. }  
  16.     }  
  17.     ….// 代码省略  
  18. }  

其中,jni.srcDirs = []的作用是禁用gradle默认的ndk-build,防止AS自己生成android.mk编译jni工程,jniLibs.srcDir 'src/main/libs'的作用设置目标的so存放路径,以将自己生成的so组装到apk中。

二、源码解析
使用OpenCV3.3.0库实现人脸检测功能主要包含以下四个步骤,即:
(1) 初始化加载OpenCV库引擎;
(2) 通过OpenCV库开启Camera渲染;
(3) 加载人脸检测模型;
(4) 调用人脸检测本地动态库实现人脸识别;
1.初始化加载OpenCV库引擎
OpenCV库的加载有两种方式,一种通过OpenCV Manager进行动态加载,也就是官方推荐的方式,这种方式需要另外安装OpenCV Manager,主要通过调用OpenCVLoader.initAsync()方法进行初始化;另一种为静态加载,也就是本文所使用的方法,即先将相关架构的so包拷贝到工程的libs目录,通过调用OpenCVLoader.initDebug()方法进行初始化,类似于调用system.loadLibrary("opencv_java")。
[java] view plaincopy
  1. if (!OpenCVLoader.initDebug()) {  
  2.     // 静态加载OpenCV失败,使用OpenCV Manager初始化  
  3.     // 参数:OpenCV版本;上下文;加载结果回调接口  
  4.      OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_3_0,   
  5.     this, mLoaderCallback);  
  6.  } else {  
  7.      // 如果静态加载成功,直接调用onManagerConnected方法  
  8.      mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);  
  9.  }  
其中,mLoaderCallback为OpenCV库初始化状态回调接口,当OpenCV被初始化成功后其onManagerConnected(int status)方法会被调用,而我们就可以在该方法中处理本地动态库的加载、加载人脸检测模型文件、初始化人脸检测引擎以及开启Camera渲染等操作,具体代码如下:
[java] view plaincopy
  1. private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {  
  2. @Override  
  3. public void onManagerConnected(int status) {  
  4.    switch (status) {  
  5.       case LoaderCallbackInterface.SUCCESS:  
  6.           // OpenCV初始化加载成功,再加载本地so库  
  7.           System.loadLibrary("detection_based_tracker");  
  8.           // 加载人脸检测模型  
  9.           …..  
  10.          // 初始化人脸检测引擎  
  11.          …..  
  12.          // 开启渲染Camera  
  13.          mCameraView.enableView();  
  14.          break;  
  15.       default:  
  16.          super.onManagerConnected(status);  
  17.          break;  
  18.      }  
  19.    }  
  20. };  
2. 通过OpenCV库开启Camera渲染
在OpenCV中与Camera紧密相关的主要有两个类,即CameraBridgeViewBase和JavaCameraView,其中,CameraBridgeViewBase是一个基类,继承于SuarfaceView和SurafaceHolder.Callback接口,用于实现Camera与OpenCV库之间的交互,它主要的作用是控制Camera、处理视频帧以及调用相关内部接口对视频帧做相关调整,然后将调整后的视频帧数据渲染到手机屏幕上。比如enableView()方法、disableView()方法用于连接到Camera和断开与Camera的连接,代码如下:
[java] view plaincopy
  1.  public void enableView() {  
  2.      synchronized(mSyncObject) {  
  3.          mEnabled = true;  
  4.          checkCurrentState();  
  5.      }  
  6.   
  7. public void disableView() {  
  8.      synchronized(mSyncObject) {  
  9.          mEnabled = false;  
  10.          checkCurrentState();  
  11.      }  
其中,checkCurrentState()方法用于更新Camera的渲染状态,它调用了processEnterState()方法来启动或停用Camera,以及将Camera的状态对外回调。为了方便开发者实时获取Camera的连接状态,CameraBridgeViewBase还提供了一个setCvCameraViewListener(CvCameraViewListener2 listener)方法,参数listener其一个内部接口,它包括三个方法:onCameraViewStarted(int width, int height)、void onCameraViewStopped()、Mat onCameraFrame(CvCameraViewFrame inputFrame),分别用于对外回调Camera连接状态和传递Camera的实时视频帧数据。
[java] view plaincopy
  1. private void checkCurrentState() {  
  2.       Log.d(TAG, "call checkCurrentState");  
  3.       int targetState;  
  4.   
  5.   
  6.       if (mEnabled && mSurfaceExist && getVisibility() == VISIBLE) {  
  7.           targetState = STARTED;  
  8.       } else {  
  9.           targetState = STOPPED;  
  10.       }  
  11.   
  12.   
  13.       if (targetState != mState) {  
  14.           /* The state change detected. Need to exit the current state and enter target state */  
  15.           processExitState(mState);  
  16.           mState = targetState;  
  17.           processEnterState(mState);  
  18.       }  
  19.   }  
  20.   private void processEnterState(int state) {  
  21.       Log.d(TAG, "call processEnterState: " + state);  
  22.       switch(state) {  
  23.       case STARTED:  
  24.           // 调用connectCamera()抽象方法,启动Camera  
  25.           onEnterStartedState();  
  26.           // 调用连接成功监听器接口方法  
  27.           if (mListener != null) {  
  28.               mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);  
  29.           }  
  30.           break;  
  31.       case STOPPED:  
  32.           // 调用disconnectCamera()抽象方法,停用Camera  
  33.           onEnterStoppedState();  
  34.           // 调用断开连接监听器接口方法  
  35.           if (mListener != null) {  
  36.               mListener.onCameraViewStopped();  
  37.           }  
  38.           break;  
  39.       };  
       既然CameraBridgeViewBase是一个基类,与Camera紧密相关的connectCamera()和disconnectCamera()又是抽象方法,那么就必定会有一个子类来实现这两个方法,而这个子类就是JavaCameraView。JavaCameraView继承于CameraBridgeViewBase和PreviewCallback接口,是衔接OpenCV和Camera的桥梁,是Camera启动、禁止的实际实现者,在这个类里我们可以看到关于Camera很多熟悉的操作。源码如下:
[java] view plaincopy
  1. @Override  
  2.   protected boolean connectCamera(int width, int height) {  
  3.       // 初始化Camera,连接到Camera  
  4.       if (!initializeCamera(width, height))  
  5.           return false;  
  6.       mCameraFrameReady = false;  
  7.       // 开启一个与Camera相关的工作线程CameraWorker  
  8.       Log.d(TAG, "Starting processing thread");  
  9.       mStopThread = false;  
  10.       mThread = new Thread(new CameraWorker());  
  11.       mThread.start();  
  12.       return true;  
  13.   }  
  14.   
  15.   
  16.   @Override  
  17.   protected void disconnectCamera() {  
  18.       // 断开Camera连接,释放相关资源  
  19.       try {  
  20.           mStopThread = true;  
  21.           Log.d(TAG, "Notify thread");  
  22.           synchronized (this) {  
  23.               this.notify();  
  24.           }  
  25.           // 停止工作线程  
  26.           if (mThread != null)  
  27.               mThread.join();  
  28.       } catch (InterruptedException e) {  
  29.           e.printStackTrace();  
  30.       } finally {  
  31.           mThread =  null;  
  32.       }  
  33.   
  34.   
  35.       /* Now release camera */  
  36.       releaseCamera();  
  37.   
  38.   
  39.       mCameraFrameReady = false;  
     CameraWorker是一个工作线程,用于处理从onPreviewFrame获得的视频帧数据,其存储在一个Mat类型的数组中。它会不断调用父类CameraBridgeViewBase的deliverAndDrawFrame方法,将处理后的视频帧数据流通过调用内部接口CvCameraViewListener2的onCameraFrame(CvCameraViewFrame frame)对外回调。
[java] view plaincopy
  1. private class CameraWorker implements Runnable {  
  2.         @Override  
  3.         public void run() {  
  4.             do {  
  5.                  …..//代码省略  
  6.                 if (!mStopThread && hasFrame) {  
  7.                     if (!mFrameChain[1 - mChainIdx].empty())  
  8.                         deliverAndDrawFrame(mCameraFrame[1 - mChainIdx]);  
  9.                 }  
  10.             } while (!mStopThread);  
  11.         }  
  12.     }  
3. 加载人脸检测模型
    为了得到更好的人脸检测性能,OpenCV在SDK中提供了多个frontface检测器(人脸模型),存放在..\opencv-3.3.0-android-sdk\OpenCV-android-sdk\sdk\etc\目录下,这篇对OpenCV自带的人脸检测模型做了比较,结果显示LBP实时性要好些。因此,本文选用目lbpcascades录下lbpcascade_frontalface.xml模型,该模型包括了3000个正样本和1500个负样本,我们将其拷贝到AS工程的res/raw目录下,并通过getDir方法保存到/data/data/com.jiangdg.opencv4android/ cascade目录下。
[java] view plaincopy
  1. InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);  
  2. File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);  
  3. mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");  
  4. FileOutputStream os = new FileOutputStream(mCascadeFile);  
  5. byte[] buffer = new byte[4096];  
  6. int byteesRead;  
  7. while ((byteesRead = is.read(buffer)) != -1) {  
  8.     os.write(buffer, 0, byteesRead);  
  9. }  
  10. is.close();  
  11. os.close();  
注:关于模型的训练在以后的博文中会讨论到。
4. 人脸检测
在opencv-3.3.0-android-sdk的face-detection示例项目中,提供了CascadeClassifier和
DetectionBasedTracker两种方式来实现人脸检测,其中,CascadeClassifier是OpenCV用于人脸检测的一个级联分类器,DetectionBasedTracker是通过JNI编程实现的人脸检测。两种方式我都试用了下,发现DetectionBasedTracker方式还是比CascadeClassifier稳定些,CascadeClassifier会存在一定频率的误检。
[java] view plaincopy
  1. public class DetectionBasedTracker {  
  2.     private long mNativeObj = 0;  
  3.     // 构造方法:初始化人脸检测引擎  
  4.     public DetectionBasedTracker(String cascadeName, int minFaceSize) {  
  5.         mNativeObj = nativeCreateObject(cascadeName, minFaceSize);  
  6.     }  
  7.     // 开始人脸检测  
  8.     public void start() {  
  9.         nativeStart(mNativeObj);  
  10.     }  
  11.     // 停止人脸检测  
  12.     public void stop() {  
  13.         nativeStop(mNativeObj);  
  14.     }  
  15.     // 设置人脸最小尺寸  
  16.     public void setMinFaceSize(int size) {  
  17.         nativeSetFaceSize(mNativeObj, size);  
  18.     }  
  19.   
  20.   
  21.     // 检测  
  22.     public void detect(Mat imageGray, MatOfRect faces) {  
  23.         nativeDetect(mNativeObj, imageGray.getNativeObjAddr(), faces.getNativeObjAddr());  
  24.     }  
  25.     // 释放资源  
  26.     public void release() {  
  27.         nativeDestroyObject(mNativeObj);  
  28.         mNativeObj = 0;  
  29.     }  
  30.     // native方法  
  31.     private static native long nativeCreateObject(String cascadeName, int minFaceSize);  
  32.     private static native void nativeDestroyObject(long thiz);  
  33.     private static native void nativeStart(long thiz);  
  34.     private static native void nativeStop(long thiz);  
  35.     private static native void nativeSetFaceSize(long thiz, int size);  
  36.     private static native void nativeDetect(long thiz, long inputImage, long faces);  
  37. }  
初始化DetectionBasedTracker后,我们只需要在CvCameraViewListener2接口的onCameraFrame方法中对每帧图片进行人脸检测即可。
[java] view plaincopy
  1. @Override  
  2. public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {  
  3.          ….// 代码省略  
  4.         // 获取检测到的脸部数据  
  5.         MatOfRect faces = new MatOfRect();  
  6.         …// 代码省略  
  7.         if (mNativeDetector != null) {  
  8.             mNativeDetector.detect(mGray, faces);  
  9.          }  
  10.         // 绘制检测框  
  11.         Rect[] facesArray = faces.toArray();  
  12.         for (int i = 0; i < facesArray.length; i++) {  
  13.             Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);  
  14.         }  
  15.         return mRgba;  
  16.     }  

注:由于篇幅原因,关于人脸检测的C/C++实现代码(原理),我们将在后续文章中讨论。

三、效果演示

1. FaceDetectActivity.class

[java] view plaincopy
  1. /** 
  2.  * 人脸检测 
  3.  *  
  4.  * Created by jiangdongguo on 2018/1/4. 
  5.  */  
  6.   
  7. public class FaceDetectActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {  
  8.     private static final int JAVA_DETECTOR = 0;  
  9.     private static final int NATIVE_DETECTOR = 1;  
  10.     private static final String TAG = "FaceDetectActivity";  
  11.     @BindView(R.id.cameraView_face)  
  12.     CameraBridgeViewBase mCameraView;  
  13.   
  14.   
  15.     private Mat mGray;  
  16.     private Mat mRgba;  
  17.     private int mDetectorType = NATIVE_DETECTOR;  
  18.     private int mAbsoluteFaceSize = 0;  
  19.     private float mRelativeFaceSize = 0.2f;  
  20.     private DetectionBasedTracker mNativeDetector;  
  21.     private CascadeClassifier mJavaDetector;  
  22.     private static final Scalar FACE_RECT_COLOR = new Scalar(02550255);  
  23.   
  24.   
  25.     private File mCascadeFile;  
  26.     private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {  
  27.         @Override  
  28.         public void onManagerConnected(int status) {  
  29.             switch (status) {  
  30.                 case LoaderCallbackInterface.SUCCESS:  
  31.                     // OpenCV初始化加载成功,再加载本地so库  
  32.                     System.loadLibrary("detection_based_tracker");  
  33.   
  34.   
  35.                     try {  
  36.                         // 加载人脸检测模式文件  
  37.                         InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);  
  38.                         File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);  
  39.                         mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");  
  40.                         FileOutputStream os = new FileOutputStream(mCascadeFile);  
  41.                         byte[] buffer = new byte[4096];  
  42.                         int byteesRead;  
  43.                         while ((byteesRead = is.read(buffer)) != -1) {  
  44.                             os.write(buffer, 0, byteesRead);  
  45.                         }  
  46.                         is.close();  
  47.                         os.close();  
  48.                         // 使用模型文件初始化人脸检测引擎  
  49.                         mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());  
  50.                         if (mJavaDetector.empty()) {  
  51.                             Log.e(TAG, "加载cascade classifier失败");  
  52.                             mJavaDetector = null;  
  53.                         } else {  
  54.                             Log.d(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());  
  55.                         }  
  56.                         mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);  
  57.                         cascadeDir.delete();  
  58.                     } catch (FileNotFoundException e) {  
  59.                         e.printStackTrace();  
  60.                     } catch (IOException e) {  
  61.                         e.printStackTrace();  
  62.                     }  
  63.                     // 开启渲染Camera  
  64.                     mCameraView.enableView();  
  65.                     break;  
  66.                 default:  
  67.                     super.onManagerConnected(status);  
  68.                     break;  
  69.             }  
  70.         }  
  71.     };  
  72.   
  73.   
  74.     @Override  
  75.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  76.         super.onCreate(savedInstanceState);  
  77.         getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  78.         setContentView(R.layout.activity_facedetect);  
  79.         // 绑定View  
  80.         ButterKnife.bind(this);  
  81.         mCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);  
  82.         // 注册Camera渲染事件监听器  
  83.         mCameraView.setCvCameraViewListener(this);  
  84.     }  
  85.   
  86.   
  87.     @Override  
  88.     protected void onResume() {  
  89.         super.onResume();  
  90.         // 静态初始化OpenCV  
  91.         if (!OpenCVLoader.initDebug()) {  
  92.             Log.d(TAG, "无法加载OpenCV本地库,将使用OpenCV Manager初始化");  
  93.             OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_3_0, this, mLoaderCallback);  
  94.         } else {  
  95.             Log.d(TAG, "成功加载OpenCV本地库");  
  96.             mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);  
  97.         }  
  98.     }  
  99.   
  100.   
  101.     @Override  
  102.     protected void onPause() {  
  103.         super.onPause();  
  104.         // 停止渲染Camera  
  105.         if (mCameraView != null) {  
  106.             mCameraView.disableView();  
  107.         }  
  108.     }  
  109.   
  110.   
  111.     @Override  
  112.     protected void onDestroy() {  
  113.         super.onDestroy();  
  114.         // 停止渲染Camera  
  115.         if (mCameraView != null) {  
  116.             mCameraView.disableView();  
  117.         }  
  118.     }  
  119.   
  120.   
  121.     @Override  
  122.     public void onCameraViewStarted(int width, int height) {  
  123.         // 灰度图像  
  124.         mGray = new Mat();  
  125.         // R、G、B彩色图像  
  126.         mRgba = new Mat();  
  127.     }  
  128.   
  129.   
  130.     @Override  
  131.     public void onCameraViewStopped() {  
  132.         mGray.release();  
  133.         mRgba.release();  
  134.     }  
  135.   
  136.   
  137.     @Override  
  138.     public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {  
  139.         mRgba = inputFrame.rgba();  
  140.         mGray = inputFrame.gray();  
  141.         // 设置脸部大小  
  142.         if (mAbsoluteFaceSize == 0) {  
  143.             int height = mGray.rows();  
  144.             if (Math.round(height * mRelativeFaceSize) > 0) {  
  145.                 mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);  
  146.             }  
  147.             mNativeDetector.setMinFaceSize(mAbsoluteFaceSize);  
  148.         }  
  149.         // 获取检测到的脸部数据  
  150.         MatOfRect faces = new MatOfRect();  
  151.         if (mDetectorType == JAVA_DETECTOR) {  
  152.             if (mJavaDetector != null) {  
  153.                 mJavaDetector.detectMultiScale(mGray, faces, 1.122,  
  154.                         new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());  
  155.             }  
  156.         } else if (mDetectorType == NATIVE_DETECTOR) {  
  157.             if (mNativeDetector != null) {  
  158.                 mNativeDetector.detect(mGray, faces);  
  159.             }  
  160.         } else {  
  161.             Log.e(TAG, "Detection method is not selected!");  
  162.         }  
  163.         // 绘制检测框  
  164.         Rect[] facesArray = faces.toArray();  
  165.         for (int i = 0; i < facesArray.length; i++) {  
  166.             Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);  
  167.         }  
  168.   
  169.   
  170.         Log.i(TAG, "共检测到 " + faces.toArray().length + " 张脸");  
  171.         return mRgba;  
  172.     }  
  173. }  
2. activity_facedetect.xml
[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:opencv="http://schemas.android.com/apk/res-auto"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.   
  8.   
  9.     <org.opencv.android.JavaCameraView  
  10.         android:id="@+id/cameraView_face"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="match_parent"  
  13.         android:visibility="gone"  
  14.         opencv:camera_id="any"  
  15.         opencv:show_fps="true" />  
  16. </RelativeLayout>  
3. AndroidMnifest.xml
[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.jiangdg.opencv4android">  
  4.     <uses-permission android:name="android.permission.CAMERA"/>  
  5.   
  6.     <uses-feature android:name="android.hardware.camera" android:required="false"/>  
  7.     <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>  
  8.     <uses-feature android:name="android.hardware.camera.front" android:required="false"/>  
  9.     <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>  
  10.   
  11.     <supports-screens android:resizeable="true"  
  12.         android:smallScreens="true"  
  13.         android:normalScreens="true"  
  14.         android:largeScreens="true"  
  15.         android:anyDensity="true" />  
  16.   
  17.     <application  
  18.         android:allowBackup="true"  
  19.         android:icon="@mipmap/ic_launcher"  
  20.         android:label="@string/app_name"  
  21.         android:roundIcon="@mipmap/ic_launcher_round"  
  22.         android:supportsRtl="true"  
  23.         android:theme="@style/AppTheme">  
  24.         <activity android:name=".MainActivity">  
  25.             <intent-filter>  
  26.                 <action android:name="android.intent.action.MAIN" />  
  27.   
  28.                 <category android:name="android.intent.category.LAUNCHER" />  
  29.             </intent-filter>  
  30.         </activity>  
  31.   
  32.         <activity android:name=".HelloOpenCVActivity"  
  33.             android:screenOrientation="landscape"  
  34.             android:configChanges="keyboardHidden|orientation"/>  
  35.         <activity android:name=".FaceDetectActivity"  
  36.             android:screenOrientation="landscape"  
  37.             android:configChanges="keyboardHidden|orientation"/>  
  38.     </application>  
  39. </manifest>  
效果演示:



源码下载:https://github.com/jiangdongguo/OpenCV4Android(欢迎star & fork)


更新于2018-3-13

四、使用Cmake方式编译和升级到OpenCV3.4.1

在上面工程中,有两个不好的体验:(1)每次编译so时必须手动调用ndk-build命令;(2)在编写C/C++代码时没有代码提示,也没有报错警告。我想这两种情况无论是哪个都是让人感觉很不爽的,因此,今天打算在OpenCV3.4.1版本的基础上对项目进行重构下,使用Cmake方式来进行编译。

1. 新建main/app/cpp目录。将jni目录下的"DetectionBasedTracker_jni.cpp" 和"DetectionBasedTracker_jni.h" 文件拷贝到该目录下,并将opencv-3.4.1源码..\opencv-3.4.1-android-sdk\OpenCV-android-sdk\sdk\native\jni\目录下的整个include目录拷贝到该cpp目录下,同时可以删除整个app/src/main/jni目录


2. 新建main/app/jniLibs目录,将opencv-3.4.1源码中..\opencv-3.4.1-android-sdk\OpenCV-android-sdk\sdk\native\libs或staticlibs相关架构的动态库(.so)和静态库(.a)文件拷贝到该jniLibs目录下,同时删除libs、obj目录。


3. 在app目录下新建脚本文件CMakeLists.txt,该文件用于编写Cmake编译运行需要的脚本。需要注意的是,你在jniLibs目录导入了哪些静态库和动态库,在CmakeList.txt编写自动编译脚本时只能导入和链接这些库:

[cpp] view plaincopy
  1. # Sets the minimum version of CMake required to build the native  
  2. # library. You should either keep the default value or only pass a  
  3. # value of 3.4.0 or lower.  
  4.   
  5. cmake_minimum_required(VERSION 3.4.1)  
  6.   
  7.   
  8. # Creates and names a library, sets it as either STATIC  
  9. # or SHARED, and provides the relative paths to its source code.  
  10. # You can define multiple libraries, and CMake builds it for you.  
  11. # Gradle automatically packages shared libraries with your APK.  
  12.   
  13. set(CMAKE_VERBOSE_MAKEFILE on)  
  14. set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")  
  15. include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)  
  16.   
  17. #--------------------------------------------------- import ---------------------------------------------------#  
  18. add_library(libopencv_java3 SHARED IMPORTED )  
  19. set_target_properties(libopencv_java3 PROPERTIES  
  20.     IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")  
  21.   
  22. add_library(libopencv_core STATIC IMPORTED )  
  23. set_target_properties(libopencv_core PROPERTIES  
  24.     IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_core.a")  
  25.   
  26. add_library(libopencv_highgui STATIC IMPORTED )  
  27. set_target_properties(libopencv_highgui PROPERTIES  
  28.     IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_highgui.a")  
  29.   
  30. add_library(libopencv_imgcodecs STATIC IMPORTED )  
  31. set_target_properties(libopencv_imgcodecs PROPERTIES  
  32.     IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_imgcodecs.a")  
  33.   
  34. add_library(libopencv_imgproc STATIC IMPORTED )  
  35. set_target_properties(libopencv_imgproc PROPERTIES  
  36.     IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_imgproc.a")  
  37.   
  38. add_library(libopencv_objdetect STATIC IMPORTED )  
  39. set_target_properties(libopencv_objdetect PROPERTIES  
  40.     IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_objdetect.a")  
  41.   
  42. #add_library(libopencv_calib3d STATIC IMPORTED )  
  43. #set_target_properties(libopencv_calib3d PROPERTIES  
  44. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_calib3d.a")  
  45. #  
  46. #  
  47. #add_library(libopencv_dnn STATIC IMPORTED )  
  48. #set_target_properties(libopencv_core PROPERTIES  
  49. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_dnn.a")  
  50. #  
  51. #add_library(libopencv_features2d STATIC IMPORTED )  
  52. #set_target_properties(libopencv_features2d PROPERTIES  
  53. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_features2d.a")  
  54. #  
  55. #add_library(libopencv_flann STATIC IMPORTED )  
  56. #set_target_properties(libopencv_flann PROPERTIES  
  57. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_flann.a")  
  58. #  
  59. #add_library(libopencv_ml STATIC IMPORTED )  
  60. #set_target_properties(libopencv_ml PROPERTIES  
  61. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_ml.a")  
  62. #  
  63. #add_library(libopencv_photo STATIC IMPORTED )  
  64. #set_target_properties(libopencv_photo PROPERTIES  
  65. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_photo.a")  
  66. #  
  67. #add_library(libopencv_shape STATIC IMPORTED )  
  68. #set_target_properties(libopencv_shape PROPERTIES  
  69. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_shape.a")  
  70. #  
  71. #add_library(libopencv_stitching STATIC IMPORTED )  
  72. #set_target_properties(libopencv_stitching PROPERTIES  
  73. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_stitching.a")  
  74. #  
  75. #add_library(libopencv_superres STATIC IMPORTED )  
  76. #set_target_properties(libopencv_superres PROPERTIES  
  77. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_superres.a")  
  78. #  
  79. #add_library(libopencv_video STATIC IMPORTED )  
  80. #set_target_properties(libopencv_video PROPERTIES  
  81. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_video.a")  
  82. #  
  83. #add_library(libopencv_videoio STATIC IMPORTED )  
  84. #set_target_properties(libopencv_videoio PROPERTIES  
  85. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_videoio.a")  
  86. #  
  87. #add_library(libopencv_videostab STATIC IMPORTED )  
  88. #set_target_properties(libopencv_videostab PROPERTIES  
  89. #    IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_videostab.a")  
  90.   
  91. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -fexceptions -frtti")  
  92.   
  93. add_library( # Sets the name of the library.  
  94.              opencv341  
  95.   
  96.              # Sets the library as a shared library.  
  97.              SHARED  
  98.   
  99.              # Provides a relative path to your source file(s).  
  100.              # Associated headers in the same location as their source  
  101.              # file are automatically included.  
  102.              src/main/cpp/DetectionBasedTracker_jni.cpp)  
  103.   
  104. find_library( # Sets the name of the path variable.  
  105.               log-lib  
  106.   
  107.               # Specifies the name of the NDK library that  
  108.               # you want CMake to locate.  
  109.               log)  
  110.   
  111. target_link_libraries(opencv341 android log  
  112.     libopencv_java3 #used for java sdk  
  113.     #17 static libs in total  
  114.     #libopencv_calib3d  
  115.     libopencv_core  
  116.     #libopencv_dnn  
  117.     #libopencv_features2d  
  118.     #libopencv_flann  
  119.     libopencv_highgui  
  120.     libopencv_imgcodecs  
  121.     libopencv_imgproc  
  122.     #libopencv_ml  
  123.     libopencv_objdetect  
  124.     #libopencv_photo  
  125.     #libopencv_shape  
  126.     #libopencv_stitching  
  127.     #libopencv_superres  
  128.     #libopencv_video  
  129.     #libopencv_videoio  
  130.     #libopencv_videostab  
  131.     ${log-lib}  
  132.     )  

4. 右击选中app/src/main/cpp目录,选择"Link C++ Project with Gradle"并浏览选择本项目中的CmakeLists.txt文件,将C++环境关联到gradle


5. 将工程中的openCVLibrary330 module更新到openCVLibrary341,修改app目录下的gradle.build文件

[cpp] view plaincopy
  1. apply plugin: 'com.android.application'  
  2.   
  3. android {  
  4.     compileSdkVersion 25  
  5.     defaultConfig {  
  6.         //...代码省略  
  7.         externalNativeBuild {  
  8.             cmake {  
  9.                 arguments "-DANDROID_ARM_NEON=TRUE""-DANDROID_TOOLCHAIN=clang","-DCMAKE_BUILD_TYPE=Release"  
  10.                 //'-DANDROID_STL=gnustl_static'  
  11.                 cppFlags "-std=c++11","-frtti""-fexceptions"  
  12.             }  
  13.         }  
  14.         // 设置输出指定目标平台so  
  15.         ndk{  
  16.             abiFilters 'armeabi-v7a','arm64-v8a','armeabi'  
  17.         }  
  18.     }  
  19.     // ...代码省略  
  20.     externalNativeBuild {  
  21.         cmake {  
  22.             path 'CMakeLists.txt'  
  23.         }  
  24.     }  
  25. }  
  26.   
  27. dependencies {  
  28.     //.. 代码省略  
  29.     implementation project(':openCVLibrary341')  
  30. }  
至此,该工程重构完毕,切换到cpp文件中,码个Mat看下成效~

相关文章:

活动|跟着微软一起,拥抱开源吧!

由开源社主办的中国开源年会2016 (COSCon16 - China Open Source Conference 2016) 即将于今年10月15日-16日在北京举办。微软大咖将为您呈现区块链&#xff0c;容器&#xff0c;大数据&#xff0c;Xamarin等时下热点技术&#xff0c;参会者还可获取价值1,500 元 Azure 服务使用…

【HDU/算法】最短路问题 杭电OJ 2544 (Dijkstra,Dijkstra+priority_queue,Floyd,Bellman_ford,SPFA)

最短路径问题是图论中很重要的问题。 解决最短路径几个经典的算法 1、Dijkstra算法 单源最短路径&#xff08;贪心&#xff09;&#xff0c;还有用 priority_queue 进行优化的 Dijkstra 算法。 2、bellman-ford算法 例题&#xff1a;【ACM】POJ 3259 Wormholes 允许负权边…

javaSE基础知识 1.5整数类型

整数的四种声明类型它们分别是&#xff0c;byte&#xff0c;short&#xff0c;int&#xff0c;long&#xff0c;这四种类型所占用的空间是不同的byte是占用1个字节&#xff0c;它的取值范围是 -128~127&#xff0c;short是占用2个字节&#xff0c;他的取值范围是-32768~32767&a…

源码分析-GLSurfaceView的内部实现

GLSurfaceView类是继承自SurfaceView的&#xff0c;并且实现了SurfaceHolder.Callback2接口。GLSurfaceView内部管理着一个surface&#xff0c;专门负责OpenGL渲染。GLSurfaceView内部通过GLThread和EGLHelper为我们完成了EGL环境渲染和渲染线程的创建及管理&#xff0c;使我们…

【POJ/算法】 3259 Wormholes(Bellman-Ford算法, SPFA ,FLoyd算法)

Bellman-Ford算法 Bellman-Ford算法的优点是可以发现负圈&#xff0c;缺点是时间复杂度比Dijkstra算法高。而SPFA算法是使用队列优化的Bellman-Ford版本&#xff0c;其在时间复杂度和编程难度上都比其他算法有优势。 Bellman-Ford算法流程分为三个阶段&#xff1a; 第一步&am…

进程控制概念简介 多线程上篇(三)

进程控制 进程的基本数据信息是操作系统控制管理进程的数据集合&#xff0c;这些信息就是用来控制进程的&#xff0c;此处我们说的进程控制就是进程的管理。比如进程有状态&#xff0c;那么进程的创建、终止&#xff0c;状态的切换&#xff0c;这都不是进程自主进行的&#xff…

Android OpenGL使用GLSurfaceView预览视频

Android OpenGL使用GLSurfaceView预览视频第一章 相关知识介绍在介绍具体的功能之前&#xff0c;先对一些主要的类和方法进行一些介绍&#xff0c;这样可以更好的理解整个程序1.1 GLSurfaceView在谷歌的官方文档中是这样解释GLSurfaceView的&#xff1a;An implementation of S…

【Android 基础】Animation 动画介绍和实现

转载自&#xff1a;http://www.cnblogs.com/yc-755909659/p/4290114.html1.Animation 动画类型Android的animation由四种类型组成&#xff1a;XML中alph渐变透明度动画效果scale渐变尺寸伸缩动画效果translate画面转换位置移动动画效果rotate画面转移旋转动画效果JavaCode中Alp…

【Codeforces】1111B - Average Superhero Gang Power

http://codeforces.com/problemset/problem/1111/B n 表示要输入的数据的个数 k 最每一个数据最多可以进行多少次操作 m 一共可以进行多少次操作 一次操作&#xff1a;删除这个数&#xff0c;或者给这个数加1 如果n为1的话&#xff0c;那么只要找出m和k的最小值加到那个数…

刷前端面经笔记(七)

1.描述一下渐进增强和优雅降级 优雅降级(graceful degradation)&#xff1a;一开始就构建站点的完整功能&#xff0c;然后针对浏览器测试和修复。渐进增强(progressive enhancement)&#xff1a;一开始只构建站点的最少特性&#xff0c;然后不断针对各浏览器追加功能。 2.为什么…

AR资料与连接梳理

AR引擎相关技术 ------------------------------ ARcore&#xff1a;https://developers.google.cn/ar/discover/ ARkit&#xff1a;https://developer.apple.com/arkit/ 以上重点关注&#xff0c;比较新有一些新的功能大家可以自行体验。 ARToolkithttp://www.artoolkit.orght…

Queues 队列

1. Definiation What is a queue? A queue is a list. With a queue, inseration is done at one end (known as rear) whereas deletion is performed at the other end (known as front). 2. Operations 指针对列 无法自定义队长 // array queue #include<iostream> u…

【HDU】1005 Number Sequence (有点可爱)

http://acm.hdu.edu.cn/showproblem.php?pid1005 A number sequence is defined as follows: f(1) 1, f(2) 1, f(n) (A * f(n - 1) B * f(n - 2)) mod 7. Given A, B, and n, you are to calculate the value of f(n). 直接递归求解f(n)的话&#xff0c;会MLE 在计算…

CNCF案例研究:奇虎360

公司&#xff1a;奇虎360地点&#xff1a;中国北京行业&#xff1a;计算机软件 挑战 中国软件巨头奇虎360科技的搜索部门&#xff0c;so.com是中国第二大搜索引擎&#xff0c;市场份额超过35&#xff05;。该公司一直在使用传统的手动操作来部署环境&#xff0c;随着项目数量的…

C#代码实现对Windows凭据的管理

今天有个任务&#xff0c;那就是使用C#代码实现对windows凭据管理的操作。例如&#xff1a;向windows凭据管理中添加凭据、删除凭据以及查询凭据等功能。于是乎&#xff0c;就开始在网上查找。经过漫长的查询路&#xff0c;终于在一片英文博客中找到了相关代码。经过实验&#…

Android:JNI 与 NDK到底是什么

前言 在Android开发中&#xff0c;使用 NDK开发的需求正逐渐增大但很多人却搞不懂 JNI 与 NDK 到底是怎么回事今天&#xff0c;我将先介绍JNI 与 NDK & 之间的区别&#xff0c;手把手进行 NDK的使用教学&#xff0c;希望你们会喜欢 目录 1. JNI介绍 1.1 简介 定义&…

【ACM】LightOJ - 1008 Fibsieve`s Fantabulous Birthday (找规律,找...)

https://vjudge.net/problem/LightOJ-1008 题目很好理解&#xff0c;第一行表示测试样例的个数&#xff0c;接下来输入一个大于等于1的数&#xff0c;按照格式输出这个数的坐标 蓝色的是 奇数的平方&#xff1b; 红色的是 偶数的平方&#xff1b; 黄色的是对角线&#xff1a…

Computed property XXX was assigned to but it has no setter

报错视图&#xff1a; 原因&#xff1a; 组件中v-model“XXX”&#xff0c;而XXX是vuex state中的某个变量vuex中是单项流&#xff0c;v-model是vue中的双向绑定&#xff0c;但是在computed中只通过get获取参数值&#xff0c;没有set无法改变参数值解决方法&#xff1a; 1.在co…

OpenGL 矩阵变换

origin refer :http://www.songho.ca/opengl/gl_transform.html#modelviewOpenGL 矩阵变换Related Topics: OpenGL Pipeline, OpenGL Projection Matrix, OpenGL Matrix Class Download: matrixModelView.zip, matrixProjection.zipOverviewOpenGL Transform MatrixExample: GL…

2016.8.11 DataTable合并及排除重复方法

合并&#xff1a; DataTable prosxxx; DataTable pstaryyy; //将两张DataTable合成一张 foreach (DataRow dr in pstar.Rows) { pros.ImportRow(dr); } DataTable设置主键&#xff0c;并判断重复 DataTable allpros xxx; 单列设为主键&#xff1a; //设置第某列为主键 allpros.…

【ACM】LightOJ - 1010 Knights in Chessboard(不是搜索...)

https://vjudge.net/problem/LightOJ-1010 给定一个mn的棋盘&#xff0c;你想把棋子放在哪里。你必须找到棋盘上最多可以放置的骑士数量&#xff0c;这样就不会有两个骑士互相攻击。不熟悉棋手的注意&#xff0c;棋手可以在棋盘上攻击8个位置&#xff0c;如下图所示。 不论输入…

webpack-dev-server 和webapck --watch的区别

webpack-dev-server 和webapck --watch 都可以监测到代码变化 &#xff0c; 区别是&#xff1a;webpack-der-server 监测到代码变化后&#xff0c;浏览器可以看到及时更新的效果&#xff0c;但是并没有自动打包修改的代码&#xff1b; webpack --watch 在监测到代码变化后自动打…

Android 应用进行性能分析/APP/系统性能分析

如何对 Android 应用进行性能分析记录一下自己在使用DDMS的过程&#xff1a;开启AS&#xff0c;打开并运行项目 找到TOOL/选择Android Device Monitor一款 App 流畅与否安装在自己的真机里&#xff0c;玩几天就能有个大概的感性认识。不过通过专业的分析工具可以使我们更好的分…

公钥与私钥,HTTPS详解

1.公钥与私钥原理1)鲍勃有两把钥匙&#xff0c;一把是公钥&#xff0c;另一把是私钥2)鲍勃把公钥送给他的朋友们----帕蒂、道格、苏珊----每人一把。3)苏珊要给鲍勃写一封保密的信。她写完后用鲍勃的公钥加密&#xff0c;就可以达到保密的效果。4)鲍勃收信后&#xff0c;用私钥…

【ACM】杭电OJ 4704 Sum (隔板原理+组合数求和公式+费马小定理+快速幂)

http://acm.hdu.edu.cn/showproblem.php?pid4704 1.隔板原理 1~N有N个元素&#xff0c;每个元素代表一个1.分成K个数&#xff0c;即在(N-1)个空挡里放置&#xff08;K-1&#xff09;块隔板 即求组合数C(N-1,0)C(N-1,1)...C(N-1&#xff0c;N-1) 2.组合数求和公式 C(n,0)C(…

Vue 中 CSS 动画原理

下面这段代码&#xff0c;是点击按钮实现hello world显示与隐藏 <div id"root"><div v-if"show">hello world</div><button click"handleClick">按钮</button> </div> let vm new Vue({el: #root,data: {s…

【ACM】UVA - 340 Master-Mind Hints(一定要好好学英语...)

https://vjudge.net/problem/UVA-340 N 表示 密码的个数 第一行是正确的密码 下面的行直到N个0之前&#xff0c;都是猜测的序列&#xff0c;输出的括号&#xff08;A&#xff0c;B&#xff09; A表示对应位置与密码相符合的个数&#xff0c;B表示出现在序列中的数字但是位…

SLAM的前世今生

SLAM的前世 从研究生开始切入到视觉SLAM领域&#xff0c;应用背景为AR中的视觉导航与定位。 定位、定向、测速、授时是人们惆怅千年都未能完全解决的问题&#xff0c;最早的时候&#xff0c;古人只能靠夜观天象和司南来做简单的定向。直至元代&#xff0c;出于对定位的需求&a…

No resource found that matches the given name '@style/Theme.AppCompat.Light'

为什么80%的码农都做不了架构师&#xff1f;>>> Android导入项目时出现此问题的解决办法&#xff1a; 1.查看是否存在此目录&#xff08;D:\android-sdk\extras\android\support\v7\appcompat&#xff09;&#xff0c;若没有此目录&#xff0c;在项目右键Android T…

极限编程 (Extreme Programming) - 迭代计划 (Iterative Planning)

(Source: XP - Iteration Planning) 在每次迭代开始时调用迭代计划会议&#xff0c;以生成该迭代的编程任务计划。每次迭代为1到3周。 客户从发布计划中按照对客户最有价值的顺序选择用户故事进行此次迭代。还选择了要修复的失败验收测试。客户选择的用户故事的估计总计达到上次…