车牌识别,移植到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窗口口中执行:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. adb install <OpenCV4Android SDKpath>/apk/OpenCV_2.4.7_Manager_2.14_armv7a-neon.apk  

开始项目:

1.新建android application工程,取名CarPlate,右击项目属性,勾选opencv类库

2.将汽车照片复制到drwabale随便哪个目录下,然后编写布局文件activity_main.xml:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"  
  6.     tools:context=".MainActivity" >  
  7.   
  8.     <TextView  
  9.         android:id="@+id/myshow"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="检测结果...." />  
  13.     <Button     
  14.         android:id="@+id/btn_plate"    
  15.         android:layout_width="fill_parent"    
  16.         android:layout_height="wrap_content"    
  17.         android:text="车牌检测"  
  18.         android:onClick="click"  
  19.         />   
  20.     <ImageView    
  21.         android:id="@+id/image_view"    
  22.         android:layout_width="wrap_content"    
  23.         android:layout_height="wrap_content"    
  24.         android:contentDescription="@string/str_proc"/>     
  25. </LinearLayout>  

3.新建CarPlateDetection类,编写本地化方法,作为调用c语言代码的入口:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.example.carplate;  
  2.   
  3.   
  4. public class CarPlateDetection {  
  5.     public static native String ImageProc(int[] pixels, int w, int h,String path);  
  6. }  

4.在dos窗口中,使用javah工具,自动生成c语言的头文件,具体方法就是在DOS窗口中跑到CarPlate项目的bin\classes目录下,输入:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. javah com.example.carplate.CarPlateDetection  
之后,在classes目录下将会有com_example_carplate_CarPlateDetection.h文件


5.新建一个jni文件夹,把刚才的那个com_example_carplate_CarPlateDetection.h文件拷贝过来。然后编写Android.mk:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. LOCAL_PATH := $(call my-dir)    
  2. include $(CLEAR_VARS)    
  3. include E:/OpenCV-2.4.7.1-android-sdk/sdk/native/jni/OpenCV.mk    
  4. LOCAL_SRC_FILES  := ImageProc.cpp    
  5. LOCAL_SRC_FILES  += Plate_Recognition.cpp  
  6. LOCAL_SRC_FILES  += Plate_Segment.cpp  
  7. LOCAL_SRC_FILES  += Plate.cpp  
  8. LOCAL_C_INCLUDES += $(LOCAL_PATH)  
  9. LOCAL_MODULE     := imageproc    
  10. LOCAL_LDLIBS += -llog   
  11. include $(BUILD_SHARED_LIBRARY)    


6.修改AndroidManifest.xml,增加sdcard权限【就算是读取,也要加上!】:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
  2. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>   

7.回到MainActivity中,编写java端主要的代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.example.carplate;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.opencv.android.BaseLoaderCallback;  
  6. import org.opencv.android.LoaderCallbackInterface;  
  7. import org.opencv.android.OpenCVLoader;  
  8. import org.opencv.core.*;  
  9.   
  10. import android.os.Bundle;  
  11. import android.os.Environment;  
  12. import android.app.Activity;  
  13. import android.graphics.Bitmap;  
  14. import android.graphics.BitmapFactory;  
  15. import android.view.Menu;  
  16. import android.view.View;  
  17. import android.widget.ImageView;  
  18. import android.widget.TextView;  
  19.   
  20. public class MainActivity extends Activity {  
  21.     private ImageView imageView = null;    
  22.     private Bitmap bmp = null;    
  23.     private TextView m_text = null;  
  24.     private String path = null//SDCARD 根目录  
  25.     @Override  
  26.     protected void onCreate(Bundle savedInstanceState) {  
  27.         super.onCreate(savedInstanceState);  
  28.         setContentView(R.layout.activity_main);  
  29.         imageView = (ImageView) findViewById(R.id.image_view);    
  30.         m_text = (TextView) findViewById(R.id.myshow);  
  31.         //将汽车完整图像加载程序中并进行显示  
  32.          bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test2);    
  33.          imageView.setImageBitmap(bmp);  
  34.          path = Environment.getExternalStorageDirectory().getAbsolutePath();//获取跟目录   
  35.          System.out.println(path);  
  36.     }  
  37.   
  38.     //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作    
  39.     private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {    
  40.        @Override    
  41.        public void onManagerConnected(int status) {    
  42.            switch (status) {    
  43.                case LoaderCallbackInterface.SUCCESS:{    
  44.                    System.loadLibrary("imageproc");    
  45.                } break;    
  46.                default:{    
  47.                    super.onManagerConnected(status);    
  48.                } break;    
  49.            }    
  50.        }    
  51.    };    
  52.      
  53.    public void click(View view){  
  54.        System.out.println("entering the jni");  
  55.        int w = bmp.getWidth();  
  56.        int h = bmp.getHeight();  
  57.        int[] pixels = new int[w * h];  
  58.        String result=null;  
  59.        bmp.getPixels(pixels, 0, w, 00, w, h);  
  60.       // System.out.println(Environment.getExternalStorageState());  
  61.        result=CarPlateDetection.ImageProc(pixels, w, h,path);  
  62.        System.out.println(result);  
  63.        m_text.setText(result);     
  64.    }  
  65.      
  66.     @Override  
  67.     protected void onResume() {  
  68.         // TODO Auto-generated method stub  
  69.         super.onResume();  
  70.           //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是    
  71.        //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中    
  72.        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);    
  73.     }  
  74. }  

