美文网首页
OpenCV实战之(1)图片滤镜

OpenCV实战之(1)图片滤镜

作者: 伦子汪不造轮子 | 来源:发表于2018-09-30 11:59 被阅读0次

    前言:之前写过关于android中通过JNI使用NDK的demo,介绍了关于so文件的生成与使用,但仅仅是demo,总觉得脱离实际应用的话相关的东西很快就会忘掉,最近准备面试才发现之前关于Cmake的配置等步骤确实忘的差不多了,这两天刚入职,手头还不忙,于是赶紧找了下OpenCV相关的实际应用来练练手(OpenCV,高级android开发面试必备的)

    如果对Cmake涉及的结构和配置不了解建议先花10分钟看看:
    1.Cmake方式生成so
    2.Cmake方式调用so

    国际惯例:开局一张图,实现慢慢侃


    image.png

    各种滤镜native算法处理参考:https://blog.csdn.net/yangtrees
    如图:分别展示的是 原图、灰度处理、高斯模糊、流金岁月、凹雕刻、突浮雕
    其他各种效果参考上面链接中的系列文章,找到对应算法稍加修改即可,注意事项下文中会提到。

    OpenCV之滤镜效果实现步骤梳理:

    1.OpenCV Android资源包下载
    下载地址
    2.新建android项目,勾选c++支持(旧项目添加c++支持可以手动去新建CMake等文件再修改配置,具体可以参考之前的文章:so生成篇
    3.main目录下面新建jniLibs文件夹,将需要适配的cpu类型对应的so文件复制进去(文件在步骤1下载的OpenCV-android-sdk\sdk\native\libs中)
    4.将include文件夹复制到cpp下(里面是opencv库的头文件,在你自己的c++代码文件中导入头文件就可以使用opencv的函数了)

    image.png
    5.(重点)配置CMakeLists,配置so路径和头文件路径,使的在自己的c++文件中可以导入头文件,使用opencv,具体配置如下:
    #CMake最低版本3.4.1
    cmake_minimum_required(VERSION 3.4.1)
    #作用不太清楚
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
    
    #配置头文件路径,CMAKE_SOURCE_DIR为 CMakeList同级目录,即app下,通过${CMAKE_SOURCE_DIR}再定位到include
    include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
    #添加opencv动态链接库的引用
    add_library(libopencv_java3 SHARED IMPORTED)
    #设置opencv动态链接库的引用的路径,${ANDROID_ABI}根据设备cpu型号选文件夹
    set_target_properties(
            libopencv_java3
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)
    #配置通过源码文件testCodeName生成libso-lib.文件  JAVA中  System.loadLibrary("so-lib")去加载这个so
    add_library( # Sets the name of the library.
            #这个是声明引用so库的名称,在项目中,如果需要使用这个so文件,引用的名称就是这个。
            #值得注意的是,实际上生成的so文件名称是libso-lib。
            so-lib
            # 这个参数表示共享so库文件,也就是在Run项目或者build项目时会在目录
            SHARED
            #构建so库的源文件
            src/main/cpp/testCodeName.cpp)
    
    
    #添加log库配置
    find_library( # Sets the name of the path variable.
            log-lib
            # Specifies the name of the NDK library that
            # you want CMake to locate.
            log)
    
    
    #将NDK库关联到本地库so-lib   ljnigraphics(高斯模糊算法用到) libopencv_java3(OpenCV)
    target_link_libraries( # Specifies the target library.
            so-lib
            ${log-lib}
            -ljnigraphics
            libopencv_java3
            )
    
    

    6.(重点中的重点)编写c++文件 实现各种滤镜效果算法,函数按JNI命名规则,给JAVA层调用

    testCodeName文件中导入的头文件

    #include <math.h>
    #include <stdlib.h>
    #include <jni.h>
    //opencv
    #include <jni.h>
    #include<opencv2/opencv.hpp>
    #include<iostream>
    

    这里选取灰度效果来研究
    JNI方法中JNIEnv *env, jobject thiz,为固定参数,实际参数为 jintArray buf, jint w, jint h
    对应JAVA中的参数就是 int[] 数组,int 宽 int 高,
    实际在调用的时候是传入bitmap的像素数组,bitmap宽度,bitmap高,
    看看JAVA中的申明:

       //native方法声明
        public native int[] gray(int[] buf, int w, int h);                                         
    

    再看看Activity中的调用(这里直接在Activity中loadLib了,并且Native函数声明gary也在activity中)


    image.png

    核心代码

           //获取bitmap宽高,新建一个像素数组(此时还没写入像素信息)
           int w = bitmap.getWidth();
            h = bitmap.getHeight();
            int[] pix = new int[w * h];
          
    
           //灰度处理
    
            //往pix中写入像素信息
            bitmap.getPixels(pix, 0, w, 0, 0, w, h);  
    
            //将pix信息和bitmap的宽高  通过Native方法 gray传进去处理像素pix,处理好后返回
            int[] resultPixes = gray(pix, w, h);
    
            //根据经过灰度处理后的resultPixes像素去创建Bitmap,给Imageview显示
            Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
            result.setPixels(resultPixes, 0, w, 0, 0, w, h);
            iv1.setImageBitmap(result);
       
    

    毫无疑问,这里的核心是JNI层中的gray方法,下面重点分析

    JNI中函数:

    1.

    extern "C" JNIEXPORT jintArray JNICALL
    
    Java_com_example_lunwang_ndktest_MainActivity_gray(JNIEnv *env, jobject thiz, jintArray buf, jint w,
                                                       jint h) {
    
        jint *cbuf;
        jboolean ptfalse = false;
        cbuf = env->GetIntArrayElements(buf, &ptfalse);
        if (cbuf == NULL) {
            return 0;
        }
    
        Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);    // 注意,Android的Bitmap是ARGB四通道,而不是RGB三通道
    

    这部分代码的作用就是根据传入的Bitmap的像素,宽,高去建立一个 Mat对象
    Mat是opencv中的图像对象, Mat对象封装了图像在内存中的信息,用于表示一副加载到内存中的图像,
    实际的滤镜效果就是通过opencv提供的函数去处理Mat对象得到的,所以得到如下结论:

    1.Mat是opencv处理图片的一个很重要的对象
    2.JAVA->JNI->OpenCV处理的转化过程涉及到Bitmap->jintArray->Mat的转化
    3.Android的Bitmap是ARGB四通道,而不是RGB三通道,所以这里生成Mat用的是CV_8UC4,网上找的滤
    镜效果算法可能用的是CV_8UC3,要注意改过来,对应的算法中如果遇到类似:float R = P0[3* x + 2];的结构注意
    改成float R = P0[4 * x + 2];

    2.

       cvtColor(imgData, imgData, CV_BGRA2GRAY);
       cvtColor(imgData, imgData, CV_GRAY2BGRA);
    

    //这两行就是调用opencv的方法去处理Mat,其他效果这里的处理会不同

    3.

        int size = w * h;
        jintArray result = env->NewIntArray(size);
        env->SetIntArrayRegion(result, 0, size, (jint *) imgData.data);
        env->ReleaseIntArrayElements(buf, cbuf, 0);
        return result;
    }
    

    //这几行就是拿到处理后的Mat,去生成像素数组,返回给JAVA层
    //在这个demo中,除了高斯模糊外,几乎所有的滤镜效果的实现1和3不变,就是改变2的处理。

    项目demo地址:https://gitee.com/lunguoguo/OpencvProject

    这种导入so的方式会造成APK体积巨大,下篇尝试通过其他方式仅仅导入使用到的资源去实现!

    相关文章

      网友评论

          本文标题:OpenCV实战之(1)图片滤镜

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