《Master Opencv...读书笔记》卡通化效果移植到android系统

  1. 云栖社区>
  2. 博客>
  3. 正文

《Master Opencv...读书笔记》卡通化效果移植到android系统

taotao1233 2013-12-25 20:34:49 浏览1124
展开阅读全文

昨天CSDN博客抽风了。这是第7次编辑这个文件了,之前老是提交失败!

 

 

声明

1.电脑比较坑爹,前置摄像头坏掉了。

2.卡通化效果运行比较慢,老外的书上说是,人每触摸一下屏幕,才生成一张卡通化效果的图片

因此,为了简便期间,我就只对一副图像进行卡通化效果。

原理什么的见前面的文章,本文的目的,是熟悉ndkjni

 

环境需求:

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

之后,在classes目录下将会有com_example_cartoonfiy_ImageProc.h文件,具体内容如下:

/* 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)  

和Application.mk:

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.关于cjava端数据传输,可以参考三个姓王的兄弟写的《android高级开发实战-UI、NDK、安全》。我这边在端直接把mat.data转换成int输出返回了。以后再学吧

 


 

网友评论

登录后评论
0/500
评论
taotao1233
+ 关注