Android OpenCV VideoCapture :: retrieve(& mat)导致致命信号11

时间:2014-07-22 16:49:23

标签: android opencv android-ndk

所以我试图创建一个Android应用程序,它将使用OpenCV通过JNI和NDK逐帧打开视频文件,检测每个帧中的面部并显示它们(最终目标是加速算法,更好地适应糟糕的移动处理。)

我尝试的第一种方法是MediaMetadataRetriever没有结果,它只返回null。

第二种方法是FFMpegMediaMetadataRetriever,它按预期工作,但速度非常慢(最高2fps;太慢),部分原因是你必须从FFMpegMMR的位图转换为Mat然后检测到绘制然后转换回位图,这是我甚至尝试的穴居人。

我目前正在研究的第三种方法是VideoCapture grab()和retrieve()。我为本机代码创建了一个包装器,主要是通过从OpenCV示例复制facestector。我也试过read(),据说将两者结合起来,但它也会导致致命的信号11(OpenCV或Android平台的某些上​​帝级别的分段错误)。

以下是应用程序获取绝对文件路径的方法(注意它确实适用于FFMpegMMR):

    //The file name, file path and filerawresource are used in getting the path of the video file on device's disk
String videoFileName="samplemp4";
String videoFileType=".mp4";
int videoFileRawResourceId=R.raw.samplemp4;

public String getVideoFilePath()
{
    InputStream is = getResources().openRawResource(videoFileRawResourceId);
    File sampleDir = getDir(videoFileName, Context.MODE_PRIVATE);
    File sampleFile= new File(sampleDir, videoFileName+videoFileType);
    try {
    FileOutputStream os = new FileOutputStream(sampleFile);

    byte[] buffer = new byte[4096];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        os.write(buffer, 0, bytesRead);
    }

        is.close();
        os.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    File videoDir = getDir(videoFileName, Context.MODE_PRIVATE);
    File videoFile = new File(videoDir, videoFileName+videoFileType);
    return videoFile.getAbsolutePath();
}

这是调用本机方法的java包装器并使用结果的地方:

