zoukankan      html  css  js  c++  java
  • 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 &locationappsrcmainjni -classpath &locationappsrcmainjava 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转入appsrcmainjni目录下,输入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 );
      祝一切顺利,以上。 
  • 相关阅读:
    java8新特性学习:stream与lambda
    Storm实践(一):基础知识
    Google Protocol Buffer入门
    zeromq实践
    maven实践--你所需要了解的maven
    springcloud实践(一)服务发现:Eureka
    职责链模式(chain of responsibility Pattern)
    代理模式 (Proxy Pattern)
    享元模式(Flyweight Pattern)
    外观模式(Facade Pattern)
  • 原文地址:https://www.cnblogs.com/mar-q/p/7493397.html
Copyright © 2011-2022 走看看