南七忆梦 2016-01-22
前阵子的考试、旅游让我有很长一阵子没来博客看看,现在寒假了,是时候写点、看点新的东西了!
前面我主要围绕着基于OpenCV的机器学习算法在图像识别处理上的应用来展开学习的,当然这些个技术(如银行卡号识别)最终都是要运用到实际生活中的。我们就拿银行卡号识别来说,大家用的最多的微信实际上就有了这个功能。大家打开微信钱包,绑定银行卡的时候是否在输入框右侧看到一个照相机一样的按钮呢,那就是实现银行卡号识别功能的地方。现在我们也模仿微信,将opencv机器学习移植到android上。
这个app是我之前用很短的时间完成的,旨在:
1.进一步了解android编程
2.了解opencv移植到android的过程,方便开发
3.了解C++移植到android的过程,方便开发
我要完成的这个简单的app主要功能是:
1.通过手机相册中已有的照片(银行卡片)识别银行卡号(暂定农行)
2.通过手机拍照得到银行卡图片识别银行卡号(暂定农行)
由于开发时间较短,实现比较简单,主要是想尽快将成果整合出来,我将用两三篇博客总结一下主要技术要点,接下来是第一部分:
一、搞定开发环境
(1) OpenCVforAndroid
环境搭建
1> eclipse for android(推荐直接下载adt-bundle-windows)
Java作为现在android编程最主流的语言,eclipse是必不可少的编程环境,现在网上有很多自带adt插件的eclipse,当然你也可以下载adt插件,在eclipse下配置,这里推荐前者,省心省事方便无穷。
2>导入OpenCV Library
在OpenCV官网下载最新的OpenCVforAndroid,解压到workspace所在盘下;
进入eclipse,导入OpenCV Library(在项目一栏中右击选择Import);
选择上图所选选项
选择……\OpenCV-android-sdk\sdk,就可将OpenCV导入到Eclipse中。
导入成功!
测试程序
1>新建一个android application project
2> 导入OpenCV Library,右键项目,点击“Bulid Path”,选择configure build path,add我们导入的OpenCV Library。
3> 编程验证:点击按钮,灰度化图片
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.useopencvtest.MainActivity" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <ImageView android:id="@+id/imageView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/imageView1" android:layout_alignParentBottom="true" android:layout_marginBottom="159dp" android:src="@drawable/abc_ab_solid_light_holo" /> <ImageView android:id="@+id/imageView1" android:layout_width="100dp" android:layout_height="150dp" android:layout_below="@+id/textView1" android:layout_centerHorizontal="true" android:layout_marginTop="27dp" android:src="@drawable/bank" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/imageView2" android:layout_marginTop="43dp" android:layout_toRightOf="@+id/imageView1" android:text="Button" /> </RelativeLayout>
package com.example.useopencvtest; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.OpenCVLoader; import org.opencv.android.Utils; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.Log; 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 com.example.useopencvtest.R.id; public class MainActivity extends ActionBarActivity { ImageView image; Button btn; String TAG = "AAA"; //OpenCV库加载并初始化成功后的回调函数 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback( this ) { @Override public void onManagerConnected( int status) { // TODO Auto-generated method stub switch (status){ case BaseLoaderCallback.SUCCESS: Log.i(TAG, "成功加载" ); break ; default : super .onManagerConnected(status); Log.i(TAG, "加载失败" ); break ; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); image = (ImageView)findViewById(R.id.imageView2); btn = (Button)findViewById(id.button1); btn.setOnClickListener( new ProcessClickListener()); } private class ProcessClickListener implements OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub use(); } } public void use() { Bitmap bitmap = getRes("bank"); Mat temp = new Mat(); Mat mat = new Mat(); Utils.bitmapToMat(bitmap, temp); Imgproc.cvtColor(temp, mat, Imgproc.COLOR_BGR2GRAY); Utils.matToBitmap(mat, bitmap); this.image.setImageBitmap(bitmap); } @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); } public Bitmap getRes(String name) {//获得res文件夹下的图片,得到bmp图片 ApplicationInfo appInfo = getApplicationInfo(); int resID = getResources().getIdentifier(name, "drawable", appInfo.packageName); return BitmapFactory.decodeResource(getResources(), resID); } @Override protected void onResume() { // TODO Auto-generated method stub super .onResume(); //load OpenCV engine and init OpenCV library OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_4, getApplicationContext(), mLoaderCallback); Log.i(TAG, "onResume sucess load OpenCV..." ); // new Handler().postDelayed(new Runnable(){ // // @Override // public void run() { // // TODO Auto-generated method stub // procSrc2Gray(); // } // // }, 1000); } }
结果:
参考:http://blog.csdn.net/yanzi1225627/article/details/16917961
(2)Java的C++接口--------JNI
环境搭建
很多情况是开发者已经用C++进行了opencv的开发,想在移动端直接使用,这时候使用opencvforandroid就显得麻烦很多了,那这里我们可以使用Java中的JNI接口,直接调用C++的代码。
需要的工具:在(1)中的基础上,只需要安装NDK就可以了(r8版本以上)(注:现在你在百度上搜索的大部分还是写的NDK+Cygwin,但是实际上新版本的NDK(r8以上,我用的是r10版本)是不需要下载Cygwin,下过的朋友知道,这个玩意很坑的,我当初下了一个晚上都没下好)
在下载安装好NDK后,参照 http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html配置NDK。
编程测试:
MainActivity.java
package com.example.haveimgfun; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.Config; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; 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; public class MainActivity extends ActionBarActivity { ImageView imgHuaishi; Button btnNDK; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imgHuaishi = (ImageView)findViewById(R.id.img_huaishi); btnNDK = (Button)findViewById(R.id.btn_gray_process); // btnNDK.setOnClickListener( new MyClickListener()); btnNDK.setOnClickListener(new OnClickListener() {//按钮事件 public void onClick(View v) { // TODO Auto-generated method stub System.out.println("进来了"); Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.img); int h = src.getHeight(); int w = src.getWidth(); int temp[] = new int[h*w]; int result[] = new int[h*w]; src.getPixels(temp, 0, w, 0, 0, w, h); result = LibImgFun.ImgFun(temp, w, h); System.out.println("成功了"); Bitmap last = Bitmap.createBitmap(w, h, Config.RGB_565); last.setPixels(result, 0, w, 0, 0, w, h); imgHuaishi.setImageBitmap(last); } }); } @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); } }
LibImgFun.java
package com.example.haveimgfun; public class LibImgFun { static { System.loadLibrary("ImgFun"); } /** * @param width the current view width * @param height the current view height */ public static native int[] ImgFun(int[] buf, int w, int h); }
右击项目,点击android tools选择add native support,这样会出现一个jni文件夹,我们新建文件如下:
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) OPENCV_CAMERA_MODULES:=off override OPENCV_INSTALL_MODULES:=on OPENCV_LIB_TYPE:=SHARED OPENCV_LIB_TYPE :=STATIC $(info ==$(OPENCV_INSTALL_MODULES)==) include G:/OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk LOCAL_MODULE := ImgFun LOCAL_SRC_FILES := ImgFun.cpp include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_STL:=gnustl_static APP_CPPFLAGS:=-frtti -fexceptions APP_ABI:=armeabi armeabi-v7a APP_PLATFORM := android-8
ImgFun.cpp
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <opencv2/opencv.hpp> using namespace cv; extern "C" { JNIEXPORT jintArray JNICALL Java_com_example_haveimgfun_LibImgFun_ImgFun( JNIEnv* env, jobject obj, jintArray buf, int w, int h); JNIEXPORT jintArray JNICALL Java_com_example_haveimgfun_LibImgFun_ImgFun( JNIEnv* env, jobject obj, jintArray buf, int w, int h){ jint *cbuf; cbuf = env->GetIntArrayElements(buf, NULL); if(cbuf == NULL) { return 0; } Mat myimg(h, w, CV_8UC4, (unsigned char*)cbuf); for(int j=0; j<myimg.rows/2; j++) { myimg.row(j).setTo(Scalar(0, 0, 0, 0)); } int size=w*h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, cbuf); env->ReleaseIntArrayElements(buf, cbuf, 0); return result; } }
至此,环境搭建及测试完毕!