class AsyncPlay extends AsyncTask<String, Mat, Bitmap>
        {

        @Override
        protected Bitmap doInBackground(String... params) {
            //for(;play;)
            {
                if(play)
                {
                    if(currentTime==0)
                    {
                        test=new Test();
                        test.startTime.setToNow();
                        test.type="Video";
                        frameGrabber.open(videoFilePath);
                    }
            //publishProgress(retriever.getFrameAtTime(currentTime*1000+111,
                    //      FFmpegMediaMetadataRetriever.OPTION_CLOSEST));


                    Mat tmp=new Mat();
                    //frameGrabber.read(tmp);
                    frameGrabber.grab();
                    frameGrabber.retrieve(tmp);
                    publishProgress(tmp);

                    currentTime+=111;
                    if(currentTime*1000>=duration*1000)
                    {
                        currentTime=0;
                        test.endTime.setToNow();
                        tester.publishResult(test);
                        frameGrabber.release();
                    }
                }
                try 
                {
                    Thread.sleep(111);
                } catch (InterruptedException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return null;
        }
        @Override
        protected void onProgressUpdate(Mat... values) {

            //face detection logic goes here
            //Bitmap bmp=values[0];

            //Mat rgbMat = new Mat();
            //Utils.bitmapToMat(bmp, rgbMat);

            DetectionResult detectionResult = openCVFaceDetector.detectFromImage(values[0], Imgproc.COLOR_RGB2GRAY);//Detecting with a native detector from OpenCV

            test.addDetection(detectionResult.detection);

            imageViewOriginal.setImageBitmap(detectionResult.detectedImage);

            super.onProgressUpdate(values);
        }

    };`

其中FrameGrabber frameGrabber是本机的java包装器,并且当调用retrieve()或read()时发送信号,请注意构造函数open()和grab()都可以工作(或者至少不要#39 ; t崩溃应用程序)。

这是包装器(我已正确传递了VideoCapture对象,我了解到这是导致致命信号的原因之一):

import org.opencv.core.Mat;

public class FrameGrabber
{ 
    private long mNativeObj = 0;
    private static native long nativeCreateObject(String fileName);
    private static native void nativeDestroyObject(long thiz);
    private static native boolean nativeOpen(long thiz, String fileName);
    private static native boolean nativeGrab(long thiz);
    private static native boolean nativeRetrieve(long thiz, Mat imageMat);
    private static native boolean nativeRead(long thiz, Mat imageMat);
    private static native void nativeRelease(long thiz);    
    public FrameGrabber(String fileName) {
        mNativeObj = nativeCreateObject(fileName);
    }
    public void destroy() {
        nativeDestroyObject(mNativeObj);
        mNativeObj = 0;
    }
    public boolean open(String fileName)
    {
        return nativeOpen(mNativeObj, fileName);
    }  
    public boolean grab()
    {
        return nativeGrab(mNativeObj);
    }   
    public boolean retrieve(Mat imageMat)
    {
        return nativeRetrieve(mNativeObj, imageMat);
    }    
    public boolean read(Mat imageMat)
    {
        return nativeRead(mNativeObj, imageMat);
    }    
    public void release()
    {
        nativeRelease(mNativeObj);
    }
}

这就是它的原生部分(导致错误的确切行是result = ((VideoCapture*)thiz)->retrieve((*((Mat*)imageMat)));,这是方法nativeRetrieve()

#include <string>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <FrameGrabber_jni.h>

#include <android/log.h>

#define LOG_TAG "FrameGrabber"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

using namespace std;
using namespace cv;

inline void jStringToString(JNIEnv * jenv, jstring jString, string stdName)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_jStringToString enter");
    LOGD("grabber is Making jnamestr");//this is buggy as hell, the line serves as a debug tool
    const char* jnamestr = jenv->GetStringUTFChars(jString, NULL);
    string stdNameTmp(jnamestr);
    stdName=stdNameTmp;
    LOGD("grabber is releasing jnamestr");
    jenv->ReleaseStringUTFChars(jString, jnamestr);
}

/*
 * CONSTRUCTOR AND DESTRUCTOR
 */
JNIEXPORT jlong JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeCreateObject
(JNIEnv * jenv, jclass, jstring jFileName)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_nativeCreateObject enter");
    string stdFileName;
    jStringToString(jenv, jFileName,stdFileName);
    jlong result = 0;

    try
    {
        result = (jlong)new VideoCapture(stdFileName);
    }
    catch(cv::Exception& e)
    {
        LOGD("nativeCreateObject caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("nativeCreateObject caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.nativeCreateObject()");
        return 0;
    }

    LOGD("Java_boris_springar_diplomska_FrameGrabber_nativeCreateObject exit");
    return result;
}

//should work
JNIEXPORT void JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeDestroyObject
(JNIEnv * jenv, jclass, jlong thiz)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_nativeDestroyObject enter");
    try
    {
        if(thiz != 0)
        {
            delete (VideoCapture*)thiz;
        }
    }
    catch(cv::Exception& e)
    {
        LOGD("nativeestroyObject caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("nativeDestroyObject caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.nativeDestroyObject()");
    }
    LOGD("Java_boris_springar_diplomska_FrameGrabber_nativeDestroyObject exit");
}


/*
 * CORE METHODS
 */

//Open function opens the filename for playing
JNIEXPORT jboolean JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeOpen
(JNIEnv * jenv, jclass,jlong thiz,jstring jFileName)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_open enter");
    string stdFileName;
    jStringToString(jenv, jFileName,stdFileName);

    jboolean result = false;

    try
    {
        result = ((VideoCapture*)thiz)->open(stdFileName);
    }
    catch(cv::Exception& e)
    {
        LOGD("frame grabber open exception caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("frame grabber open caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.open()");
        return 0;
    }

    LOGD("Java_boris_springar_diplomska_FrameGrabber_open exit");

    return result;
}

//grab grabs the next frame from file or camera
JNIEXPORT jboolean JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeGrab
(JNIEnv * jenv, jclass,jlong thiz)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_grab enter");
    jboolean result = false;

    try
    {
        result = ((VideoCapture*)thiz)->grab();
    }
    catch(cv::Exception& e)
    {
        LOGD("frame grabber grab exception caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("frame grabber grab caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.grab()");
        return 0;
    }

    LOGD("Java_boris_springar_diplomska_FrameGrabber_grab exit");

    return result;
}

//retrieve retrieves the next frame and writes it to the image matrix
JNIEXPORT jboolean JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeRetrieve
(JNIEnv * jenv, jclass,jlong thiz, jlong imageMat)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_retrieve enter");
    jboolean result = false;

    try
    {
        LOGD("grabber trying to retrieve");
        result = ((VideoCapture*)thiz)->retrieve((*((Mat*)imageMat)));//should write the current frame to the image matrix
    }
    catch(cv::Exception& e)
    {
        LOGD("frame grabber retrieve exception caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("frame grabber retrieve caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.retrieve(fileName)");
        return 0;
    }

    LOGD("Java_boris_springar_diplomska_FrameGrabber_retrieve exit");

    return result;
}

//read combines grab and retrieve and writes the stuff to the image matrix
JNIEXPORT jboolean JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeRead
(JNIEnv * jenv, jclass,jlong thiz, jlong imageMat)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_read enter");
    LOGD("grabber setting result to false");
    jboolean result = false;

    try
    {
        LOGD("grabber trying to read capture");
        result = ((VideoCapture*)thiz)->read((*((Mat*)imageMat)));//should write the current frame to the image matrix
    }
    catch(cv::Exception& e)
    {
        LOGD("frame grabber read exception caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("frame grabber read caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.open(fileName)");
        return 0;
    }

    LOGD("Java_boris_springar_diplomska_FrameGrabber_read exit");

    return result;
}

//Release releases the resource it's using, I hope
JNIEXPORT void JNICALL Java_boris_springar_diplomska_FrameGrabber_nativeRelease
(JNIEnv * jenv, jclass,jlong thiz)
{
    LOGD("Java_boris_springar_diplomska_FrameGrabber_release enter");
    jboolean result = false;

    try
    {
        ((VideoCapture*)thiz)->release();//should release
    }
    catch(cv::Exception& e)
    {
        LOGD("frame grabber read exception caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("frame grabber release caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of FrameGrabber.open(fileName)");
    }

    LOGD("Java_boris_springar_diplomska_FrameGrabber_release exit");
}

还有LogCat输出:

07-22 17:36:24.886: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_nativeCreateObject enter
07-22 17:36:24.886: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_jStringToString enter
07-22 17:36:24.886: D/FrameGrabber(15359): grabber is Making jnamestr
07-22 17:36:24.886: D/FrameGrabber(15359): grabber is releasing jnamestr
07-22 17:36:24.886: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_nativeCreateObject exit
07-22 17:36:24.936: I/dalvikvm(15359): threadid=3: reacting to signal 3
07-22 17:36:24.936: I/dalvikvm(15359): Wrote stack traces to '/data/anr/traces.txt'
07-22 17:36:24.946: D/libEGL(15359): loaded /system/lib/egl/libEGL_mali.so
07-22 17:36:24.956: D/libEGL(15359): loaded /system/lib/egl/libGLESv1_CM_mali.so
07-22 17:36:24.976: D/libEGL(15359): loaded /system/lib/egl/libGLESv2_mali.so
07-22 17:36:25.006: D/OpenGLRenderer(15359): Enabling debug mode 0
07-22 17:36:27.336: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_open enter
07-22 17:36:27.336: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_jStringToString enter
07-22 17:36:27.336: D/FrameGrabber(15359): grabber is Making jnamestr
07-22 17:36:27.336: D/FrameGrabber(15359): grabber is releasing jnamestr
07-22 17:36:27.336: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_open exit
07-22 17:36:27.336: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_grab enter
07-22 17:36:27.336: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_grab exit
07-22 17:36:27.336: D/FrameGrabber(15359): Java_boris_springar_diplomska_FrameGrabber_retrieve enter
07-22 17:36:27.336: D/FrameGrabber(15359): grabber trying to retrieve
07-22 17:36:27.336: A/libc(15359): Fatal signal 11 (SIGSEGV) at 0x1d400019 (code=1)

调试此代码的问题在于它是Eclipse中的JNI,我不知道如何在Eclipse中调试c ++,这就是我使用日志消息查找的原因代码中的错误。那些帮助非常大,因为open()和grab()没有工作,我发现并压扁了一个bug,我忘了将thiz传递给方法。 retrieve()的问题在于我无法为我的生活找到它的来源。在highgui.hpp中有部分定义,但没有实现,我可以在其中放置日志消息来帮助我调试。

可能的解决方案是:

  • retrieve()支持的其他文件格式,但我不知道哪种文件格式可能是
  • 使用非绝对文件路径?虽然open()和grab()都没有导致致命信号
  • 废弃整个东西并使用PNG(我真的很想弄明白)

所以,如果有人能告诉我在哪里可以找到retrieve()和read()的实现,我将非常感激。或者,如果我错过了一些明显和愚蠢的事情(我希望)。

与此同时,我会尝试在Eclipse中找到一种调试c ++的方法,也许会尝试其他格式?

1 个答案:

答案 0 :(得分:1)

最后,根据Aleksander的建议找到它,我设法找到从Java传递给JNI的对象的类型不匹配。

事实证明,错误发生在java部分,其中声明了本机方法。而不是传递Mat,我必须传递Mat.getNativeObjAddr(),这是一个很长的。

所以而不是

    private static native boolean nativeRetrieve(long thiz, Mat imageMat);
    private static native boolean nativeRead(long thiz, Mat imageMat);
    public boolean retrieve(Mat imageMat)
    {
        return nativeRetrieve(mNativeObj, imageMat);
    }    
    public boolean read(Mat imageMat)
    {
        return nativeRead(mNativeObj, imageMat);
    }    

我用过这个:

        private static native boolean nativeRetrieve(long thiz, long imageMat);
        private static native boolean nativeRead(long thiz, long imageMat);
        public boolean retrieve(Mat imageMat)
        {
            return nativeRetrieve(mNativeObj, imageMat.getNativeObjAddr());
        }    
        public boolean read(Mat imageMat)
        {
            return nativeRead(mNativeObj, imageMat.getNativeObjAddr());
        }   

注意:传递错误的类型DID不会在c ++中触发异常,即使代码是在try ... catch中。尝试捕获可能写错了,我不知道。