Android JNI 开发 Demo by NDK (native develop kit)

最近终于有了一些空闲时间,所以想着把之前的Android相关的项目整理一下,前几天整理了Android OCR 的项目, 今天就干脆再整理一下 NDK JNI调用。


关于Android 中通过JNI进行本地调用的配置与开发,已经在链接 http://blog.csdn.net/watkinsong/article/details/9849973 中给出了非常详细的说明。 请仔细阅读。


同时,关于Android 作OCR的说明,也已经 在 http://blog.csdn.net/watkinsong/article/details/45541215 给出了详细的解释。 所以,本文章不再赘述如何配置开发环境,如何创建项目等,而是直接给出一个项目,列出需要注意的几点要点。


1. 首先,通过https://github.com/weixsong/libra 链接下载该项目,找到NDKDemo项目。

2. 通过Eclipse导入项目。

3. 在调用JNI之前,我们需要定义一个接口,实现和JNI通讯,本例的接口定义:

package com.example.ndk;

public class CannyDetect {
	static {
		System.loadLibrary("native");
	}

	/**
	 * @param width
	 *            the current view width
	 * @param height
	 *            the current view height
	 */
	public static native int[] cannyDetect(int[] buf, int w, int h);
}

接口中,要通过静态初始化代码块加载.so文件,不需要添加后缀名。 然后定义static native 的接口,这个接口需要符合JNI的规范。


4. 既然有了JNI调用接口,就要看看如何编写JNI程序:

在native.cpp 中:

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>

using namespace cv;
IplImage * change4channelTo3InIplImage(IplImage * src);

extern "C" {
JNIEXPORT jintArray JNICALL Java_com_example_ndk_CannyDetect_cannyDetect(
		JNIEnv* env, jobject obj, jintArray buf, int w, int h);
JNIEXPORT jintArray JNICALL Java_com_example_ndk_CannyDetect_cannyDetect(
		JNIEnv* env, jobject obj, jintArray buf, int w, int h) {

	jint *cbuf;
	cbuf = env->GetIntArrayElements(buf, 0);
	if (cbuf == NULL) {
		return 0;
	}

	Mat myimg(h, w, CV_8UC4, (unsigned char*) cbuf);
	IplImage image = IplImage(myimg);
	IplImage* image3channel = change4channelTo3InIplImage(&image);

	IplImage* pCannyImage = cvCreateImage(cvGetSize(image3channel),
			IPL_DEPTH_8U, 1);

	cvCanny(image3channel, pCannyImage, 50, 150, 3);

	int* outImage = new int[w * h];
	for (int i = 0; i < w * h; i++) {
		outImage[i] = (int) pCannyImage->imageData[i];
	}

	int size = w * h;
	jintArray result = env->NewIntArray(size);
	env->SetIntArrayRegion(result, 0, size, outImage);
	env->ReleaseIntArrayElements(buf, cbuf, 0);
	return result;
}
}

IplImage * change4channelTo3InIplImage(IplImage * src) {
	if (src->nChannels != 4) {
		return NULL;
	}

	IplImage * destImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
	for (int row = 0; row < src->height; row++) {
		for (int col = 0; col < src->width; col++) {
			CvScalar s = cvGet2D(src, row, col);
			cvSet2D(destImg, row, col, s);
		}
	}

	return destImg;
}

之前的博客评论中,总是有些小伙伴说哎呀,JNI找不到函数阿神码神码的,麻烦您先彻底看清楚博客怎么写的。。。

这里再强调一下:

JNIEXPORT jintArray JNICALL Java_com_example_ndk_CannyDetect_cannyDetect (***)

这个函数的声明,一定要遵循JNI的标准规范,首先是 

JNIEXPORT jintArray JNICALL

然后, Java_ + 包名称,其中,包中的点用_代替,然后,步骤3中定义的接口的类名,然后是方法名。


5. 注意一下Android.mk文件:

LOCAL_PATH := $(call my-dir) 
include $(CLEAR_VARS)
OPENCV_LIB_TYPE:=STATIC
OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
#try to load OpenCV.mk from default install location
include /home/wesong/software/OpenCV-2.4.10-android-sdk/sdk/native/jni/OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif
LOCAL_MODULE    := native
LOCAL_SRC_FILES := native.cpp

LOCAL_LDLIBS +=  -llog -ldl
include $(BUILD_SHARED_LIBRARY)

修改里面的OpenCV.mk的路径。


6. 看一眼 Application.mk

APP_STL:=gnustl_static  
APP_CPPFLAGS:=-frtti -fexceptions  
APP_ABI:=armeabi armeabi-v7a
APP_PLATFORM := android-19


7. 主程序:

package com.example.ndk;

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.view.View;
import android.widget.ImageView;

public class MainActivity extends Activity {

	ImageView imgView;
	Button btnNDK, btnRestore;
	
	private String title = "Canny detect by NDK";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		this.setTitle(title);
		btnRestore = (Button) this.findViewById(R.id.btnRestore);
		btnRestore.setOnClickListener(new ClickEvent());
		btnNDK = (Button) this.findViewById(R.id.btnNDK);
		btnNDK.setOnClickListener(new ClickEvent());
		imgView = (ImageView) this.findViewById(R.id.ImageView01);
		Bitmap img = ((BitmapDrawable) getResources().getDrawable(
				R.drawable.lena)).getBitmap();
		imgView.setImageBitmap(img);
	}

	class ClickEvent implements View.OnClickListener {
		public void onClick(View v) {
			if (v == btnNDK) {
				long current = System.currentTimeMillis();
				Bitmap img1 = ((BitmapDrawable) getResources().getDrawable(
						R.drawable.lena)).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 = CannyDetect.cannyDetect(pix, w, h);
				Bitmap resultImg = Bitmap.createBitmap(w, h, Config.RGB_565);
				resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
				long performance = System.currentTimeMillis() - current;
				imgView.setImageBitmap(resultImg);
				MainActivity.this.setTitle("NDK consumed: "
						+ String.valueOf(performance) + " ms");
			} else if (v == btnRestore) {
				Bitmap img2 = ((BitmapDrawable) getResources().getDrawable(
						R.drawable.lena)).getBitmap();
				imgView.setImageBitmap(img2);
				MainActivity.this.setTitle("使用OpenCV进行图像处理");
			}
		}
	}
}


截图:

技术分享


技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。