错误描述
基于c++使用ncnn封装了深度学习模型,并将c++项目打包成为了一个so库给Android调用,在打包成为so库之前,在Windows系统以及Ubuntu系统利用电脑的camera测试过都能正常运行没有任何异常,打包成so库之后给Android的开发人员调用时,直接报了一个null pointer dereference错误,并且APP直接奔溃
错误定位
对于打包的so库不好定位错误的位置,能够提供的信息都是内存地址相关信息,所以我们需要先通过这些错误信息提供的内存地址来定位so库报错的位置。Android的NDK提供了一个addr2line工具,我们可以通过这个工具结合内存地址查看c++项目中报错文件的行数信息
- linux系统
cd android-ndk-r21/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin
#后面的地址信息,是调用so库时报错所指向的地址,从后面(so库最后一个地址)往前
#最前的地址指向的信息可能是第三方库或者系统文件的位置,我们需要确定的是我们代码的错误位置
./arm-linux-androideabi-addr2line -e *.so 0006b8f7 00022683 0006f159 0008845f
- Windows系统
#默认目录
cd C:\Users\用户\AppData\Local\Android\Sdk\ndk\21.0.6113669\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin
arm-linux-androideabi-addr2line.exe *.so 0006b8f7 00022683 0006f159 0008845f
通过上面的信息可以定位到你封装so库项目的报错位置,最终我定位到
ex.extract
就是在使用ncnn加载模型之后,通过Extractor来获取网络提取的节点信息时报错了
错误原因分析
在定位到错误之后,我们就可以来分析一下这个错误原因,ncnn的extract函数提供了一个返回值信息,可以打印一下看看,如果是0就说明成功了,返回值如果是-100就说明提取失败。我这里的返回值是-100,然后我还测试了一下ex.input("data", in)函数的返回值,发现是0,说明这个是成功了。
这也就说明了代码本身没有问题,那么问题在哪呢?肯定是输入的图片数据出问题了
解决办法
既然是数据出问题了,我们就检查一下图片的通道和尺寸
- 检查输入图片是否为空
- 查看模型图片的输入通道
首先查看模型输入是3通道、4通道还是单通道,然后再确定输入图片的通道数是否和模型输入一致。还需要确定模型输入的通道顺序和输入图片的通道顺序是否一致,如果不一致会导致模型预测效果变差。
- 查看输入图片的尺寸
输入图片的尺寸是否符合模型输入的尺寸。除此之外,还有一点需要特别注意,有时候你会发现图片的输入尺寸和模型输入尺寸一致时,extract函数在提取节点信息的时候还是返回-100,这时候需要确定数据的编码和解码是否存在问题。
我遇到的问题时,在使用手机前置摄像头拍照的时候,Android开发人员传给我的照片的宽和高反过来了,主要是因为他在使用android camera2获取摄像头图片的时候没有进行旋转,后面旋转过来之后图片的尺寸正常,但是还是报异常。后面我发现他传过来的图片大小有十几M,而我用前置摄像头正常拍照只有几M,最终改了他的解码方式,完美解决了这个问题。