Android的毛玻璃模糊效果,我使用OpenCV来搞

4,238 阅读3分钟

开始学习OpenCV,毛玻璃模糊效果目前网上流行的有三种办法:

  1. 使用java来编写一长串的像素处理办法算法来改变bitmap(性能教差,而且一堆算法代码,难理解,不优雅)
  2. 使用C语言的方式同样使用和java一样的算法来实现(性能好,同样一堆算法代码难理解,也不优雅)
  3. 使用RenderScript这个有Api版本的限制。

现在我们可以利用OpenCV框架中滤波算法来实现图片的模糊虚化。


准备工作:

先到OpenCV官网, 下载Android平台的sdk包: www.opencv.org

解压后:
sdk目录里是openCV的一些动态库,cmake构建文件,以及java的一些api。


image.png

新建一个支持NDK的工程:


勾选c++的支持.png
选择stdC++11的标准.png

配置集成OpenCV库到工程:

我这里只编译支持了armeabi,cpu架构的平台,需要在app,module的build.gradle中做一些修改:


image.png

对了这里我使用AS自带的cmake工具来构建NDK库的链接和编译的支持,所以不需要再写Android.mk的配置文件,这里配置下CMakeLists.txt就可以,更加简单:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# 添加我们自己要编译的so库,以及源码文件
add_library(
             image_process
             SHARED
             src/main/cpp/image_process.cpp )

# 增加opencv库
add_library( opencv_java3 SHARED IMPORTED )
# 编译的平台是armeabi
if(${ANDROID_ABI} STREQUAL "armeabi")
# 设置动态库文件的路径属性
set_target_properties(
    opencv_java3
    PROPERTIES IMPORTED_LOCATION
    ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libopencv_java3.so
    )
endif(${ANDROID_ABI} STREQUAL "armeabi")
# opencv库的头文件路径设置,在此是opencv-sdk的路径,当然你也可以把include目录拷贝到工程中
include_directories(
    D:/opencv-3.2.0-android-sdk/OpenCV-android-sdk/sdk/native/jni/include
)


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 )

# 需要链接的库
target_link_libraries( # Specifies the target library.
                       image_process
                       opencv_java3
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

上面的添加依赖库,和自己要编译的so库的写法都是差不多的,就是这些套路。(自古深情留不住,总是套路得人心)同时把sdk中libopencv_java3.so文件拷贝到对应的工程目录下我这里是jniLibs为了方便不然还得配置gradle修改source目录的映射路径:


image.png
image.png

编写java层的对外开发调用api

public class ImageProcessUtils {

    /**
     * 毛玻璃一张图片
     * @param srcBitmap    原始图片
     * @return  毛玻璃后的图片
     */
    public static Bitmap blur(Bitmap srcBitmap){
        // 获取原始图片的宽高
        int width = srcBitmap.getWidth();
        int height = srcBitmap.getHeight();
        // 初始化一个用来存储图片所有像素的int数组
        int[] pixels = new int[width * height];
        // 把原始图片的所有原始存入数组中
        srcBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        // 通过jni本地方法毛玻璃化图片
        blurImage(pixels, width, height);
        // 创建一个新的图片
        Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        // 把处理后的图片像素设置给新图片
        newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
        return newBitmap;
    }

    // 毛玻璃图片
    public static native void blurImage(int[] pixels, int w, int h);

    // 加载so库
    static {
        System.loadLibrary("image_process");
        System.loadLibrary("opencv_java3");
    }
}

接下来是在NDK中使用opencv来实现图片的毛玻璃化

#include <jni.h>
#include <android/log.h>
#include <opencv2/opencv.hpp>    // 引入opencv库头文件
#include <opencv2/highgui/highgui.hpp> // 引入opencv图形界面,暂时没用到

// 定义了log日志宏函数,方便打印日志在logcat中查看调试
#define TAG "Jerry-NDK-Image-Pro"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG, __VA_ARGS__)

using namespace cv;

extern "C"
JNIEXPORT void JNICALL
Java_com_jerry_jerryopencvdemo_imageprocess_ImageProcessUtils_blurImage(
        JNIEnv *env,
        jclass jcls,
        jintArray jarr_pixels, 
        jint j_width, 
        jint j_height) {

    // 获取java中传入的像素数组值,jintArray转化成jint指针数组
    jint *c_pixels = env->GetIntArrayElements(jarr_pixels, JNI_FALSE);
    if(c_pixels == NULL){
        return;
    }

    LOGE("图片宽度:%d, 高度:%d", j_width, j_height);

    // 把c的图片数据转化成opencv的图片数据
    // 使用Mat创建图片
    Mat mat_image_src(j_height, j_width, CV_8UC4, (unsigned char*) c_pixels);
    // 选择和截取一段行范围的图片 
    Mat temp = mat_image_src.rowRange(j_height / 3, 2 * j_height / 3);
    // 方框滤波
//    boxFilter(temp, temp, -1, Size(85, 85));
    // 均值滤波
    blur(temp, temp, Size(85, 85));
    // 使用高斯模糊滤波
//    GaussianBlur(temp, temp, Size(45, 13), 0, 0);
    // 将opencv图片转化成c图片数据,RGBA转化成灰度图4通道颜色数据
    cvtColor(temp, temp, CV_RGBA2GRAY, 4);

    // 更新java图片数组和释放c++中图片数组的值
    env->ReleaseIntArrayElements(jarr_pixels, c_pixels, JNI_FALSE);
}

看看效果图对比图:


原图.png
毛玻璃后效果图.png

简单的利用了滤波算法函数处理,来达到毛玻璃的效果,当然opencv的强大远远不限于此。关于opencv进一步的学习使用还会继续记录在博客中。