8.好了,现在开始主要的C语言部分。对应头文件和源文件内容分别是(这些文件也放在jni目录下):

Plate.h:【车牌类,包含车牌数据结构及对识别的车牌字符顺序调整函数】

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #ifndef Plate_h  
  2. #define Plate_h  
  3.   
  4. #include <string.h>  
  5. #include <vector>  
  6.   
  7. #include <cv.h>  
  8. #include <highgui.h>  
  9. #include <cvaux.h>  
  10.   
  11. using namespace std;  
  12. using namespace cv;  
  13.   
  14. class Plate{  
  15.     public:  
  16.         Plate();  
  17.         Plate(Mat img, Rect pos);  
  18.         string str();  
  19.         Rect position;  
  20.         Mat plateImg;  
  21.         vector<char> chars;  
  22.         vector<Rect> charsPos;          
  23. };  
  24.   
  25. #endif  

Plate.cpp:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "Plate.h"  
  2.   
  3. Plate::Plate(){  
  4. }  
  5.   
  6. Plate::Plate(Mat img, Rect pos){  
  7.     plateImg=img;  
  8.     position=pos;  
  9. }  
  10.   
  11. string Plate::str(){  
  12.     string result="";  
  13.     //Order numbers  
  14.     vector<int> orderIndex;  
  15.     vector<int> xpositions;  
  16.     for(int i=0; i< charsPos.size(); i++){  
  17.         orderIndex.push_back(i);  
  18.         xpositions.push_back(charsPos[i].x);  
  19.     }  
  20.     float min=xpositions[0];  
  21.     int minIdx=0;  
  22.     for(int i=0; i< xpositions.size(); i++){  
  23.         min=xpositions[i];  
  24.         minIdx=i;  
  25.         for(int j=i; j<xpositions.size(); j++){  
  26.             if(xpositions[j]<min){  
  27.                 min=xpositions[j];  
  28.                 minIdx=j;  
  29.             }  
  30.         }  
  31.         int aux_i=orderIndex[i];  
  32.         int aux_min=orderIndex[minIdx];  
  33.         orderIndex[i]=aux_min;  
  34.         orderIndex[minIdx]=aux_i;  
  35.           
  36.         float aux_xi=xpositions[i];  
  37.         float aux_xmin=xpositions[minIdx];  
  38.         xpositions[i]=aux_xmin;  
  39.         xpositions[minIdx]=aux_xi;  
  40.     }  
  41.     for(int i=0; i<orderIndex.size(); i++){  
  42.         result=result+chars[orderIndex[i]];  
  43.     }  
  44.     return result;  
  45. }  

PlateSegment.h:【功能:从一张汽车图片中分割得到一张车牌】

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #ifndef seg_h  
  2. #define seg_h  
  3.   
  4. #include<iostream>  
  5. #include <cv.h>  
  6. #include <highgui.h>  
  7. #include <cvaux.h>  
  8. #include "Plate.h"  
  9.   
  10. using namespace std;  
  11. using namespace cv;  
  12.   
  13. bool verifySizes(RotatedRect mr);  
  14. Mat histeq(Mat in);  
  15. vector<Plate> segment(Mat input);  
  16.   
  17. #endif  

