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 );

 

  祝一切顺利,以上。 
posted @ 2017-09-08 10:42  负赑屃  阅读(2082)  评论(0编辑  收藏  举报