前言:之前寫過關(guān)于android中通過JNI使用NDK的demo,介紹了關(guān)于so文件的生成與使用,但僅僅是demo,總覺得脫離實(shí)際應(yīng)用的話相關(guān)的東西很快就會忘掉,最近準(zhǔn)備面試才發(fā)現(xiàn)之前關(guān)于Cmake的配置等步驟確實(shí)忘的差不多了,這兩天剛?cè)肼殻诸^還不忙,于是趕緊找了下OpenCV相關(guān)的實(shí)際應(yīng)用來練練手(OpenCV,高級android開發(fā)面試必備的)
如果對Cmake涉及的結(jié)構(gòu)和配置不了解建議先花10分鐘看看:
1.Cmake方式生成so
2.Cmake方式調(diào)用so
國際慣例:開局一張圖,實(shí)現(xiàn)慢慢侃

各種濾鏡native算法處理參考:https://blog.csdn.net/yangtrees
如圖:分別展示的是 原圖、灰度處理、高斯模糊、流金歲月、凹雕刻、突浮雕
其他各種效果參考上面鏈接中的系列文章,找到對應(yīng)算法稍加修改即可,注意事項(xiàng)下文中會提到。
OpenCV之濾鏡效果實(shí)現(xiàn)步驟梳理:
1.OpenCV Android資源包下載
下載地址
2.新建android項(xiàng)目,勾選c++支持(舊項(xiàng)目添加c++支持可以手動去新建CMake等文件再修改配置,具體可以參考之前的文章:so生成篇
3.main目錄下面新建jniLibs文件夾,將需要適配的cpu類型對應(yīng)的so文件復(fù)制進(jìn)去(文件在步驟1下載的OpenCV-android-sdk\sdk\native\libs中)
4.將include文件夾復(fù)制到cpp下(里面是opencv庫的頭文件,在你自己的c++代碼文件中導(dǎo)入頭文件就可以使用opencv的函數(shù)了)

5.(重點(diǎn))配置CMakeLists,配置so路徑和頭文件路徑,使的在自己的c++文件中可以導(dǎo)入頭文件,使用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動態(tài)鏈接庫的引用
add_library(libopencv_java3 SHARED IMPORTED)
#設(shè)置opencv動態(tài)鏈接庫的引用的路徑,${ANDROID_ABI}根據(jù)設(shè)備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")去加載這個(gè)so
add_library( # Sets the name of the library.
#這個(gè)是聲明引用so庫的名稱,在項(xiàng)目中,如果需要使用這個(gè)so文件,引用的名稱就是這個(gè)。
#值得注意的是,實(shí)際上生成的so文件名稱是libso-lib。
so-lib
# 這個(gè)參數(shù)表示共享so庫文件,也就是在Run項(xiàng)目或者build項(xiàng)目時(shí)會在目錄
SHARED
#構(gòu)建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庫關(guān)聯(lián)到本地庫so-lib ljnigraphics(高斯模糊算法用到) libopencv_java3(OpenCV)
target_link_libraries( # Specifies the target library.
so-lib
${log-lib}
-ljnigraphics
libopencv_java3
)
6.(重點(diǎn)中的重點(diǎn))編寫c++文件 實(shí)現(xiàn)各種濾鏡效果算法,函數(shù)按JNI命名規(guī)則,給JAVA層調(diào)用
testCodeName文件中導(dǎo)入的頭文件
#include <math.h>
#include <stdlib.h>
#include <jni.h>
//opencv
#include <jni.h>
#include<opencv2/opencv.hpp>
#include<iostream>
這里選取灰度效果來研究
JNI方法中JNIEnv *env, jobject thiz,為固定參數(shù),實(shí)際參數(shù)為 jintArray buf, jint w, jint h
對應(yīng)JAVA中的參數(shù)就是 int[] 數(shù)組,int 寬 int 高,
實(shí)際在調(diào)用的時(shí)候是傳入bitmap的像素?cái)?shù)組,bitmap寬度,bitmap高,
看看JAVA中的申明:
//native方法聲明
public native int[] gray(int[] buf, int w, int h);
再看看Activity中的調(diào)用(這里直接在Activity中l(wèi)oadLib了,并且Native函數(shù)聲明gary也在activity中)

核心代碼
//獲取bitmap寬高,新建一個(gè)像素?cái)?shù)組(此時(shí)還沒寫入像素信息)
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傳進(jìn)去處理像素pix,處理好后返回
int[] resultPixes = gray(pix, w, h);
//根據(jù)經(jīng)過灰度處理后的resultPixes像素去創(chuàng)建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方法,下面重點(diǎn)分析
JNI中函數(shù):
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三通道
這部分代碼的作用就是根據(jù)傳入的Bitmap的像素,寬,高去建立一個(gè) Mat對象
Mat是opencv中的圖像對象, Mat對象封裝了圖像在內(nèi)存中的信息,用于表示一副加載到內(nèi)存中的圖像,
實(shí)際的濾鏡效果就是通過opencv提供的函數(shù)去處理Mat對象得到的,所以得到如下結(jié)論:
1.Mat是opencv處理圖片的一個(gè)很重要的對象
2.JAVA->JNI->OpenCV處理的轉(zhuǎn)化過程涉及到Bitmap->jintArray->Mat的轉(zhuǎn)化
3.Android的Bitmap是ARGB四通道,而不是RGB三通道,所以這里生成Mat用的是CV_8UC4,網(wǎng)上找的濾
鏡效果算法可能用的是CV_8UC3,要注意改過來,對應(yīng)的算法中如果遇到類似:float R = P0[3* x + 2];的結(jié)構(gòu)注意
改成float R = P0[4 * x + 2];
2.
cvtColor(imgData, imgData, CV_BGRA2GRAY);
cvtColor(imgData, imgData, CV_GRAY2BGRA);
//這兩行就是調(diào)用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,去生成像素?cái)?shù)組,返回給JAVA層
//在這個(gè)demo中,除了高斯模糊外,幾乎所有的濾鏡效果的實(shí)現(xiàn)1和3不變,就是改變2的處理。
項(xiàng)目demo地址:https://gitee.com/lunguoguo/OpencvProject
這種導(dǎo)入so的方式會造成APK體積巨大,下篇嘗試通過其他方式僅僅導(dǎo)入使用到的資源去實(shí)現(xiàn)!