本文讲解windows下eclipse+adt+sdk+ndk+opencv环境搭建,并成功运行图片处理功能。
全部工具下载
http://pan.baidu.com/s/1skr6rDF
1、安装java开发环境jdk-8u65-windows-x64.exe
在系统变量中添加:
JAVA_HOME D:\A_Program Files\Java\jdk1.8.0_65
CLASSPATH .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
Path %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
2、安装eclipse
目标路径不要有空格
3、安装sdk:installer_r24.4.1-windows.exe(翻墙)
注意路径不要有空格,然后进入安装的目录打开SDK Manager.exe
勾选如下内容安装:
4、安装插件adt,需要翻墙
4、安装插件adt,需要翻墙
打开eclipse,help->install new software
输入https://dl-ssl.google.com/android/eclipse/ 全选出现的插件,下一步,安装
5、配置sdk和ndk目录
解压android-ndk64-r10b-windows-x86_64.zip,路径不要有空格,打开eclipse
Window->Preferences
6、安装bluestack模拟器
Installer_native2.0.2.5624.1451269209.exe
7、创建android工程
创建android工程,先看看能否正常生成apk在模拟器上运行
File->New->Other
finish 把废弃的ActionBarActivity改成Activity,再导入Activity包
代码如下
</pre><pre name="code" class="java">package com.example.tp002;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle action bar item clicks here. The action bar will
//automatically handle clicks on the Home/Up button, so long
//as you specify a parent activity in AndroidManifest.xml.
intid = item.getItemId();
if(id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
运行配置Run->Run configurations
Apply->close
启动bluestacks,如果分辨率太大可以在注册表中修改
修改完后
Run->build_app,
会部署到模拟器上自动运行
8、C/C++ so库的编译
右键工程名,AndroidTools-> add nativesupport
jni目录下面打开cpp文件,发现jni.h找不到
右键工程properties->c/c++General->Paths and Symbols
Add->File system添加ndk的jni头文件目录
D:\C_Code\Android\android-ndk-r10b\platforms\android-L\arch-arm64\usr\include
配置native编译
Run ->build_native
会在libs armeabi目录下生成libtp002.so
这个so什么代码都没有,下面增加个helloworld
代码如下,注意函数名字的结构是
Java_包名(.用_代替)_工程名_调用本接口的activty名_本函数名
#include<jni.h>
#ifndef _Included_jni_HelloWorld
#define _Included_jni_HelloWorld
#ifdef __cplusplus
extern "C"{
#endif
JNIEXPORT jstring JNICALL Java_com_example_tp002_MainActivity_HelloWorld
(JNIEnv * env, jobject obj)
{
return env->NewStringUTF("Hello From CPP");
}
#ifdef __cplusplus
}
#endif
#endif
再次编译so,通过。
在java中增加调用代码
首先是加载so库和声明本地函数
static{
System.loadLibrary("tp002");
}
public native StringHelloWorld();
为了观察效果,需要获取TextView控件,在原有activity_main.xml中的TextView中增加Id
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:id="@+id/tvHello"/>
然后java中增加操作TextView的代码
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView myText = (TextView)findViewById(R.id.tvHello);
myText.setText(HelloWorld());
}
运行效果:
完整代码如下
CPP
#include<jni.h>
#ifndef _Included_jni_HelloWorld
#define _Included_jni_HelloWorld
#ifdef __cplusplus
extern "C"{
#endif
JNIEXPORT jstring JNICALL Java_com_example_tp002_MainActivity_HelloWorld
(JNIEnv * env, jobject obj)
{
return env->NewStringUTF("Hello From CPP");
}
#ifdef __cplusplus
}
#endif
#endif
JAVA
package com.example.tp002;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
static{
System.loadLibrary("tp002");
}
public native String HelloWorld();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView myText = (TextView)findViewById(R.id.tvHello);
myText.setText(HelloWorld());
}
}
到这里已经打通了jni调用
下面增加opencv
9、增加opencv例程
解压OpenCV-2.4.11-android-sdk.zip,路径不要有空格
首先修改jni目录下的mk,新建一个Application.mk内容为
APP_ABI := armeabi-v7a
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_PLATFORM := android-9
Android.mk内容为
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
OPENCV_LIB_TYPE:=STATIC
include D:\C_Code\Android\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk
LOCAL_LDLIBS += -lm -llog -landroid
LOCAL_MODULE := tp002
LOCAL_SRC_FILES := tp002.cpp
include $(BUILD_SHARED_LIBRARY)
CPP例程内容:
#include <jni.h>
#include <opencv2/opencv.hpp>
using namespace cv;
extern "C" {
JNIEXPORT jintArray JNICALL Java_com_example_tp002_MainActivity_ImgFun(
JNIEnv* env, jobject obj, jintArray buf, int w, int h);
JNIEXPORT jintArray JNICALL Java_com_example_tp002_MainActivity_ImgFun(
JNIEnv* env, jobject obj, jintArray buf, int w, int h) {
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, NULL);
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 ++){
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;
}
}
到这里就已经可以编译通过了,但是eclipse还是提示opencv的头文件和类型无法解析,应该是eclipse的错误检查没办法从mk中读取opencv的头文件,那我们把头文件路径添加到eclipse的环境变量中,工程右键,properties,
同样,为了观察效果,我们在activity_main.xml中增加一个button和ImageView控件,去掉前面用到的TextView控件
<Button
android:id="@+id/btnProcessPic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/proc_pic"/>
<ImageView
android:id="@+id/ivAndroid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="@drawable/meinv"/>
增加图片资源,名字和上面控件用到的一致
在strings.xml中增加button显示的字符串
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">tp002</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="proc_pic">duang~</string>
</resources>
Java调用代码:
package com.example.tp002;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.widget.Button;
import android.widget.ImageView;
import android.view.View;
import android.view.View.OnClickListener;
public class MainActivity extends Activity {
static{
System.loadLibrary("tp002");
}
// public native String HelloWorld();
public native int[] ImgFun(int[] buf, int w, int h);
private ImageView ivAndroid;
private Button btnProcessPic;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnProcessPic = (Button) findViewById(R.id.btnProcessPic);
ivAndroid = (ImageView) findViewById(R.id.ivAndroid);
btnProcessPic.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
long current = System.currentTimeMillis();
@SuppressWarnings("deprecation")
Bitmap img1 = ((BitmapDrawable) getResources().getDrawable(R.drawable.meinv)).getBitmap();
int w = img1.getWidth(), h = img1.getHeight();
int[] pix = new int[w * h];
img1.getPixels(pix, 0, w, 0, 0, w, h);
int[] resultInt = ImgFun(pix, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
ivAndroid.setImageBitmap(resultImg);
}
});
}
}
运行效果
点击按钮
至此,全部完成!脖子好酸。