OpenCV4Android背景建模(MOG、MOG2)
本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃
很久以前的笔记了,分享给大家吧。。。OpenCV4Android中用于背景建模的类主要有:BackgroundSubtractor、BackgroundSubtractorMOG、BackgroundSubtractorMOG2、BackgroundSubtractorKNN,主要对使用方法做个总结。
借用OpenCV提供的API,Android编程可以实现比较丰富的视觉处理效果。经过多次尝试,终于梳理出OpenCV背景建模在Android中的使用方法。BackgroundSubtractor的java API在OpenCV2.x和3.x版本有较大区别,3.x提供了比较丰富的API接口,取消了2.4中的高斯背景建模1(BackgroundSubtractorMOG),保留了高斯背景建模2(BackgroundSubtractorMOG2),引入了KNN背景建模(BackgroundSubtractorKNN)。同时提供了很多访问BackgroundSubtractor背景模型的属性方法,例如getBackgroundImage等。废话不多说,总结如下:
一、通过Java接口实现背景建模
无论在OpenCV2.x还是3.x,BackgroundSubtractor类都必须在帧循环处理之前定义,例如:给OpenCV自带的Tutorial2 例程增加一个BackgroundSubtractorMOG2类的前景侦测方法。
1、定义一个BackgroundSubtractorMOG2 对象,Tutorial2 例程对于用到的mat类对象定义为private全局变量,参照给出定义:
public class Tutorial2Activity extends Activity implements CvCameraViewListener2 { …… private Mat mRgba; private Mat mIntermediateMat; private Mat mGray; private BackgroundSubtractorMOG2 mog2;
2、mog2对象初始化,Tutorial2 例程在onCameraViewStarted方法中对mat对象进行初始化,参照执行:
public void onCameraViewStarted(int width, int height) { mRgba = new Mat(height, width, CvType.CV_8UC4); mIntermediateMat = new Mat(height, width, CvType.CV_8UC4); mGray = new Mat(height, width, CvType.CV_8UC1); mog2 = new BackgroundSubtractorMOG2(); //OpenCV2.x初始化方法 //mog2 = video.createBackgroundSubtractorMOG2();//OpenCV3.x初始化方法 }
需要注意的是OpenCV2.x初始化BackgroundSubtractorMOG2对象是通过new来实现,但是在3.x版本中是通过mog2 = Video.createBackgroundSubtractorMOG2()来实现。
3、执行背景建模,直接把例程中的canny方法改成BackgroundSubtractor:
case BUTTON_GETBG: mRgba = inputFrame.rgba(); bg2.apply(inputFrame.gray(),mIntermediateMat,0.01);//前景保存在mIntermediateMat中 //bg2.getBackgroundImage(mBG); //OpenCV3.x提供获取背景函数
由于OpenCV2.x没有提供getBackgroundImage方法,让我们折腾折腾,通过jni给它实现一下:
二、通过jni实现背景建模
首先你得大致会jni,不在这里展开讲,推荐看一下http://www.cnblogs.com/linguanh/p/4624768.html,做好准备工作。
1、定义GetBGImage类继承BackgroundSubtractorMOG2类,并定义其中的native方法:
/** * 因为OpenCV2.4中没有提供BackgroundSubtractorMOG2类getBackgroundImage的API接口, * 所以通过FindBackground类定义获取背景的方法,FindBackground类的初始化工作和 * BackgroundSubtractorMOG2类一致,必须在Frame循环之外完成,获取背景的方法在帧循环体内。 */ public class GetBGImage extends BackgroundSubtractorMOG2{ //继承自BackgroundSubtractorMOG2,其构造方法继承父类构造方法 public GetBGImage (int history, float varThreshold, boolean bShadowDetection) { super( history, varThreshold, bShadowDetection) ; return; } //FindBackground extends BackgroundSubtractorMOG2 extends BackgroundSubtractor extends Algorithm类 //algorithm类定义了long类型的nativeobj public long getNativeObjAddr() { return nativeObj; } public void FindFeature(Mat mGr, Mat mBG){ FindFeatures(nativeObj, mGr.nativeObj, mBG.nativeObj); return; } public static native void FindFeatures(long nativeObj,long mGr_nativeObj, long mBG_nativeObj); }
2、编写Java_com_example_Myapplication_GetBGImage.cpp代码:
#include <jni.h> #include <opencv2/core/core.hpp> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/features2d/features2d.hpp> #include <vector> #include <android/log.h> #include <sys/time.h> #include <opencv2/video/background_segm.hpp> #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ProjectName", __VA_ARGS__) using namespace std; using namespace cv; //异常处理,可以不要 static void throwJavaException(JNIEnv *env, const std::exception *e, const char *method) { std::string what = "unknown exception"; jclass je = 0; if(e) { std::string exception_type = "std::exception"; if(dynamic_cast<const cv::Exception*>(e)) { exception_type = "cv::Exception"; je = env->FindClass("org/opencv/core/CvException"); } what = exception_type + ": " + e->what(); } if(!je) je = env->FindClass("java/lang/Exception"); env->ThrowNew(je, what.c_str()); LOGE("%s caught %s", method, what.c_str()); (void)method; // avoid "unused" warning } //方法主体,一定要以extern "C"{开头 extern "C" { JNIEXPORT void JNICALL Java_com_example_Myapplication_GetBGImage_FindFeatures(JNIEnv* , jobject , jlong self, jlong addrGray, jlong addrBG); //在java native方法中传入的参数主要有三个,分别对应self、addrGray、addrBG,其中self是自定义的GetBGImage类long类型的nativeobj,它可以作为一个指针指向你定义的GetBGImage类对象。 //当对象指针通过jni传入C++后,你可以通过GetBGImage的父类BackgroundSubtractorMOG2的指针指向这个对象,并在通过指针调用C的接口,从而实现对getBackgroundImage方法的调用。 JNIEXPORT void JNICALL Java_com_example_Myapplication _GetBGImage_FindFeatures(JNIEnv* env, jobject , jlong self, jlong addrGray, jlong addrBG) { static const char method_name[] ="FindFeatures->getBackgroundImage()getFrontMaskImage()"; try{ LOGE("%s",method_name); Mat& mGr = *(Mat*)addrGray; Mat& mBG = *(Mat*)addrBG; cv::BackgroundSubtractorMOG2* me = (cv::BackgroundSubtractorMOG2*) self; me->operator()(mGr, mBG,0.001); me->getBackgroundImage(mGr); return; }catch(const std::exception &e) { throwJavaException(env, &e, method_name); } catch (...) { throwJavaException(env, 0, method_name); } return; } }
注:需要调用的C++函数需在native里声明,声明的文件名要与编写的cpp文件一致,例如Java_com_example_Myapplication_GetBGImage_FindFeatures对应的FindFeatures。
3、生成.h头文件和.so库文件
生成.h头文件(在as terminal中输入命令:javah -d &location\app\src\main\jni -classpath &location\app\src\main\java com.example.Myapplication.GetBGImage),如果一切顺利则不会报错,并生成Java_com_example_Myapplication_GetBGImage.h文件;
编写Android.mk文件、application.mk文件,具体请参照Tutorial2或相关博客,提示一句:如果需要mk多个cpp文件(例如1.cpp,2.cpp),请在标记LOCAL_SRC_FILES :=1.cpp LOCAL_SRC_FILES := 2.cpp,当然还有更通用的写法,可以自己查找,这里不再介绍。
编译.so文件,as terminal转入app\src\main\jni目录下,输入ndk-build,如果一切顺利则会提示生成了一系列.so文件,同时你的project的main目录下会生成libs和obj两个目录。这就表示编译成功,在你的mainActivity里加载这个.so文件吧,加载方法:
BaseLoaderCallback中加载System.loadLibrary("xxxx"); //.so文件名称一般为libxxxx.so
这一步很关键,也很容易出错,请耐心调试,如果我的方法不适用可以多搜别人的博客看看。
4、完成以上步骤,就可以放心使用GetBGImage的FindFeature方法了。步骤可以参考java背景建模,首先预定义GetBGImage类对象
public class Tutorial2Activity extends Activity implements CvCameraViewListener2 { …… private Mat mRgba; private Mat mIntermediateMat; private Mat mGray; private GetBGImage mog2;
在onCameraViewStarted方法中进行初始化,这里的参数请参照背景建模的需求进行调试。
public void onCameraViewStarted(int width, int height) { mRgba = new Mat(height, width, CvType.CV_8UC4); mIntermediateMat = new Mat(height, width, CvType.CV_8UC1); mGray = new Mat(height, width, CvType.CV_8UC1); //GetBGImage类继承自BackgroundSubtractorMOG2,其构造方法继承父类构造方法 bg2 = new GetBGImage(30,16,false);
在onCameraFrame方法中调用FindFeature
public Mat onCameraFrame(CvCameraViewFrame inputFrame) { mGray = inputFrame.gray(); bg2.FindFeature(mGray,mIntermediateMat );
祝一切顺利,以上。