本文是参考博客名为“千里之外”的朋友的文章,感谢!原链接为: 但是没有完全一样,做了一些修改,已实践可用。
之前做的opencv图像处理,总是会依赖Opencv Manager,所以需要再安装一个Opencv Manager的apk,这样就比较不方便,很多人也不愿意。 所以今天用到的是纯C++,抛弃了Opencv Manager。
首先上资源代码(注意:因为OpenCVSDK占内存超出我上传文件的限制额,所以我把它去除了,用的时候还需要下载opencv的sdk,放入jni目录中。怎么命名下面对jni目录介绍时会提到这点)
下面是项目的步骤:
1、首先在项目中创建一个jni目录。目录的文件列表如下图:
下面将对这些文件一一介绍。
1)ImageProc.cpp文件。C++文件,里面有图片处理的方法。
#include <ImageProc.h>
#include <opencv2/core/core.hpp>
#include <string>
#include <vector>
using namespace cv;
using namespace std;
void proc(Mat &src);
JNIEXPORT jintArray JNICALL Java_com_example_grayonlyc_ImageProc_grayProc
(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h, jfloatArray width, jfloatArray maskX, jfloatArray maskY){
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
if(cbuf == NULL){
return 0;
}
/ 测试object数组 ///
jfloat *mx, *my;
//jint length;
mx = env->GetFloatArrayElements(maskX,JNI_FALSE);
my = env->GetFloatArrayElements(maskY,JNI_FALSE);
//length = env->GetArrayLength(maskX);
Point2f mask[16];
for(int i=0;i<16;i++){
mask[i].x = mx[i];
mask[i].y = my[i];
}
mask[0] = Point2f(0,1);
mx[0] = mask[0].x;
my[0] = mask[0].y;
mx[1] = mask[1].x;
my[1] = mask[1].y;
//
/ 测试float数组 ///
jfloat* arr;
jint length;
arr = env->GetFloatArrayElements(width,JNI_FALSE);
length = env->GetArrayLength(width);
float finWidth[15];
for(int i=0;i<15;i++){
finWidth[i] = arr[i];
}
finWidth[0] = 0.1f;
arr[0] = finWidth[0];
//SetIntArrayRegion(width, 0, length, arr);
//
Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
proc(imgData);//C++处理
int size=w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
void proc(Mat &src){
uchar* ptr = src.ptr(0);
for(int i = 0; i < src.cols * src.rows; i ++){
//计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
//对于一个int四字节,其彩色值存储方式为: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;
}
}
供Java调用的方法的命名规则为:Java_包名_类名_方法名。
2)ImageProc.h头文件。声明定义C文件中用到的方法。声明规则也是:Java_包名_类名_方法名。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_grayonlyc_ImageProc */
#ifndef _Included_com_example_grayonlyc_ImageProc
#define _Included_com_example_grayonlyc_ImageProc
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_grayonlyc_ImageProc
* Method: grayProc
* Signature: ([III[F[F[F)[I
*/
JNIEXPORT jintArray JNICALL Java_com_example_grayonlyc_ImageProc_grayProc
(JNIEnv *, jclass, jintArray, jint, jint, jfloatArray, jfloatArray, jfloatArray);
#ifdef __cplusplus
}
#endif
#endif
.h头文件可以自己手动创建,然后声明方法,也可以通过命令自动生成。介绍一下自动生成:
在Eclipse中写好java文件保存后,会自动生成class文件,在bin目录的classes目录下,有自己项目的包名,在相应java的包下就有相应class文件。在doc中进入到classes目录下,输入javah com.example.grayonlyc.ImageProc即可以生成.h头文件。这里的包名是我自己的,需要改成你自己的。
生成的头文件,可以自己修改成自己想要的名称,比如我这用的名称为ImageProc.h。
3)Android.mk文件。这个文件是向ndk系统描述代码的信息和需要生成文件的信息。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/OpenCVsdk/native/jni/OpenCV.mk
LOCAL_SRC_FILES := ImageProc.cpp
LOCAL_MODULE := image_proc
include $(BUILD_SHARED_LIBRARY)
4)Application.mk文件。这个文件是用来描述你的程序需要哪些模块。
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-19
5)OpencvSDK 这个是opencv的官方sdk。我把名称改成了OpenCVSDK。它的目录列表如下:
项目中因为OpencvSDK占内存超出我上传文件的限制额,所以我把它去除了,用的时候还需要下载opencv的sdk,然后把名称改成OpenCVSDK放入jni目录中就可以使用了,引用已经在Android.mk文件中写好了。
6)以上5步都完成后,需要将C文件编译成so库供java使用。首先电脑里要下载好ndk,然后配置好环境变量(没有的话百度一下,很简单,解压后配置一下环境变量就OK)。然后在doc系统中进入jni中的C文件所在目录,输入命令:ndk-build 或者 ndk-build.cmd 然后就等待编译成功,就可以发现lib下相应的文件夹内就有libimage_proc.so库了。
2、java文件。共有两个java文件。MainActivity.java和ImageProc.java文件。
1)MainActivity.java
package com.example.grayonlyc;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.Config;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private Button btnProc;
private ImageView imageView;
private TextView textView;
private Bitmap bmp;
private float[] width = new float[15];//手指宽度
//private Point[] mask = new Point[16];
private float[] maskX = new float[16];//X坐标
private float[] maskY = new float[16];//Y坐标
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.loadLibrary("image_proc");
btnProc = (Button) findViewById(R.id.btn_gray_process);
imageView = (ImageView) findViewById(R.id.image_view);
//textView = (TextView) findViewById(R.id.text_view);
//将lena图像加载程序中并进行显示
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
imageView.setImageBitmap(bmp);
btnProc.setOnClickListener(this);
for(int i=0;i<15;i++){
width[i] = 0.0f;
maskX[i] = 0;
maskY[i] = 0;
}
maskX[15] = maskY[15] = 0;
width[1] = 1.0f;
maskX[1] = maskY[1] = 1;
//textView.setText("width原值01:"+width[0]+";"+width[1]);
//textView.setText("mask原值:("+ maskX[0] + "," + maskY[0] +") ; ("+ maskX[1] + "," + maskY[1] +")");
}
@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.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
long current = System.currentTimeMillis();
int w = bmp.getWidth();
int h = bmp.getHeight();
int[] pixels = new int[w*h];
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
int[] resultInt = ImageProc.grayProc(pixels, w, h, width, maskX, maskY);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
long performance = System.currentTimeMillis() - current;
this.setTitle("NDK耗时"+ String.valueOf(performance) + " 毫秒");
imageView.setImageBitmap(resultImg);
//textView.setText("mask改变值:("+ maskX[0] + "," + maskY[0] +") ; ("+ maskX[1] + "," + maskY[1] +")");
}
}
2)ImageProc.java 改文件中是声明了一个本地方法,调用C文件中相应的方法。命名规则:方法前要有native关键字,表示调用JNI中相应方法名的方法。
package com.example.grayonlyc;
public class ImageProc {
public static native int[] grayProc(int[] pixels, int w, int h, float width[], float maskX[], float maskY[]);
}
3、布局文件,activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/btn_gray_process"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str_proc"/>
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/str_proc"/>
</LinearLayout>
以上是这个工程所有的介绍,虽然是参考别人的