Android OpenCV VideoCapture::retrieve(&mat) 导致致命信号 11
Posted
技术标签:
【中文标题】Android OpenCV VideoCapture::retrieve(&mat) 导致致命信号 11【英文标题】:Android OpenCV VideoCapture::retrieve(&mat) causes fatal signal 11 【发布时间】:2014-09-13 14:50:30 【问题描述】:所以我正在尝试制作一个 android 应用程序,该应用程序将使用 OpenCV 通过 JNI 和 NDK 逐帧打开视频文件,检测每一帧中的人脸并显示它们(最终目标是加速算法,以更好地适合蹩脚的移动处理)。
我尝试的第一个方法是 MediaMetadataRetriever 没有结果,它只是返回 null。
第二种方法是FFMpegMediaMetadataRetriever,它确实按预期工作,但非常慢(最高 2fps;太慢了),部分原因是您必须从 FFMpegMMR 的位图转换为 Mat,然后检测并绘制,然后再转换回位图,我什至想尝试一下。
我目前正在研究的第三种方法是 VideoCapture grab() 和 retrieve()。我为本机代码制作了一个包装器,主要是通过从 OpenCV 示例中复制 facetector。我也尝试过 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 是 native 的 java 包装器,当调用 retrieve() 或 read() 时会发送信号,请注意构造函数 open() 和 grab() 都可以工作(或者至少不工作使应用程序崩溃)。
这是包装器(我已经正确传递了 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() 不起作用,我发现并解决了一个错误,我忘记将 thiz 传递给方法。 retrieve() 的问题是我一辈子都找不到它的来源。 highgui.hpp 中有部分定义,但没有实现,我可以在其中放置日志消息以帮助我调试。
可能的解决方案是:
retrieve() 支持的不同文件格式,除了我不知道可能是哪种文件格式 使用非绝对文件路径?虽然 open() 和 grab() 都没有造成致命信号 放弃整个事情并使用 PNG(不过我真的很想弄清楚这一点)如果有人能告诉我在哪里可以找到retrieve() 和read() 的实现,我将不胜感激。或者如果这是我错过的明显和愚蠢的事情(我希望)。
同时,我会尝试找到一种在 Eclipse 中调试 c++ 的方法,也许尝试另一种格式?
【问题讨论】:
好的,我之前确实使用过 Python 包装器并且遇到了同样的事情。基本上,您需要查看 C++ 文档并尝试了解 C++ 方面的问题所在。您能否将代码缩短为仅与错误相关的内容,即引发错误的方法、错误的设置以及失败方法调用的方法? 在 C++ 方面,我将其范围缩小到result = ((VideoCapture*)thiz)->retrieve((*((Mat*)imageMat)));
,因为我没有找到 highgui 的来源。
好吧,抱歉,我不擅长 JNI,所以这可能是一个愚蠢的问题,但是为什么当你调用 ->retrieve()
时你会像这样:(VideoCapture*)thiz
投 thiz
?此外,您是否 100% 确定:(*((Mat*)imageMat))
具有它必须具有的类型,即根据 C++ 文档,Mat& image, int channel=0
? docs.opencv.org/modules/highgui/doc/… 我在使用 Python 绑定时遇到的一个问题是,有时您在 Python (Java) 端引用了一些 C++ 对象,而该对象没有 C++ 期望的类型,这会引发错误。
thiz 是我正在使用的 VideoCapture 对象。由于这是 JNI,因此您在 .cpp 中并没有真正的类,并且在构建类之后,当您在 .cpp 中有引用时,您必须将该类的引用从 Java 端传递给本机 cpp 方法。 cpp,您还必须将其转换为所需的类才能使用它的方法。我复制了大部分有效的代码示例 DetectionBasedTracker。您基本上是以长格式传递引用,然后取消引用并转换它。你确实提出了很好的观点,我会调查一下,看看我是否以某种方式弄乱了文件类型。
就是这样!我传递的是 Mat 而不是 long(对 Mat 的引用)。非常感谢您的建议,您可能挽救了我的理智。
【参考方案1】:
终于找到了,按照 Aleksander 的建议,我设法找到了从 Java 传递到 JNI 的对象的类型不匹配。
原来,错误出在 java 部分,在该部分中声明了本机方法。我不得不通过 Mat.getNativeObjAddr() 而不是通过 Mat,这是一个很长的。
所以不是
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());
注意:传递错误的类型不会触发 c++ 中的异常,即使代码在 try...catch 中。 try...catch 可能写错了,我不知道。
【讨论】:
我需要帮助,可以吗?以上是关于Android OpenCV VideoCapture::retrieve(&mat) 导致致命信号 11的主要内容,如果未能解决你的问题,请参考以下文章
opencv for android(十六):opencv在android上的录制avi格式的视频
android +opencv 配置,Android+OpenCV4.2.0环境配置详解(Android studio)