车牌识别,移植到android系统
Posted taotao1233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了车牌识别,移植到android系统相关的知识,希望对你有一定的参考价值。
首先吐槽,搞了1天半,终于弄好了。自己android开发是小白,之前一门心思想在jni目录下读取xml文件,事实证明无论如何都不行的。好吧,后来发现资源文件应该都放在assets目录下,可是文件会被压缩,必须用什么assetmanager访问。opencv之前训练的两个svm.xml和ocr.xml文件,和一般的xml文件不同的,自己解析xml存到opencv的mat中太麻烦了。后来想了又想,还是放到sdcard中比较好,我是通过DDMS导入的,反正这次只是长姿势
声明:
1.本次导入的汽车图片还是包含西班牙的车牌的汽车,它与中国车牌最大的不同是不包含中文,西班牙车牌含有0-9数字及20个英文字符
2.在模拟机上运行速度貌似和vs2008一样慢,而且有识别错的可能,我碰到过
3.原理什么的见我前面的文章,我这次直接使用训练好的svm.xml和ocr.xml,并给出完整的识别流程。整个工程文件,待会上传csdn下载频道
环境需求:
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工程,取名CarPlate,右击项目属性,勾选opencv类库
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/myshow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="检测结果...." />
<Button
android:id="@+id/btn_plate"
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>
3. 新建CarPlateDetection 类,编写本地化方法,作为调用 c 语言代码的入口:
package com.example.carplate;
public class CarPlateDetection
public static native String ImageProc(int[] pixels, int w, int h,String path);
4 . 在 dos 窗口中,使用 javah 工具,自动生成 c 语言的头文件,具体方法就是在DOS窗口中跑到 CarPlate 项目的 bin\\classes 目录下,输入:
javah com.example.carplate.CarPlateDetection
之后,在classes目录下将会有com_example_carplate_CarPlateDetection.h文件
5.新建一个jni文件夹,把刚才的那个com_example_carplate_CarPlateDetection.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 += Plate_Recognition.cpp
LOCAL_SRC_FILES += Plate_Segment.cpp
LOCAL_SRC_FILES += Plate.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_MODULE := imageproc
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
6.修改AndroidManifest.xml,增加sdcard权限【就算是读取,也要加上!】:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
7.
回到MainActivity
中,编写java端主要的代码:
package com.example.carplate;
import java.io.File;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.*;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity
private ImageView imageView = null;
private Bitmap bmp = null;
private TextView m_text = null;
private String path = null; //SDCARD 根目录
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
m_text = (TextView) findViewById(R.id.myshow);
//将汽车完整图像加载程序中并进行显示
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test2);
imageView.setImageBitmap(bmp);
path = Environment.getExternalStorageDirectory().getAbsolutePath();//获取跟目录
System.out.println(path);
//OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this)
@Override
public void onManagerConnected(int status)
switch (status)
case LoaderCallbackInterface.SUCCESS:
System.loadLibrary("imageproc");
break;
default:
super.onManagerConnected(status);
break;
;
public void click(View view)
System.out.println("entering the jni");
int w = bmp.getWidth();
int h = bmp.getHeight();
int[] pixels = new int[w * h];
String result=null;
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
// System.out.println(Environment.getExternalStorageState());
result=CarPlateDetection.ImageProc(pixels, w, h,path);
System.out.println(result);
m_text.setText(result);
@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);
8.好了,现在开始主要的 C 语言部分。对应头文件和源文件内容分别是(这些文件也放在 jni 目录下):
Plate.h:【车牌类,包含车牌数据结构及对识别的车牌字符顺序调整函数】
#ifndef Plate_h
#define Plate_h
#include <string.h>
#include <vector>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
using namespace std;
using namespace cv;
class Plate
public:
Plate();
Plate(Mat img, Rect pos);
string str();
Rect position;
Mat plateImg;
vector<char> chars;
vector<Rect> charsPos;
;
#endif
Plate.cpp:
#include "Plate.h"
Plate::Plate()
Plate::Plate(Mat img, Rect pos)
plateImg=img;
position=pos;
string Plate::str()
string result="";
//Order numbers
vector<int> orderIndex;
vector<int> xpositions;
for(int i=0; i< charsPos.size(); i++)
orderIndex.push_back(i);
xpositions.push_back(charsPos[i].x);
float min=xpositions[0];
int minIdx=0;
for(int i=0; i< xpositions.size(); i++)
min=xpositions[i];
minIdx=i;
for(int j=i; j<xpositions.size(); j++)
if(xpositions[j]<min)
min=xpositions[j];
minIdx=j;
int aux_i=orderIndex[i];
int aux_min=orderIndex[minIdx];
orderIndex[i]=aux_min;
orderIndex[minIdx]=aux_i;
float aux_xi=xpositions[i];
float aux_xmin=xpositions[minIdx];
xpositions[i]=aux_xmin;
xpositions[minIdx]=aux_xi;
for(int i=0; i<orderIndex.size(); i++)
result=result+chars[orderIndex[i]];
return result;
PlateSegment.h:【功能:从一张汽车图片中分割得到一张车牌】
#ifndef seg_h
#define seg_h
#include<iostream>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include "Plate.h"
using namespace std;
using namespace cv;
bool verifySizes(RotatedRect mr);
Mat histeq(Mat in);
vector<Plate> segment(Mat input);
#endif
PlateSegment.cpp:
#include "Plate_Segment.h"
//对minAreaRect获得的最小外接矩形,用纵横比进行判断
bool verifySizes(RotatedRect mr)
float error=0.4;
//Spain car plate size: 52x11 aspect 4,7272
float aspect=4.7272;
//Set a min and max area. All other patchs are discarded
int min= 15*aspect*15; // minimum area
int max= 125*aspect*125; // maximum area
//Get only patchs that match to a respect ratio.
float rmin= aspect-aspect*error;
float rmax= aspect+aspect*error;
int area= mr.size.height * mr.size.width;
float r= (float)mr.size.width / (float)mr.size.height;
if(r<1)
r= (float)mr.size.height / (float)mr.size.width;
if(( area < min || area > max ) || ( r < rmin || r > rmax ))
return false;
else
return true;
Mat histeq(Mat in)
Mat out(in.size(), in.type());
if(in.channels()==3)
Mat hsv;
vector<Mat> hsvSplit;
cvtColor(in, hsv, CV_BGR2HSV);
split(hsv, hsvSplit);
equalizeHist(hsvSplit[2], hsvSplit[2]);
merge(hsvSplit, hsv);
cvtColor(hsv, out, CV_HSV2BGR);
else if(in.channels()==1)
equalizeHist(in, out);
return out;
vector<Plate> segment(Mat input)
vector<Plate> output;
//apply a Gaussian blur of 5 x 5 and remove noise
Mat img_gray;
cvtColor(input, img_gray, CV_BGR2GRAY);
blur(img_gray, img_gray, Size(5,5));
//Finde vertical edges. Car plates have high density of vertical lines
Mat img_sobel;
Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT);//xorder=1,yorder=0,kernelsize=3
//apply a threshold filter to obtain a binary image through Otsu's method
Mat img_threshold;
threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
//Morphplogic operation close:remove blank spaces and connect all regions that have a high number of edges
Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) );
morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);
//Find 轮廓 of possibles plates
vector< vector< Point> > contours;
findContours(img_threshold,
contours, // a vector of contours
CV_RETR_EXTERNAL, // 提取外部轮廓
CV_CHAIN_APPROX_NONE); // all pixels of each contours
//Start to iterate to each contour founded
vector<vector<Point> >::iterator itc= contours.begin();
vector<RotatedRect> rects;
//Remove patch that are no inside limits of aspect ratio and area.
while (itc!=contours.end())
//Create bounding rect of object
RotatedRect mr= minAreaRect(Mat(*itc));
if( !verifySizes(mr))
itc= contours.erase(itc);
else
++itc;
rects.push_back(mr);
cv::Mat result;
input.copyTo(result);
for(int i=0; i< rects.size(); i++)
//get the min size between width and height
float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height;
minSize=minSize-minSize*0.5;
//initialize rand and get 5 points around center for floodfill algorithm
srand ( time(NULL) );
//Initialize floodfill parameters and variables
Mat mask;
mask.create(input.rows + 2, input.cols + 2, CV_8UC1);
mask= Scalar::all(0);
int loDiff = 30;
int upDiff = 30;
int connectivity = 4;
int newMaskVal = 255;
int NumSeeds = 10;
Rect ccomp;
int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
for(int j=0; j<NumSeeds; j++)
Point seed;
seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);
seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);
int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
//Check new floodfill mask match for a correct patch.
//Get all points detected for get Minimal rotated Rect
vector<Point> pointsInterest;
Mat_<uchar>::iterator itMask= mask.begin<uchar>();
Mat_<uchar>::iterator end= mask.end<uchar>();
for( ; itMask!=end; ++itMask)
if(*itMask==255)
pointsInterest.push_back(itMask.pos());
RotatedRect minRect = minAreaRect(pointsInterest);
if(verifySizes(minRect))
// rotated rectangle drawing
Point2f rect_points[4]; minRect.points( rect_points );
//Get rotation matrix
float r= (float)minRect.size.width / (float)minRect.size.height;
float angle=minRect.angle;
if(r<1)
angle=90+angle;
Mat rotmat= getRotationMatrix2D(minRect.center, angle,1);
//Create and rotate image
Mat img_rotated;
warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);
//Crop image
Size rect_size=minRect.size;
if(r < 1)
swap(rect_size.width, rect_size.height);
Mat img_crop;
getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);
Mat resultResized;
resultResized.create(33,144, CV_8UC3);
resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
//Equalize croped image
Mat grayResult;
cvtColor(resultResized, grayResult, CV_BGR2GRAY);
blur(grayResult, grayResult, Size(3,3));
grayResult=histeq(grayResult);
output.push_back(Plate(grayResult,minRect.boundingRect()));
return output;
PlateRecogntion.h:【从车牌图片上识别各个字符】
#ifndef rec_h
#define rec_h
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <ml.h>
#include <iostream>
#include <vector>
#define HORIZONTAL 1
#define VERTICAL 0
using namespace std;
using namespace cv;
bool verifySizes(Mat r);
Mat preprocessChar(Mat in);
Mat ProjectedHistogram(Mat img, int t);
Mat features(Mat in, int sizeData);
int classify(Mat f,CvANN_MLP *ann);
void train(Mat TrainData, Mat classes,CvANN_MLP *ann,int nlayers);
#endif
PlateRecognition.cpp:
#include "Plate_Recognition.h"
const int numCharacters=30;
bool verifySizes(Mat r)
//Char sizes 45x77
float aspect=45.0f/77.0f;
float charAspect= (float)r.cols/(float)r.rows;
float error=0.35;
float minHeight=15;
float maxHeight=28;
//We have a different aspect ratio for number 1, and it can be ~0.2
float minAspect=0.2;
float maxAspect=aspect+aspect*error;
//area of pixels
float area=countNonZero(r);
//bb area
float bbArea=r.cols*r.rows;
// of pixel in area
float percPixels=area/bbArea;
if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeight && r.rows < maxHeight)
return true;
else
return false;
Mat preprocessChar(Mat in)
//Remap image
int h=in.rows;
int w=in.cols;
int charSize=20; //统一每个字符的大小
Mat transformMat=Mat::eye(2,3,CV_32F);
int m=max(w,h);
transformMat.at<float>(0,2)=m/2 - w/2;
transformMat.at<float>(1,2)=m/2 - h/2;
Mat warpImage(m,m, in.type());
warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );
Mat out;
resize(warpImage, out, Size(charSize, charSize) );
return out;
//create the accumulation histograms,img is a binary image, t is 水平或垂直
Mat ProjectedHistogram(Mat img, int t)
int sz=(t)?img.rows:img.cols;
Mat mhist=Mat::zeros(1,sz,CV_32F);
for(int j=0; j<sz; j++)
Mat data=(t)?img.row(j):img.col(j);
mhist.at<float>(j)=countNonZero(data); //统计这一行或一列中,非零元素的个数,并保存到mhist中
//Normalize histogram
double min, max;
minMaxLoc(mhist, &min, &max);
if(max>0)
mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方图中的最大值,归一化直方图
return mhist;
Mat features(Mat in, int sizeData)
//Histogram features
Mat vhist=ProjectedHistogram(in,VERTICAL);
Mat hhist=ProjectedHistogram(in,HORIZONTAL);
//Low data feature
Mat lowData;
resize(in, lowData, Size(sizeData, sizeData) );
//Last 10 is the number of moments components
int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;
Mat out=Mat::zeros(1,numCols,CV_32F);
//Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量
int j=0;
for(int i=0; i<vhist.cols; i++)
out.at<float>(j)=vhist.at<float>(i);
j++;
for(int i=0; i<hhist.cols; i++)
out.at<float>(j)=hhist.at<float>(i);
j++;
for(int x=0; x<lowData.cols; x++)
for(int y=0; y<lowData.rows; y++)
out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);
j++;
return out;
int classify(Mat f,CvANN_MLP *ann)
int result=-1;
Mat output(1, 30, CV_32FC1); //西班牙车牌只有30种字符
(*ann).predict(f, output);
Point maxLoc;
double maxVal;
minMaxLoc(output, 0, &maxVal, 0, &maxLoc);
return maxLoc.x;
void train(Mat TrainData, Mat classes,CvANN_MLP *ann,int nlayers)
Mat layers(1,3,CV_32SC1);
layers.at<int>(0)= TrainData.cols;
layers.at<int>(1)= nlayers;
layers.at<int>(2)= 30;
(*ann).create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1);
//Prepare trainClases
//Create a mat with n trained data by m classes
Mat trainClasses;
trainClasses.create( TrainData.rows, 30, CV_32FC1 );
for( int i = 0; i < trainClasses.rows; i++ )
for( int k = 0; k < trainClasses.cols; k++ )
//If class of data i is same than a k class
if( k == classes.at<int>(i) )
trainClasses.at<float>(i,k) = 1;
else
trainClasses.at<float>(i,k) = 0;
Mat weights( 1, TrainData.rows, CV_32FC1, Scalar::all(1) );
//Learn classifier
(*ann).train( TrainData, trainClasses, weights );
然后,编写我们的 ImageProc.cpp :【这边我把sdcard的路径都写死了,大家自己调整下】
#include<com_example_carplate_CarPlateDetection.h>
#include "Plate.h"
#include "Plate_Segment.h"
#include "Plate_Recognition.h"
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
/*char* jstring2str(JNIEnv* env, jstring jstr)
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr,mid,strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr,JNI_FALSE);
if(alen > 0)
rtn = (char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
env->ReleaseByteArrayElements(barr,ba,0);
return rtn;
*/
JNIEXPORT jstring JNICALL Java_com_example_carplate_CarPlateDetection_ImageProc
(JNIEnv *env, jclass obj, jintArray buf, jint w, jint h,jstring dir)
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, false);
//char* path = jstring2str(env,dir);
Size size;
size.width = w;
size.height = h;
Mat imageData,input;
imageData = Mat(size, CV_8UC4, (unsigned char*)cbuf);
input = Mat(size, CV_8UC3);
cvtColor(imageData,input,CV_BGRA2BGR);
vector<Plate> posible_regions = segment(input);
const char strCharacters[] = '0','1','2','3','4','5','6','7','8','9','B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z';
CvANN_MLP ann;
//SVM for each plate region to get valid car plates,Read file storage.
FileStorage fs;
//strcat(path,"/SVM.xml");
fs.open("/storage/sdcard/SVM.xml", FileStorage::READ);
Mat SVM_TrainingData;
Mat SVM_Classes;
fs["TrainingData"] >> SVM_TrainingData;
fs["classes"] >> SVM_Classes;
if(fs.isOpened())
LOGD("read success!");
//Set SVM params
LOGD("size:%d",SVM_TrainingData.rows);
SVM_TrainingData.convertTo(SVM_TrainingData, CV_32FC1);
SVM_Classes.convertTo(SVM_Classes, CV_32FC1);
CvSVMParams SVM_params;
SVM_params.svm_type = CvSVM::C_SVC;
SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR;
SVM_params.degree = 0;
SVM_params.gamma = 1;
SVM_params.coef0 = 0;
SVM_params.C = 1;
SVM_params.nu = 0;
SVM_params.p = 0;
SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);
LOGD("Everything is ready");
//Train SVM
LOGD("START TO ENTER SVM PREDICT");
CvSVM svmClassifier(SVM_TrainingData, SVM_Classes, Mat(), Mat(), SVM_params);
//For each possible plate, classify with svm if it's a plate or no
vector<Plate> plates;
for(int i=0; i< posible_regions.size(); i++)
Mat img=posible_regions[i].plateImg;
Mat p= img.reshape(1, 1);
p.convertTo(p, CV_32FC1);
int response = (int)svmClassifier.predict( p );
if(response==1)
plates.push_back(posible_regions[i]);
LOGD("SVM PREDICT FINISH");
fs.release();
//Read file storage.
FileStorage fs2;
fs2.open("/storage/sdcard/OCR.xml", FileStorage::READ);
Mat TrainingData;
Mat Classes;
fs2["TrainingDataF15"] >> TrainingData;
fs2["classes"] >> Classes;
LOGD("size:%d",TrainingData.rows);
LOGD("START TO TRAIN MLP");
//训练神经网络
train(TrainingData, Classes,&ann,10);
LOGD("FINISH TRAIN MLP");
Mat inputs=plates[0].plateImg;
Plate mplate;
//dealing image and save each character image into vector<CharSegment>
//Threshold input image
Mat img_threshold;
threshold(inputs, img_threshold, 60, 255, CV_THRESH_BINARY_INV);
Mat img_contours;
img_threshold.copyTo(img_contours);
//Find contours of possibles characters
vector< vector< Point> > contours;
findContours(img_contours,
contours, // a vector of contours
CV_RETR_EXTERNAL, // retrieve the external contours
CV_CHAIN_APPROX_NONE); // all pixels of each contours
//Start to iterate to each contour founded
vector<vector<Point> >::iterator itc= contours.begin();
LOGD("Before extracting hist and low-resolution image");
//Remove patch that are no inside limits of aspect ratio and area.
while (itc!=contours.end())
//Create bounding rect of object
Rect mr= boundingRect(Mat(*itc));
//Crop image
Mat auxRoi(img_threshold, mr);
if(verifySizes(auxRoi))
auxRoi=preprocessChar(auxRoi);
LOGD("FINISH extracting features");
//对每一个小方块,提取直方图特征
Mat f=features(auxRoi,15);
//For each segment feature Classify
LOGD("START TO CLASSIFY IN MLP");
int character=classify(f,&ann);
mplate.chars.push_back(strCharacters[character]);
LOGD("FINISH CLASSIFY");
mplate.charsPos.push_back(mr);
//printf("%c ",strCharacters[character]);
++itc;
fs2.release();
string licensePlate=mplate.str();
//const char *result;
//result=licensePlate.c_str();
env->ReleaseIntArrayElements(buf, cbuf, 0);
return env->NewStringUTF(licensePlate.c_str());
9.最后用cygwin进行交叉编译:
打开cygwin,输入
cd /cygdrive/e/worksapce/CarPlate
ndk-build
记得按F5,并clean一下工程,这是在libs目录下有个libimage_proc.so文件,
10.通过DDMS向sdcard中添加文件:
打开虚拟机,点击DDMS:
如果能进入如下界面的话:【否则点击左半边的小倒三角,选择reset adb】
点击右半边右上角第二个按钮:
跑到如storage/sdcard目录下,将之前训练好的SVM.XML和OCR.XML都加入进去。
如果cygwin没有报错的话,然后运行我们的android applicatoin
效果图:
注意:
1.如果想玩国内车牌的话,可以用我之前 2篇文章的方法,自己人工分类图片【不用你裁剪,只要挑选就行】,并运行程序得到相应的xml文件
2.这边我的路径和资源摆放都很不够理想,暂时也想不出更好的了
完整的程序下载地址:http://download.csdn.net/detail/jinshengtao/6828651
里面的assets文件夹下有训练好的svm.xml和ocr.xml,把他放到sdcard中吧
以上是关于车牌识别,移植到android系统的主要内容,如果未能解决你的问题,请参考以下文章
不想手动输入车牌号?手机Android端车牌识别助你一臂之力!