PlateSegment.cpp:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "Plate_Segment.h"  
  2.   
  3. //对minAreaRect获得的最小外接矩形,用纵横比进行判断  
  4. bool verifySizes(RotatedRect mr)  
  5. {  
  6.     float error=0.4;  
  7.     //Spain car plate size: 52x11 aspect 4,7272  
  8.     float aspect=4.7272;  
  9.     //Set a min and max area. All other patchs are discarded  
  10.     int min= 15*aspect*15; // minimum area  
  11.     int max= 125*aspect*125; // maximum area  
  12.     //Get only patchs that match to a respect ratio.  
  13.     float rmin= aspect-aspect*error;  
  14.     float rmax= aspect+aspect*error;  
  15.   
  16.     int area= mr.size.height * mr.size.width;  
  17.     float r= (float)mr.size.width / (float)mr.size.height;  
  18.     if(r<1)  
  19.         r= (float)mr.size.height / (float)mr.size.width;  
  20.   
  21.     if(( area < min || area > max ) || ( r < rmin || r > rmax )){  
  22.         return false;  
  23.     }else{  
  24.         return true;  
  25.     }  
  26. }  
  27.   
  28. Mat histeq(Mat in)  
  29. {  
  30.     Mat out(in.size(), in.type());  
  31.     if(in.channels()==3){  
  32.         Mat hsv;  
  33.         vector<Mat> hsvSplit;  
  34.         cvtColor(in, hsv, CV_BGR2HSV);  
  35.         split(hsv, hsvSplit);  
  36.         equalizeHist(hsvSplit[2], hsvSplit[2]);  
  37.         merge(hsvSplit, hsv);  
  38.         cvtColor(hsv, out, CV_HSV2BGR);  
  39.     }else if(in.channels()==1){  
  40.         equalizeHist(in, out);  
  41.     }  
  42.   
  43.     return out;  
  44. }  
  45.   
  46. vector<Plate> segment(Mat input){  
  47.     vector<Plate> output;  
  48.   
  49.     //apply a Gaussian blur of 5 x 5 and remove noise  
  50.     Mat img_gray;  
  51.     cvtColor(input, img_gray, CV_BGR2GRAY);  
  52.     blur(img_gray, img_gray, Size(5,5));      
  53.   
  54.     //Finde vertical edges. Car plates have high density of vertical lines  
  55.     Mat img_sobel;  
  56.     Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT);//xorder=1,yorder=0,kernelsize=3  
  57.   
  58.     //apply a threshold filter to obtain a binary image through Otsu's method  
  59.     Mat img_threshold;  
  60.     threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);  
  61.   
  62.     //Morphplogic operation close:remove blank spaces and connect all regions that have a high number of edges  
  63.     Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) );  
  64.     morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);  
  65.   
  66.     //Find 轮廓 of possibles plates  
  67.     vector< vector< Point> > contours;  
  68.     findContours(img_threshold,  
  69.         contours, // a vector of contours  
  70.         CV_RETR_EXTERNAL, // 提取外部轮廓  
  71.         CV_CHAIN_APPROX_NONE); // all pixels of each contours  
  72.   
  73.     //Start to iterate to each contour founded  
  74.     vector<vector<Point> >::iterator itc= contours.begin();  
  75.     vector<RotatedRect> rects;  
  76.   
  77.     //Remove patch that are no inside limits of aspect ratio and area.      
  78.     while (itc!=contours.end()) {  
  79.         //Create bounding rect of object  
  80.         RotatedRect mr= minAreaRect(Mat(*itc));  
  81.         if( !verifySizes(mr)){  
  82.             itc= contours.erase(itc);  
  83.         }else{  
  84.             ++itc;  
  85.             rects.push_back(mr);  
  86.         }  
  87.     }  
  88.   
  89.     cv::Mat result;  
  90.     input.copyTo(result);  
  91.   
  92.     for(int i=0; i< rects.size(); i++)  
  93.     {  
  94.         //get the min size between width and height  
  95.         float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height;  
  96.         minSize=minSize-minSize*0.5;  
  97.         //initialize rand and get 5 points around center for floodfill algorithm  
  98.         srand ( time(NULL) );  
  99.         //Initialize floodfill parameters and variables  
  100.         Mat mask;  
  101.         mask.create(input.rows + 2, input.cols + 2, CV_8UC1);  
  102.         mask= Scalar::all(0);  
  103.         int loDiff = 30;  
  104.         int upDiff = 30;  
  105.         int connectivity = 4;  
  106.         int newMaskVal = 255;  
  107.         int NumSeeds = 10;  
  108.         Rect ccomp;  
  109.         int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;  
  110.         for(int j=0; j<NumSeeds; j++){  
  111.             Point seed;  
  112.             seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);  
  113.             seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);  
  114.             int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);  
  115.         }  
  116.   
  117.         //Check new floodfill mask match for a correct patch.  
  118.         //Get all points detected for get Minimal rotated Rect  
  119.         vector<Point> pointsInterest;  
  120.         Mat_<uchar>::iterator itMask= mask.begin<uchar>();  
  121.         Mat_<uchar>::iterator end= mask.end<uchar>();  
  122.         for( ; itMask!=end; ++itMask)  
  123.             if(*itMask==255)  
  124.                 pointsInterest.push_back(itMask.pos());  
  125.   
  126.         RotatedRect minRect = minAreaRect(pointsInterest);  
  127.   
  128.         if(verifySizes(minRect)){  
  129.             // rotated rectangle drawing   
  130.             Point2f rect_points[4]; minRect.points( rect_points );     
  131.   
  132.             //Get rotation matrix  
  133.             float r= (float)minRect.size.width / (float)minRect.size.height;  
  134.             float angle=minRect.angle;      
  135.             if(r<1)  
  136.                 angle=90+angle;  
  137.             Mat rotmat= getRotationMatrix2D(minRect.center, angle,1);  
  138.   
  139.             //Create and rotate image  
  140.             Mat img_rotated;  
  141.             warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);  
  142.   
  143.             //Crop image  
  144.             Size rect_size=minRect.size;  
  145.             if(r < 1)  
  146.                 swap(rect_size.width, rect_size.height);  
  147.             Mat img_crop;  
  148.             getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);  
  149.   
  150.             Mat resultResized;  
  151.             resultResized.create(33,144, CV_8UC3);  
  152.             resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);  
  153.             //Equalize croped image  
  154.             Mat grayResult;  
  155.             cvtColor(resultResized, grayResult, CV_BGR2GRAY);   
  156.             blur(grayResult, grayResult, Size(3,3));  
  157.             grayResult=histeq(grayResult);  
  158.             output.push_back(Plate(grayResult,minRect.boundingRect()));  
  159.         }  
  160.     }  
  161.     return output;  
  162. }  

