昨天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 <OpenCV4Android SDKpath>/apk/OpenCV_2.4.7_Manager_2.14_armv7a-neon.apk
开始项目:
1.新建android application工程,取名Cartoonfiy,右击项目属性,勾选opencv类库
2.将林志玲MM的照片复制到drwabale随便哪个目录下,然后编写布局文件activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <Button android:id="@+id/btn_gray_process" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="卡通化" android:onClick="click" /> <ImageView android:id="@+id/image_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/str_proc"/> </LinearLayout>
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 <jni.h> /* 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 <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> 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." <<endl; exit(1); } cvtColor(src,gray,CV_BGR2GRAY); medianBlur(gray,gray,MEDIAN_BLUR_FILTER_SIZE); Laplacian(gray, edges, CV_8U, LAPLACIAN_FILTER_SIZE); threshold(edges, masks, EDGES_THRESHOLD, 255, THRESH_BINARY_INV); resize(src, smallImg, smallSize, 0,0, INTER_LINEAR); for (int i=0; i<repetitions; i++) { int ksize = 9; // Filter size. Has a large effect on speed. double sigmaColor = 9; // Filter color strength. double sigmaSpace = 7; // Spatial strength. Affects speed. bilateralFilter(smallImg, tmp, ksize, sigmaColor, sigmaSpace); bilateralFilter(tmp, smallImg, ksize, sigmaColor, sigmaSpace); } resize(smallImg, bigImg, size, 0,0, INTER_LINEAR); dst.setTo(0); //! copies those matrix elements to "m" that are marked with non-zero mask elements. bigImg.copyTo(dst,masks); return dst; }
然后,编写我们的ImageProc.cpp:
#include<com_example_cartoonfiy_ImageProc.h> #include<Cartoon.h> 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输出返回了。以后再学吧