一、創(chuàng)建支持C++的android基礎(chǔ)模版項(xiàng)目
新建項(xiàng)目佑女,勾選【Include C++ support】烟馅,后續(xù)一直按【next】按鈕取默認(rèn)設(shè)置即可。
最終生成的目錄結(jié)構(gòu)如下圖:
此時(shí)直接build并運(yùn)行能正確看到效果玖像。
打開MainActivity,可以看到生成的示例代碼:
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
再打開 native-lib.cpp 文件齐饮,也可以看到自動生成的JNI風(fēng)格代碼:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring
JNICALL
Java_com_woodstream_opencvdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
其中
Java_com_woodstream_opencvdemo_MainActivity_stringFromJNI
就包含了java對應(yīng)方法的不少信息捐寥。
為了看看IDE是怎么操作的笤昨,回到MainActivity,寫入一個新的方法:
public native int[] gray(int[] buf, int w, int h);
此時(shí)方法名會標(biāo)紅握恳,在IDE的智能提示下選擇Create function xxx瞒窒,會自動跳轉(zhuǎn)到native-lib.cpp文件,并且自動添加了代碼:
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_woodstream_opencvdemo_MainActivity_gray(JNIEnv *env, jobject instance, jintArray buf_,
jint w, jint h) {
jint *buf = env->GetIntArrayElements(buf_, NULL);
// TODO
env->ReleaseIntArrayElements(buf_, buf, 0);
}
就這么簡單的步驟就可以進(jìn)行JNI的開發(fā)乡洼,可見Android Studio(下稱AS)大為減少我們編寫JNI的工作量崇裁,而它又是怎么工作的?常規(guī)的makefile什么的不用了束昵?
二拔稳、默認(rèn)CMake的構(gòu)建方式
- 可以看到目錄有一個CMakeLists.txt文件。
-
打開build.gradle锹雏,看到有外部native編譯的配置:
image.png -
打開app/build/intermediates/cmake目錄壳炎,看到其子目錄下生成了so文件:
image.png
從這幾個信息可以知道AS默認(rèn)是通過CMake來完成上述操作的,當(dāng)然也支持android.mk的方式逼侦,但比后者要方便不少。
三腰耙、用CMake的方式集成OpenCV
首先榛丢,可以先看下此文:Android 接入 OpenCV庫的三種方式
除非你是用java寫圖像處理算法,不然不建議用第一種方式挺庞,因?yàn)閳D像識別實(shí)現(xiàn)考慮由C++來完成晰赞,所以選用第二種方式,此時(shí)如下步驟搭建OpenCV的環(huán)境:
- https://opencv.org/releases.html下載源碼包并解壓选侨。
-
新建jniLibs文件夾掖鱼,把OpenCV中sdk/native/libs內(nèi)容復(fù)制到其下:
image.png - 修改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.
set(pathToOpenCv /Users/woodstream/Documents/dev/projects/android/OpenCV-android-sdk)#設(shè)置OpenCv的路徑變量
cmake_minimum_required(VERSION 3.4.1)
#支持-std=gnu++11
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
#配置加載native依賴
include_directories(${pathToOpenCv}/sdk/native/jni/include)
#動態(tài)方式加載
add_library(lib_opencv STATIC IMPORTED ) #表示創(chuàng)建一個導(dǎo)入庫,靜態(tài)方式
#引入libopencv_java3.so文件
set_target_properties(lib_opencv
PROPERTIES
IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${}/libopencv_java3.so
)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#自己的源文件
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
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 )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} android -ljnigraphics lib_opencv)
至此環(huán)境已經(jīng)搭好了(主要修改pathToOpenCv援制、set_target_properties的IMPORTED_LOCATION戏挡、add_library的source file)。
四晨仑、編寫代碼測試效果
完善C++文件native-lib.app先前生成的gray方法:
#include <jni.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_eastcom_cpptest_MainActivity_gray(JNIEnv *env, jobject instance, jintArray buf, jint w,
jint h) {
jint *cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
if (cbuf == NULL) {
return 0;
}
Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);
uchar* ptr = imgData.ptr(0);
for(int i = 0; i < w*h; i ++){
//計(jì)算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
//對于一個int四字節(jié)褐墅,其彩色值存儲方式為:BGRA
int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
ptr[4*i+1] = grayScale;
ptr[4*i+2] = grayScale;
ptr[4*i+0] = grayScale;
}
int size = w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
MainActivity改成:
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
R.mipmap.pic_test)).getBitmap();
int w = bitmap.getWidth(), h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
// 調(diào)用JNI實(shí)現(xiàn)的gray方法
int [] resultPixes = gray(pix,w,h);
Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565);
result.setPixels(resultPixes, 0, w, 0, 0,w, h);
ImageView img = (ImageView)findViewById(R.id.img2);
img.setImageBitmap(result);
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native int[] gray(int[] buf, int w, int h);
public native String stringFromJNI();
}
activity_main.xml布局文件改成:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:orientation="vertical"
tools:context="com.eastcom.cpptest.MainActivity">
<TextView
android:id="@+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="原圖:" />
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/pic_test"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="處理后的圖:" />
<ImageView
android:id="@+id/img2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
此時(shí)編譯運(yùn)行可以看到效果如圖: