OpenCV实战之(1)图片滤镜

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

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

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


OpenCV实战之(1)图片滤镜_第1张图片
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的函数了)

OpenCV实战之(1)图片滤镜_第2张图片
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 
#include 
#include 
//opencv
#include 
#include
#include

这里选取灰度效果来研究
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中)


OpenCV实战之(1)图片滤镜_第3张图片
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)图片滤镜)