前阵子的考试、旅游让我有很长一阵子没来博客看看,现在寒假了,是时候写点、看点新的东西了!
前面我主要围绕着基于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;
}
}
至此,环境搭建及测试完毕!