PlateRecogntion.h:【从车牌图片上识别各个字符】

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #ifndef rec_h  
  2. #define rec_h  
  3. #include <cv.h>  
  4. #include <highgui.h>  
  5. #include <cvaux.h>  
  6. #include <ml.h>  
  7.   
  8. #include <iostream>  
  9. #include <vector>  
  10. #define HORIZONTAL    1  
  11. #define VERTICAL    0  
  12.   
  13. using namespace std;  
  14. using namespace cv;  
  15.   
  16. bool verifySizes(Mat r);  
  17. Mat preprocessChar(Mat in);  
  18. Mat ProjectedHistogram(Mat img, int t);  
  19. Mat features(Mat in, int sizeData);  
  20. int classify(Mat f,CvANN_MLP *ann);  
  21. void train(Mat TrainData, Mat classes,CvANN_MLP *ann,int nlayers);  
  22. #endif  

PlateRecognition.cpp:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "Plate_Recognition.h"  
  2.   
  3. const int numCharacters=30;  
  4.   
  5. bool verifySizes(Mat r){  
  6.     //Char sizes 45x77  
  7.     float aspect=45.0f/77.0f;  
  8.     float charAspect= (float)r.cols/(float)r.rows;  
  9.     float error=0.35;  
  10.     float minHeight=15;  
  11.     float maxHeight=28;  
  12.     //We have a different aspect ratio for number 1, and it can be ~0.2  
  13.     float minAspect=0.2;  
  14.     float maxAspect=aspect+aspect*error;  
  15.     //area of pixels  
  16.     float area=countNonZero(r);  
  17.     //bb area  
  18.     float bbArea=r.cols*r.rows;  
  19.     // of pixel in area  
  20.     float percPixels=area/bbArea;  
  21.   
  22.     if(percPixels < 0.8 && charAspect > minAspect && charAspect < maxAspect && r.rows >= minHeight && r.rows < maxHeight)  
  23.         return true;  
  24.     else  
  25.         return false;  
  26.   
  27. }  
  28.   
  29. Mat preprocessChar(Mat in){  
  30.     //Remap image  
  31.     int h=in.rows;  
  32.     int w=in.cols;  
  33.     int charSize=20;    //统一每个字符的大小  
  34.     Mat transformMat=Mat::eye(2,3,CV_32F);  
  35.     int m=max(w,h);  
  36.     transformMat.at<float>(0,2)=m/2 - w/2;  
  37.     transformMat.at<float>(1,2)=m/2 - h/2;  
  38.   
  39.     Mat warpImage(m,m, in.type());  
  40.     warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0) );  
  41.   
  42.     Mat out;  
  43.     resize(warpImage, out, Size(charSize, charSize) );   
  44.   
  45.     return out;  
  46. }  
  47.   
  48. //create the accumulation histograms,img is a binary image, t is 水平或垂直  
  49. Mat ProjectedHistogram(Mat img, int t)  
  50. {  
  51.     int sz=(t)?img.rows:img.cols;  
  52.     Mat mhist=Mat::zeros(1,sz,CV_32F);  
  53.   
  54.     for(int j=0; j<sz; j++){  
  55.         Mat data=(t)?img.row(j):img.col(j);  
  56.         mhist.at<float>(j)=countNonZero(data);    //统计这一行或一列中,非零元素的个数,并保存到mhist中  
  57.     }  
  58.   
  59.     //Normalize histogram  
  60.     double min, max;  
  61.     minMaxLoc(mhist, &min, &max);  
  62.   
  63.     if(max>0)  
  64.         mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方图中的最大值,归一化直方图  
  65.   
  66.     return mhist;  
  67. }  
  68.   
  69. Mat features(Mat in, int sizeData){  
  70.     //Histogram features  
  71.     Mat vhist=ProjectedHistogram(in,VERTICAL);  
  72.     Mat hhist=ProjectedHistogram(in,HORIZONTAL);  
  73.   
  74.     //Low data feature  
  75.     Mat lowData;  
  76.     resize(in, lowData, Size(sizeData, sizeData) );  
  77.   
  78.     //Last 10 is the number of moments components  
  79.     int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols;  
  80.   
  81.     Mat out=Mat::zeros(1,numCols,CV_32F);  
  82.     //Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量  
  83.     int j=0;  
  84.     for(int i=0; i<vhist.cols; i++)  
  85.     {  
  86.         out.at<float>(j)=vhist.at<float>(i);  
  87.         j++;  
  88.     }  
  89.     for(int i=0; i<hhist.cols; i++)  
  90.     {  
  91.         out.at<float>(j)=hhist.at<float>(i);  
  92.         j++;  
  93.     }  
  94.     for(int x=0; x<lowData.cols; x++)  
  95.     {  
  96.         for(int y=0; y<lowData.rows; y++){  
  97.             out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);  
  98.             j++;  
  99.         }  
  100.     }  
  101.   
  102.     return out;  
  103. }  
  104.   
  105.   
  106. int classify(Mat f,CvANN_MLP *ann){  
  107.     int result=-1;  
  108.     Mat output(1, 30, CV_32FC1); //西班牙车牌只有30种字符  
  109.     (*ann).predict(f, output);  
  110.     Point maxLoc;  
  111.     double maxVal;  
  112.     minMaxLoc(output, 0, &maxVal, 0, &maxLoc);  
  113.     return maxLoc.x;  
  114. }  
  115.   
  116. void train(Mat TrainData, Mat classes,CvANN_MLP *ann,int nlayers){  
  117.     Mat layers(1,3,CV_32SC1);  
  118.     layers.at<int>(0)= TrainData.cols;  
  119.     layers.at<int>(1)= nlayers;  
  120.     layers.at<int>(2)= 30;  
  121.     (*ann).create(layers, CvANN_MLP::SIGMOID_SYM, 1, 1);  
  122.   
  123.     //Prepare trainClases  
  124.     //Create a mat with n trained data by m classes  
  125.     Mat trainClasses;  
  126.     trainClasses.create( TrainData.rows, 30, CV_32FC1 );  
  127.     forint i = 0; i <  trainClasses.rows; i++ )  
  128.     {  
  129.         forint k = 0; k < trainClasses.cols; k++ )  
  130.         {  
  131.             //If class of data i is same than a k class  
  132.             if( k == classes.at<int>(i) )  
  133.                 trainClasses.at<float>(i,k) = 1;  
  134.             else  
  135.                 trainClasses.at<float>(i,k) = 0;  
  136.         }  
  137.     }  
  138.     Mat weights( 1, TrainData.rows, CV_32FC1, Scalar::all(1) );  
  139.   
  140.     //Learn classifier  
  141.     (*ann).train( TrainData, trainClasses, weights );  
  142. }  

