昨天CSDN博客抽风了。这是第7次编辑这个文件了,之前老是提交失败!
声明
1.电脑比较坑爹,前置摄像头坏掉了。
2.卡通化效果运行比较慢,老外的书上说是,人每触摸一下屏幕,才生成一张卡通化效果的图片
因此,为了简便期间,我就只对一副图像进行卡通化效果。
原理什么的见前面的文章,本文的目的,是熟悉ndk和jni
环境需求:
eclipse juno
ndk(r9)
android sdk 4.4 api 19
opencv 2.4.7 android版本
cygwin
准备工作:
1.将E:\OpenCV-2.4.7.1-android-sdk\sdk中的java项目导入工作空间,日后凡事java端调用opencv的函数都要用到这个类库
2.安装opencv manager.apk,目前在android上所有的opencv程序都必须依附于android manger。在DOS窗口口中执行:
adb install
开始项目:
1.新建android application工程,取名Cartoonfiy,右击项目属性,勾选opencv类库
2.将林志玲MM的照片复制到drwabale随便哪个目录下,然后编写布局文件activity_main.xml:
4.新建ImageProc类,编写本地化方法,作为调用c语言代码的入口:
package com.example.cartoonfiy;
public class ImageProc {
public static native void CartoonProc(int[] pixels,int[] result, int w, int h);
}
5.在dos窗口中,使用javah工具,自动生成c语言的头文件,具体方法就是在DOS窗口中跑到Cartoonfiy项目的bin\classes目录下:
javah com.example.cartoonfiy.ImageProc
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_example_cartoonfiy_ImageProc */
#ifndef _Included_com_example_cartoonfiy_ImageProc
#define _Included_com_example_cartoonfiy_ImageProc
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_cartoonfiy_ImageProc
* Method: CartoonProc
* Signature: ([III)[I
*/
JNIEXPORT void JNICALL Java_com_example_cartoonfiy_ImageProc_CartoonProc
(JNIEnv *, jclass, jintArray,jintArray, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
6.我们新建一个jni文件夹(名字就这个,不能随便改,否则ndk-build命令据说找不到的),把刚才的那个com_example_cartoonfiy_ImageProc.h文件拷贝过来。然后分别编写Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include E:/OpenCV-2.4.7.1-android-sdk/sdk/native/jni/OpenCV.mk
LOCAL_SRC_FILES := ImageProc.cpp
LOCAL_SRC_FILES += Cartoon.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_MODULE := image_proc
include $(BUILD_SHARED_LIBRARY)
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-8
7.回到MainActivity中,编写java端主要的代码:
package com.example.cartoonfiy;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.Config;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends Activity {
private ImageView imageView;
private Bitmap bmp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
//将lena图像加载程序中并进行显示
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lady);
imageView.setImageBitmap(bmp);
}
//OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:{
System.loadLibrary("image_proc");
} break;
default:{
super.onManagerConnected(status);
} break;
}
}
};
public void click(View view){
int w = bmp.getWidth();
int h = bmp.getHeight();
int[] pixels = new int[w * h];
int[] resultInt = new int[w*h];
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
ImageProc.CartoonProc(pixels,resultInt, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
imageView.setImageBitmap(resultImg);
}
@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
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
//通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
//OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
}
}
好了,现在开始主要的C语言部分。由于是针对一副图像处理,我把上次的代码修改封装成一个函数,对应头文件和源文件内容分别是(这两个文件也放在jni目录下):
Cartoon.h:
#include
#include
#include
#include
using namespace cv;
using namespace std;
Mat image_proc(Mat src);
Cartoon.cpp:
#include "Cartoon.h"
//对一张图像进行卡通化效果处理
Mat image_proc(Mat src)
{
Mat smallImg,tmp,bigImg,gray,edges,masks,dst;
int repetitions = 7; // Repetitions for strong cartoon effect.
const int MEDIAN_BLUR_FILTER_SIZE = 7;
const int LAPLACIAN_FILTER_SIZE = 5;
const int EDGES_THRESHOLD = 80;
Size size = src.size();
Size smallSize;
smallSize.width = size.width/2;
smallSize.height = size.height/2;
smallImg = Mat(smallSize, CV_8UC3);
tmp = Mat(smallSize, CV_8UC3);
dst= Mat(size,CV_8UC3);
if (src.empty()) {
cerr << "ERROR: Couldn't grab a video frame." <
然后,编写我们的ImageProc.cpp:
#include
#include
JNIEXPORT void JNICALL Java_com_example_cartoonfiy_ImageProc_CartoonProc
(JNIEnv *env, jclass obj, jintArray buf,jintArray res, jint w, jint h){
jint *cbuf,*bgra;
cbuf = env->GetIntArrayElements(buf, false);
bgra = env->GetIntArrayElements(res, 0);
Mat src,dst,mbgra,imgData;
Size size;
size.width = w;
size.height = h;
src = Mat(size, CV_8UC3);
dst = Mat(size, CV_8UC3);
imgData = Mat(size, CV_8UC4, (unsigned char*)cbuf);
mbgra = Mat(size, CV_8UC4, (unsigned char *)bgra);
cvtColor(imgData,src,CV_BGRA2BGR);
dst = image_proc(src);
cvtColor(dst, mbgra, CV_BGR2BGRA);
env->ReleaseIntArrayElements(buf, cbuf, 0);
env->ReleaseIntArrayElements(res, bgra, 0);
}
最后用cygwin进行交叉编译:
打开cygwin,输入
cd /cygdrive/e/worksapce/Cartoonfiy
ndk-build
记得按F5,并clean一下工程,这是在libs目录下有个libimage_proc.so文件,
如果cygwin没有报错的话,然后运行我们的android applicatoin
运行效果:
注意:
1.老外书中说的android处理图片的color format,什么用照相机拍出来的是:YUV420s,用Bitmap读取的本地图片是:BGRA。当你的图像处理函数只能应付BGR时,你必须用cvtColor进行格式转换。老外又说什么转换格式会影响速度等等,这些先不管了。
2.关于c与java端数据传输,可以参考三个姓王的兄弟写的《android高级开发实战-UI、NDK、安全》。我这边在c 端直接把mat.data转换成int输出返回了。以后再学吧