美文网首页
Android opencv笔记

Android opencv笔记

作者: hjm1fb | 来源:发表于2017-09-16 16:49 被阅读56次

    Mac环境下opencv for android笔记

    想不到时隔一年,又要接触NDK了。。。
    首先按照在Android Studio中安装OpenCV mac环境/Linux环境小试了一把。

    JNI Tip

    jni的文件夹名必须是作者截图中的jniLibs(因为这个是Gradle默认的JNI文件夹),不然System.loadLibrary方法会报错。也可以用另一个属性 jniLibs.srcDirs = ['libs']设置,这样的话就把JNI文件放到与src同级的libs文件夹。
    另外,只需要复制要支持的cpu架构的文件夹。如果只需要调用opencv中封装好的JNI接口,文件夹中只保留opencv_java.so这个文件。

    opencv 初始化

    调用OpenCVLoader.initAsync()的话会检测 OpenCVManager 这个程序有没有安装,没有就会引导用户安装。OpenCVManager里包含的是你要调用的各种so文件,一应俱全。
    但这样显然会影响用户体验,所以推荐另一个初始化方法OpenCVLoader.initDebug()
    这个方法基本等同于System.loadLibrary("opencv_java")
    opencv_java是我们上一步放到jniLibs下的libopencv_java.so文件, 包含了所有opencv封装的JNI接口。
    如果你还需要使用其他so文件,可以使用 System.loadLibrary继续加载。这样,初始化的逻辑就搞清楚了。初始化可以在onResume(),或者static 代码块里执行。

    基本数据结构和概念解释
    Size

    opencV的Imgproc有很多模糊函数, 它们都需要传入Size参数。参数名为ksize, 是kernel size的缩写,即滤波器模板(核)的尺寸。构造函数 Size(w,h) w 为像素宽度, h为像素高度。Size(3,3)就是33的核。像Size和blockSize这种,边长还是设置成2,5,7等奇数比较好理解。虽然有时候22也可以设置,但不知道和3*3有啥区别。

    • Scalar
         public Scalar</font>(double v0) {
            val = new double[] { v0, 0, 0, 0 };
             }
    
          以上面单个参数的构造方法为例,可以看出是一个size为4的一维数组。应用举例:
    
          //与一维数组相乘,所以结果是第一个通道(ARGB的话就是alpha通道)的值被放大一百倍,其他通道的值变为0
         Core.multiply(mat1, new Scalar(100), mat1);
    
    • Sobel
         //这个构造函数的dx指的是x方向求导的阶数,dy指的是y方向求导的阶数。ddepth指的是输出图像的深度。
         public static void Sobel(Mat src, Mat dst, 
         int ddepth, int dx, int dy, int ksize,
          double scale, double delta);
    
    • Mat
      Mat即矩阵(Matrix)的缩写, 是保存图像像素信息的矩阵。它主要包含两部分:矩阵头和一个指向像素数据的矩阵指针。代码示例:
    //构造一个3*3卷积核,8位无符号整型单通道。
    Mat kernel= new Mat( 3, 3, CvType.CV_8UC1);
    //前两个参数表示操作起始坐标,为(0,0),之后的参数为填充数据[0,-1, 0,-1, 5,-1, 0,-1, 0] 
    //因为是单通道,所以9个数刚好能填满。如果是4通道,就需要9*4才能填满。
    kernel. put( 0, 0, 0,-1, 0,-1, 5,-1, 0,-1, 0); 
    

    得到的卷积核如下

    0 -1 0
    -1 5 -1
    0 -1 0

    霍夫变换
    参考文章霍夫变换 确定图像上直线位置
    以检测直线为例,
    通过定义理解:
    笛卡尔坐标系的点(X, Y)对应着经过它的无数条直线,这无数条直线在p-θ平面上(p轴代表直线截距,θ代表直线夹角)上可以用一条直线表示。把笛卡尔坐标系的大量的点都映射到p-θ平面上,就有了大量直线。如果p-θ平面上存在大量直线在某个点相交,就说明笛卡尔坐标系包含一条直线,直线的斜率和截距对应着此点的p和θ。

    通过公式理解:
    其实,笛卡尔坐标系的直线公式转化一下,也能得出结论,就是个相对的思维。
    用y = kx+b表示笛卡尔坐标系的任意一条直线,这样x, y, k, b 都是未知数了。
    而b 和k 通过三角函数可以转化成p和θ,
    暂且用b = f(p,θ)和 k = g(p,θ)
    这样,直线y = kx+b上的点,虽然每一个点都能在p-θ平面上映射无数条直线,但必定每个点映射的直线必定有一条是
    f(p,θ) = y- g(p,θ)x
    笛卡尔坐标系里,确定y = kx+b的参数值,只需要两个在这条直线上的不同的点的坐标(x0,y0), (x1,y1)
    把同样的(x0,y0), (x1,y1)带入到方程f(p,θ) = y- g(p,θ)x,就可以求出p, θ的值了。所以,笛卡尔坐标系的直线就对应着p-θ平面上的一个点。

    在检测圆的过程中,发现Imgproc.HoughCircles方法居然会改变输入的Mat, 也就是第一个参数。而且如果采用new Mat()的方法生成Mat, 并且不是第一个Mat, 就可能会影响之前的Mat。而调用Mat.zeros方法就不会影响。暂时当作opencv4Android的一个bug吧,C++版本应该没这么明显的bug。

    在JNI中调用openCV

    在JNI中使用openCV时,如果报错imread imwrite等undefined reference, 可能是因为编译时STL配置的问题,需要使用gnustl_shared: Recently, NDK switched to libc++ as default STL, but OpenCV is built with gnustl

    YUV21转RGB

    YUV21转RGB的方法有很多种, 效率对比如下:
    【视频处理】YUV与RGB格式转换
    Android libyuv应用系列(二)libyuv在Android中的使用
    最后觉得OpenCV的方式既不会失真, 速度也快, 接入也方便:
    android + java opencv + Mat与byte[]互换

    JNI打印Mat信息:

    void printMAtMessage(Mat &mat) {
        LOGD("printMAtMessage","***************************Mat信息开始************************");
        LOGD("printMAtMessage","mat.rows %d",mat.rows);
        LOGD("printMAtMessage","mat.cols %d",mat.cols);
        LOGD("printMAtMessage","mat.total %d",mat.total());
        LOGD("printMAtMessage","mat.channels %d",mat.channels());
        LOGD("printMAtMessage","mat.depth %d",mat.depth());
        LOGD("printMAtMessage","mat.type %d",mat.type());
        LOGD("printMAtMessage","mat.flags %d",mat.flags);
        LOGD("printMAtMessage","mat.elemSize %d",mat.elemSize());
        LOGD("printMAtMessage","mat.elemSize1 %d",mat.elemSize1());
        LOGD("printMAtMessage","mat.data[0] %d",mat.data[0]);
        LOGD("printMAtMessage","mat.data[1] %d",mat.data[1]);
        LOGD("printMAtMessage","mat.data[mat.total()*mat.elemSize()-1]) %d",mat.data[mat.total()*mat.elemSize()-1]);
        LOGD("printMAtMessage","mat.data[mat.cols*mat.elemSize()-1] %d",mat.data[mat.cols*mat.elemSize()-1]);
        LOGD("printMAtMessage","mat.data[mat.total()*mat.elemSize()-mat.cols*mat.elemSize()] %d",mat.data[mat.total()*mat.elemSize()-mat.cols*mat.elemSize()]);
        LOGD("printMAtMessage","***************************Mat信息结束************************");
    }
    

    相关文章

      网友评论

          本文标题:Android opencv笔记

          本文链接:https://www.haomeiwen.com/subject/vxqusxtx.html