然后,编写我们的ImageProc.cpp:【这边我把sdcard的路径都写死了,大家自己调整下】

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include<com_example_carplate_CarPlateDetection.h>  
  2. #include "Plate.h"  
  3. #include "Plate_Segment.h"  
  4. #include "Plate_Recognition.h"  
  5. #include <android/log.h>  
  6. #define LOG_TAG "System.out"  
  7. #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)  
  8. #define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)  
  9. #define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)  
  10.   
  11. /*char* jstring2str(JNIEnv* env, jstring jstr) 
  12. { 
  13.     char*   rtn   =   NULL; 
  14.     jclass   clsstring   =   env->FindClass("java/lang/String"); 
  15.     jstring   strencode   =   env->NewStringUTF("GB2312"); 
  16.     jmethodID   mid   =   env->GetMethodID(clsstring,   "getBytes",   "(Ljava/lang/String;)[B"); 
  17.     jbyteArray   barr=   (jbyteArray)env->CallObjectMethod(jstr,mid,strencode); 
  18.     jsize   alen   =   env->GetArrayLength(barr); 
  19.     jbyte*   ba   =   env->GetByteArrayElements(barr,JNI_FALSE); 
  20.     if(alen   >   0) 
  21.     { 
  22.         rtn   =   (char*)malloc(alen+1); 
  23.         memcpy(rtn,ba,alen); 
  24.         rtn[alen]=0; 
  25.     } 
  26.     env->ReleaseByteArrayElements(barr,ba,0); 
  27.     return  rtn; 
  28. }*/  
  29.   
  30. JNIEXPORT jstring JNICALL Java_com_example_carplate_CarPlateDetection_ImageProc  
  31.   (JNIEnv *env, jclass obj, jintArray buf, jint w, jint h,jstring dir){  
  32.     jint *cbuf;  
  33.     cbuf = env->GetIntArrayElements(buf, false);  
  34.     //char* path = jstring2str(env,dir);  
  35.   
  36.     Size size;  
  37.     size.width = w;  
  38.     size.height = h;  
  39.   
  40.     Mat imageData,input;  
  41.     imageData = Mat(size, CV_8UC4, (unsigned char*)cbuf);  
  42.     input = Mat(size, CV_8UC3);  
  43.     cvtColor(imageData,input,CV_BGRA2BGR);  
  44.   
  45.     vector<Plate> posible_regions = segment(input);  
  46.   
  47.     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'};  
  48.     CvANN_MLP ann;  
  49.     //SVM for each plate region to get valid car plates,Read file storage.  
  50.     FileStorage fs;  
  51.     //strcat(path,"/SVM.xml");  
  52.     fs.open("/storage/sdcard/SVM.xml", FileStorage::READ);  
  53.     Mat SVM_TrainingData;  
  54.     Mat SVM_Classes;  
  55.     fs["TrainingData"] >> SVM_TrainingData;  
  56.     fs["classes"] >> SVM_Classes;  
  57.     if(fs.isOpened())  
  58.         LOGD("read success!");  
  59.   
  60.     //Set SVM params  
  61.     LOGD("size:%d",SVM_TrainingData.rows);  
  62.     SVM_TrainingData.convertTo(SVM_TrainingData, CV_32FC1);  
  63.     SVM_Classes.convertTo(SVM_Classes, CV_32FC1);  
  64.     CvSVMParams SVM_params;  
  65.     SVM_params.svm_type = CvSVM::C_SVC;  
  66.     SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR;  
  67.     SVM_params.degree = 0;  
  68.     SVM_params.gamma = 1;  
  69.     SVM_params.coef0 = 0;  
  70.     SVM_params.C = 1;  
  71.     SVM_params.nu = 0;  
  72.     SVM_params.p = 0;  
  73.     SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);  
  74.     LOGD("Everything is ready");  
  75.     //Train SVM  
  76.     LOGD("START TO ENTER SVM PREDICT");  
  77.     CvSVM svmClassifier(SVM_TrainingData, SVM_Classes, Mat(), Mat(), SVM_params);  
  78.     //For each possible plate, classify with svm if it's a plate or no  
  79.     vector<Plate> plates;  
  80.     for(int i=0; i< posible_regions.size(); i++)  
  81.     {  
  82.         Mat img=posible_regions[i].plateImg;  
  83.         Mat p= img.reshape(1, 1);  
  84.         p.convertTo(p, CV_32FC1);  
  85.   
  86.         int response = (int)svmClassifier.predict( p );  
  87.         if(response==1)  
  88.             plates.push_back(posible_regions[i]);  
  89.     }  
  90.     LOGD("SVM PREDICT FINISH");  
  91.     fs.release();  
  92.     //Read file storage.  
  93.     FileStorage fs2;  
  94.     fs2.open("/storage/sdcard/OCR.xml", FileStorage::READ);  
  95.     Mat TrainingData;  
  96.     Mat Classes;  
  97.     fs2["TrainingDataF15"] >> TrainingData;  
  98.     fs2["classes"] >> Classes;  
  99.     LOGD("size:%d",TrainingData.rows);  
  100.     LOGD("START TO TRAIN MLP");  
  101.     //训练神经网络  
  102.     train(TrainingData, Classes,&ann,10);  
  103.     LOGD("FINISH TRAIN MLP");  
  104.     Mat inputs=plates[0].plateImg;  
  105.     Plate mplate;  
  106.     //dealing image and save each character image into vector<CharSegment>  
  107.     //Threshold input image  
  108.     Mat img_threshold;  
  109.     threshold(inputs, img_threshold, 60, 255, CV_THRESH_BINARY_INV);  
  110.   
  111.     Mat img_contours;  
  112.     img_threshold.copyTo(img_contours);  
  113.     //Find contours of possibles characters  
  114.     vector< vector< Point> > contours;  
  115.     findContours(img_contours,  
  116.         contours, // a vector of contours  
  117.         CV_RETR_EXTERNAL, // retrieve the external contours  
  118.         CV_CHAIN_APPROX_NONE); // all pixels of each contours  
  119.     //Start to iterate to each contour founded  
  120.     vector<vector<Point> >::iterator itc= contours.begin();  
  121.     LOGD("Before extracting hist and low-resolution image");  
  122.     //Remove patch that are no inside limits of aspect ratio and area.  
  123.     while (itc!=contours.end()) {  
  124.   
  125.         //Create bounding rect of object  
  126.         Rect mr= boundingRect(Mat(*itc));  
  127.         //Crop image  
  128.         Mat auxRoi(img_threshold, mr);  
  129.         if(verifySizes(auxRoi)){  
  130.             auxRoi=preprocessChar(auxRoi);  
  131.             LOGD("FINISH extracting features");  
  132.             //对每一个小方块,提取直方图特征  
  133.             Mat f=features(auxRoi,15);  
  134.             //For each segment feature Classify  
  135.             LOGD("START TO CLASSIFY IN MLP");  
  136.             int character=classify(f,&ann);  
  137.             mplate.chars.push_back(strCharacters[character]);  
  138.             LOGD("FINISH CLASSIFY");  
  139.             mplate.charsPos.push_back(mr);  
  140.             //printf("%c ",strCharacters[character]);  
  141.         }  
  142.         ++itc;  
  143.     }  
  144.     fs2.release();  
  145.     string licensePlate=mplate.str();  
  146.     //const char *result;  
  147.     //result=licensePlate.c_str();  
  148.     env->ReleaseIntArrayElements(buf, cbuf, 0);  
  149.   
  150.     return env->NewStringUTF(licensePlate.c_str());  
  151. }  

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开发 交叉编译 神经网络
猜你在找
图像开运算+闭运算+腐蚀+膨胀
【机器学习系列】logistic回归python实现
西安中软国际机试题
学习OpenCV:滤镜系列(3)——颜色变幻
比微软kinect更强的视频跟踪算法--TLD跟踪算法介绍 .
OpenCv的连通域操作
Android NDK开发篇(一):新版NDK环境搭建(免Cygwin,超级快)
boost swap
在Objective-C中 NSString并不受引用计数器机制管理
支持NFC的Android手机型号
id="ad_frm_0" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=4&containerId=ad_cen&frmId=ad_frm_0" style="border-width: 0px; overflow: hidden; width: 746px; height: 90px; ">
查看评论
13楼  learningpro0108 2014-10-06 11:45发表 [回复]
建议增加判断 posible_regions.size()是否是0 以及response是否是1,不然程序容易crash,我已经修改好,并且使用摄像头实时处理
12楼  learningpro0108 2014-10-04 10:20发表 [回复]
运行的时候出现了这个错误,用的是opencv4android2.4.9,是opencv的bug么?
OpenCV Error: One of arguments' values is out of range (there should be at least one input and one output and every hidden layer must have more than 1 neuron) in CvANN_MLP::create, file /home/reports/ci/slave_desktop/50-SDK/opencv/modules/ml/src/ann_mlp.cpp, line 244
11楼  HeyTom 2014-09-24 11:34发表 [回复]
楼主您好,我运行您的代码,在读取ocr文件,完成训练(输出FINISH TRAIN MLP)之后程序就死掉了 
错误提示:
Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1), thread 22543 (xample.carplate)

