首先说明所有车牌识别算法,我都是参考其他人的文章加上自己的一点点理解,希望通过这些文章让自己对车牌的原理、算法,Android版本实现、Android Studio的使用,Opencv一些变量、函数和调用库等有一些了解。因为目前车牌识别的整体算法尚未完全搞明白,所以先说说自己的一些研究后面在整体总结。【EasyPR,特意感谢其作者和其他贡献者。】
一)Android Studio2.2.2上加载Opencv库开发NDK。
第一步:新建工程app目录下build.gradle文件中添加下面的代码
- sourceSets {
- main {
- jniLibs.srcDirs=['/home/gzh/AndroidStudioProjects/MyPlateLocate/app/src/main/opencv310_libs']
- }
- }
复制代码
第二步:新建工程app目录下CMakeLists.txt文件中添加下面的代码,注意路径要修改为自己使用的路径,也要创建对应的目录和文件。
- # Sets the minimum version of CMake required to build the native
- # library. You should either keep the default value or only pass a
- # value of 3.4.0 or lower.
- cmake_minimum_required(VERSION 3.4.1)
- #
- set(pathToProject ~/AndroidStudioProjects/MyPlateLocate)
- #OpenCV-android-sdk
- set(pathToOpenCv ~/linux-tools/OpenCV-android-sdk)
- #支持-std=gnu++11
- set(CMAKE_VERBOSE_MAKEFILE on)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
- include_directories(${pathToOpenCv}/sdk/native/jni/include)
- include_directories(${pathToProject}//app/src/main/cpp/platelocate/include)
- # Creates and names a library, sets it as either STATIC
- # or SHARED, and provides the relative paths to its source code.
- # You can define multiple libraries, and CMake builds it for you.
- # Gradle automatically packages shared libraries with your APK.
- add_library( # Sets the name of the library.
- native-lib
- #lib_opencv
- # Sets the library as a shared library.
- SHARED
- # Provides a relative path to your source file(s).
- # Associated headers in the same location as their source
- # file are automatically included.
- src/main/cpp/platelocate/src/platelocate.cpp
- src/main/cpp/native-lib.cpp
- )
- add_library( lib_opencv SHARED IMPORTED )
- #引入libopencv_java3.so
- set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${pathToProject}/app/src/main/opencv310_libs/${ANDROID_ABI}/libopencv_java3.so)
- # Searches for a specified prebuilt library and stores the path as a
- # variable. Because system libraries are included in the search path by
- # default, you only need to specify the name of the public NDK library
- # you want to add. CMake verifies that the library exists before
- # completing its build.
- find_library( # Sets the name of the path variable.
- log-lib
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log )
- # Specifies libraries CMake should link to your target library. You
- # can link multiple libraries, such as libraries you define in the
- # build script, prebuilt third-party libraries, or system libraries.
- target_link_libraries( # Specifies the target library.
- native-lib
- # Links the target library to the log library
- # included in the NDK.
- ${log-lib}
- lib_opencv
- )
复制代码
第三步:添加合适的代码,可以参考下面的灰度图转换实例,不过必须符合自己的实际情况。
二)Opencv Mat类型如何在Java和NDK之间传递。
1)Mat在Java中定义,传递给NDK。
Mat rgbMat = new Mat();
myCvtColor(rgbMat.getNativeObjAddr(), grayMat.getNativeObjAddr(), COLOR_RGB2GRAY);
2)Mat在Java中获取,传递给Java。
Mat& mGr = *(Mat*)imageGray;
Mat& mRgb = *(Mat*)faces;
3)在NDK中创建,然后作为函数返回值返回 ************方法未经验证
Mat *hist = new Mat();
// ...
return (jlong) hist;
此时Java中也要做相应转换才可以使用
long address = generateHistogram(bitmapMat.nativeObj);
Mat histogram = new Mat(address);************方法未经验证
三)灰度图转换实例
3)PlateRecognizer.java代码实现
- package com.example.gzh.myplatelocate;
- import android.content.Context;
- import android.util.Log;
- import org.opencv.core.Mat;
- /**
- * Created by gzh on 17-2-12.
- */
- public class PlateRecognizer {
- // Used to load the 'native-lib' library on application startup.
- static String TAG = "PlateRecognizer";
- static {
- System.loadLibrary("native-lib");
- // System.loadLibrary("opencv_java3");
- }
- private Context mContext;
- private String mSvmpath = "svm.xml";
- private String mAnnpath = "ann.xml";
- private boolean mRecognizerInited = false;
- private long mRecognizerPtr = 0;
- public PlateRecognizer(Context context) {
- mContext = context;
- mRecognizerPtr = initPR();
- if (0 != mRecognizerPtr) {
- mRecognizerInited = true;
- }
- }
- public void finalize() {
- uninitPR(mRecognizerPtr);
- mRecognizerPtr = 0;
- mRecognizerInited = false;
- }
- public void my_SobelOper(Mat rgbMat, Mat grayMat, int COLOR_RGB2GRAY) {
- Log.i(TAG, "my_SobelOper=1=");
- mySobelOper(mRecognizerPtr, rgbMat.getNativeObjAddr(), grayMat.getNativeObjAddr());//rgbMat to gray grayMat
- Log.i(TAG, "my_SobelOper=2=");
- }
- public void my_CvtColor(Mat rgbMat, Mat grayMat, int COLOR_RGB2GRAY) {
- Log.i(TAG, "my_CvtColor=1=");
- myCvtColor(rgbMat.getNativeObjAddr(), grayMat.getNativeObjAddr(), COLOR_RGB2GRAY);//rgbMat to gray grayMat
- Log.i(TAG, "my_CvtColor=2=");
- }
- /**
- * A native method that is implemented by the 'native-lib' native library,
- * which is packaged with this application.
- */
- public static native String stringFromJNI();
- //public static native String validate(long matAddrGr, long matAddrRgba);
- //图像处理
- private static native void myCvtColor(long thiz, long inputImage, long faces);
- public static native int[] getGrayImage(int[] pixels, int w, int h);
- public static native long initPR();
- public static native long uninitPR(long recognizerPtr);
- public static native void mySobelOper(long recognizerPtr, long inputImage, long faces);
- }
复制代码
4)PlateRecognizer.java函数调用代码
- public void procSrc2Gray(){
- int nFalg_test = 2;
- Mat rgbMat = new Mat();
- Mat grayMat = new Mat();
- srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heidfk640);
- grayBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.RGB_565);
- Utils.bitmapToMat(srcBitmap, rgbMat);//convert original bitmap to Mat, R G B.
- if(nFalg_test == 1)
- {
- String str1 = null;
- str1 = String.format("call my_CvtColor rgbMat.channels()=%d", rgbMat.channels());
- Log.i(TAG, str1);
- mPlateRecognizer.my_CvtColor(rgbMat, grayMat, COLOR_RGB2GRAY);//rgbMat to gray grayMat
- // Then convert the processed Mat to Bitmap
- Log.i(TAG, "call my_CvtColor sucess...=2=");
- }
- else {
- String str1 = null;
- str1 = String.format("call my_SobelOper rgbMat.channels()=%d", rgbMat.channels());
- Log.i(TAG, str1);
- mPlateRecognizer.my_SobelOper(rgbMat, grayMat, COLOR_RGB2GRAY);//rgbMat to gray grayMat
- // Then convert the processed Mat to Bitmap
- Log.i(TAG, "call my_SobelOper sucess...=2=");
- }
- Utils.matToBitmap(grayMat, grayBitmap);
- Log.i(TAG, "procSrc2Gray sucess...=3=");
- show_image.setImageBitmap(grayBitmap);
- Log.i(TAG, "procSrc2Gray sucess...");
- }
复制代码
5)mySobelOper函数NDK调用
- extern "C"
- JNIEXPORT void JNICALL
- Java_com_example_gzh_myplatelocate_PlateRecognizer_mySobelOper
- (JNIEnv * jenv,
- jlong thiz,
- jlong recognizerPtr,
- jlong imageGray, jlong faces)
- {
- Mat imgData_out;
- Mat& mGr = *(Mat*)imageGray;
- Mat& mRgb = *(Mat*)faces;
- LOGE("=1= Java_com_example_gzh_myplatelocate_PlateRecognizer_mySobelOper");
- LOGE("=2= mGr.cols=%d", mGr.cols);
- LOGE("=2= mGr.rows=%d", mGr.rows);
- LOGE("=2= mGr.dims=%d", mGr.dims);
- LOGE("=2= mGr.channels=%d", mGr.channels());
- // cv:cvtColor(mGr, mRgb, CV_RGB2GRAY);
- //mRgb = mRgb /255.0;
- CPlateLocate *pr = (CPlateLocate *)recognizerPtr;
- #if 1
- <b> pr->sobelOper(mGr, imgData_out, m_GaussianBlurSize, m_MorphSizeWidth,m_MorphSizeHeight);//注意此时会有type==CV_8UC1的错误,尚未解决。</b>
- #else
- #if 1
- pr->cvtgray(mGr, mRgb, CV_RGB2GRAY);
- #else
- cv:cvtColor(mGr, mRgb, CV_RGB2GRAY);//运行ok
- #endif
- #endif
- LOGE("=2= imgData_out.cols=%d", imgData_out.cols);
- LOGE("=2= imgData_out.rows=%d", imgData_out.rows);
- imgData_out.dims = 0;
- LOGE("=2= imgData_out.dims=%d", imgData_out.dims);
- LOGE("=2= imgData_out.channels=%d", imgData_out.channels());
- // imgData_out = imgData_out / 255.0;
- // imgData_out.copyTo(*hist);
- LOGE("=3= imgData_outJava_com_example_gzh_myplatelocate_PlateRecognizer_mySobelOper");
- }
复制代码
6)mySobelOper函数C++代码代码
- int CPlateLocate::sobelOper(const Mat &in, Mat &out, int blurSize, int morphW,int morphH)
- {
- Mat mat_blur;
- LOGE("=1= sobelOper");
- mat_blur = in.clone();
- GaussianBlur(in, mat_blur, Size(blurSize, blurSize), 0, 0, BORDER_DEFAULT);
- LOGE("=2= sobelOper");
- Mat mat_gray;
- if (mat_blur.channels() == 3)
- cvtColor(mat_blur, mat_gray, CV_RGB2GRAY);
- else
- mat_gray = mat_blur;
- LOGE("=3= sobelOper");
- int scale = SOBEL_SCALE;
- int delta = SOBEL_DELTA;
- int ddepth = SOBEL_DDEPTH;
- Mat grad_x, grad_y;
- Mat abs_grad_x, abs_grad_y;
- LOGE("=4= sobelOper");
- Sobel(mat_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
- convertScaleAbs(grad_x, abs_grad_x);
- LOGE("=5= sobelOper");
- Mat grad;
- addWeighted(abs_grad_x, SOBEL_X_WEIGHT, 0, 0, 0, grad);
- Mat mat_threshold;
- double otsu_thresh_val =
- threshold(grad, mat_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
- LOGE("=6= sobelOper");
- #if 0
- out = mat_threshold;
- #else
- Mat element = getStructuringElement(MORPH_RECT, Size(morphW, morphH));
- morphologyEx(mat_threshold, mat_threshold, MORPH_CLOSE, element);
- out = mat_threshold;
- #endif
- LOGE("=71= sobelOper");
- return 0;
- }
复制代码
四)Opencv310帮助文件使用方法 http://docs.opencv.org/3.1.0/index.html是opencv310帮助的主页,可以在搜索框中搜索所需关键字,会详细的说明,甚至示例代码。
五)总结
1.上面的实例中为了学习Opencv特意将图片数据通过Mat这样的数据结构传递,其实最好是将文件句柄传递给NDK处理,只返回车牌识别后的信息,这样我们就可以把注意力专注到车牌识别的算法和代码实现上。
2.PlateRecognizer.java文件与MainActivity.java文件放置到一起,可以将PlateRecognizer.java单独编译成一个Java库,而在其他Java文件中调用。
3.下面可以考虑把USB Camera预览的数据传递给NDK部分,将处理过的图像传递回来并显示。
Working