网上有人说这是jin的问题或者是线程问题,说和4.0以上系统有关系,请问大家有遇到过这个问题么?该怎么解决呢?
10楼  siqisiyongsi 2014-09-10 00:33发表 [回复]
楼主能不能把身份证识别的源码发给我
9楼  siqisiyongsi 2014-09-10 00:31发表 [回复]
楼主能不能把android身份证识别的源码发给我,我想借鉴一下。542297333@qq.com
8楼  rongpantian 2014-08-22 16:54发表 [回复]
楼主,你真是帮了大忙,好好的研究下您的代码,能把身份证的源码共享吗?
7楼  wusheng304 2014-05-17 13:53发表 [回复]
楼主您好.请问字符识别.你是用svm做的吗?有这方面学习资料吗?给个建议?
6楼  小义爱小谟 2014-05-10 20:22发表 [回复]
楼主,一切都准备了就绪,但是运行软件之后,直接就停止了
5楼  xiaofengchuilei 2014-05-07 16:20发表 [回复]
楼主请问,国内车牌的话,直接运行您之前的两篇文章中给出的程序就能得到相应的国内车牌的svm.xml和ocr.xml文件?并且也可以直接识别国内车牌了是吗?
4楼  wusheng304 2014-03-31 15:18发表 [回复]
楼 主,你好。我下载你到手机运行不了。“package not found" openCv Manager package was not found!Try to installit?怎样才能加载运行。???
Re:  jourella 2014-04-30 14:57发表 [回复]
回复wusheng304:要在手机端装opencv manager哈,在谷歌应用里就有
3楼  shangfeng88 2014-02-20 09:38发表 [回复]
楼主你好,我运行你的代码提示下面的错误,可以指导一下吗?
1. E/OpenCV_for_Tegra(1970): Tegra Version detected: 0
2. E/cv::error()(1970): OpenCV Error: Bad argument (train data must be floating-point matrix) in cvCheckTrainData, file /home/reports/ci/slave_desktop/50-SDK/opencv/modules/ml/src/inner_functions.cpp, line 857
3. Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1)
Re:  jourella 2014-05-01 14:09发表 [回复]
回复shangfeng88:最后一个问题的原因是图片太大了,把图片改成600乘450的在我的nexus上就可以用了。
Re:  jourella 2014-05-01 13:55发表 [回复]
回复shangfeng88:第一个问题也解决了,是opencv manager不配套的问题,配套的manager在下载下来的文件包里,比如下载opencv 2.4.7版本,里面有四个文件夹,apk文件夹里面选择正确的manager,而不要用谷歌play的。
Re:  jourella 2014-04-30 15:01发表 [回复]
回复shangfeng88:我只解决了第二个问题,楼主写死的路径要修改。注意sdcard要改成sdcard0。
比如我使用nexus 7,把xml文件放在根目录新建的文件夹detect文件夹里面。
路径写成/storage/sdcard0/detect/OCR.xml
2楼  大柳 2014-01-25 12:33发表 [回复]
楼主请问:我 把你的源码放在Mac下冲洗NDK 之后,不能运行啊!发现vector<Plate> posible_regions = segment(input); posible_regions.size()等于0,这是为什么?
Re:  taotao1233 2014-01-25 19:39发表 [回复]
回复zhuozhoumatou:1.额,这些代码不能处理中国车牌,只能搞西班牙的车牌。
2.确保你的图片在ndk那边能否正确读入
3.建议在vs2008端把代码能运行起来了,再搞mac上
4.我也是学习<matering opencv....>的笔记,有想不通的,你可以看原书
Re:  大柳 2014-02-06 22:07发表 [回复]
回复jinshengtao:谢谢楼主~ 我自己再查查
1楼  大柳 2014-01-21 22:35发表 [回复] [引用] [举报]
楼主 能请教下问题吗? 我想移植 OpenCVC 到android 上 拍照获取身份证信息,不知道能否指教下~
Re:  Sword-Master 2014-07-04 16:32发表 [回复] [引用] [举报]
回复zhuozhoumatou:这个很简单 身份证识别 我这有源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值