本文旨在帮助Java开发人员构建桌面和服务器端Java应用程序,以在护照,旅行文档和ID卡中检测机器可读区域( MRZ )。您将看到如何将Dynamsoft C++ OCR SDK封装到Java Jar软件包中,以及如何快速创建使用几行Java代码的命令行MRZ检测器。
Java的类和MRZ检测方法
创建NativeLabelRecognizer.java
,NativeLoader.java
,MrzResult.java
和MrzParser.java
。
-
NativeLabelRecognizer.java
是本机库的包装类。它加载本机库并调用本机方法。NativeLabelRecognizer
中定义的主要本机方法如下:
public NativeLabelRecognizer() { nativePtr = nativeCreateInstance(); } public void destroyInstance() { if (nativePtr != 0) nativeDestroyInstance(nativePtr); } public static int setLicense(String license) { return nativeInitLicense(license); } public ArrayList<MrzResult> detectFile(String fileName) { return nativeDetectFile(nativePtr, fileName); } public String getVersion() { return nativeGetVersion(); } public int loadModel() throws IOException { ... return nativeLoadModel(nativePtr, targetPath); } private native static int nativeInitLicense(String license); private native long nativeCreateInstance(); private native void nativeDestroyInstance(long nativePtr); private native ArrayList<MrzResult> nativeDetectFile(long nativePtr, String fileName); private native int nativeLoadModel(long nativePtr, String modelPath);
loadModel()
方法很特别。它需要动态更新模型路径
根据JON形式的模板文件在JAR包装的提取路径中指定。 Gson可用于加载和更新JSON对象。
public int loadModel() throws IOException { String modeFile = "MRZ.json"; String tempFolder = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath(); String targetPath = new File(tempFolder, modeFile).getAbsolutePath(); // Modify the model path based on your own environment FileReader reader = new FileReader(targetPath); char[] chars = new char[1024]; int len = 0; StringBuilder sb = new StringBuilder(); while ((len = reader.read(chars)) != -1) { sb.append(new String(chars, 0, len)); } String template = sb.toString(); if (reader != null) { reader.close(); } Gson gson = new Gson(); JsonObject jsonObject = gson.fromJson(template, JsonObject.class); JsonArray array = jsonObject.get("CharacterModelArray").getAsJsonArray(); JsonObject object = array.get(0).getAsJsonObject(); String modelPath = object.get("DirectoryPath").getAsString(); if (modelPath != null && modelPath.contains("model")) { object.addProperty("DirectoryPath", tempFolder); } FileWriter writer = new FileWriter(targetPath); writer.write(jsonObject.toString()); writer.flush(); writer.close(); return nativeLoadModel(nativePtr, targetPath); }
-
NativeLoader.java
是一个实用程序类,可从JAR软件包中提取MRZ OCR模型文件和C ++共享库文件,并加载本机库。所有资产将提取到用户操作系统的临时目录。 MD5校验和用于比较文件更改。
private static boolean extractResourceFiles(String dlrNativeLibraryPath, String dlrNativeLibraryName, String tempFolder) throws IOException { String[] filenames = null; if (Utils.isWindows()) { filenames = new String[] {"DynamsoftLicenseClientx64.dll", "vcomp140.dll", "DynamicPdfx64.dll", "DynamsoftLabelRecognizerx64.dll", "dlr.dll"}; } else if (Utils.isLinux()) { filenames = new String[] {"libDynamicPdf.so", "libDynamsoftLicenseClient.so", "libDynamsoftLabelRecognizer.so", "libdlr.so"}; } boolean ret = true; for (String file : filenames) { ret &= extractAndLoadLibraryFile(dlrNativeLibraryPath, file, tempFolder); } // Extract model files String modelPath = "/model"; filenames = new String[] {"MRZ.json", "MRZ.caffemodel", "MRZ.txt", "MRZ.prototxt"}; for (String file : filenames) { ret &= extractAndLoadLibraryFile(modelPath, file, tempFolder); } return ret; } static String md5sum(InputStream input) throws IOException { BufferedInputStream in = new BufferedInputStream(input); try { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); DigestInputStream digestInputStream = new DigestInputStream(in, digest); for (; digestInputStream.read() >= 0;) { } ByteArrayOutputStream md5out = new ByteArrayOutputStream(); md5out.write(digest.digest()); return md5out.toString(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("MD5 algorithm is not available: " + e); } finally { in.close(); } }
-
MrzResult.java
是java类,用于存储MRZ检测结果,包括检测信心,文本和坐标。
public class MrzResult { public int confidence; public String text; public int x1, y1, x2, y2, x3, y3, x4, y4; public MrzResult(int confidence, String text, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { this.confidence = confidence; this.text = text; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.x3 = x3; this.y3 = y3; this.x4 = x4; this.y4 = y4; } }
-
MrzParser.java
是解析MRZ检测结果并解码MRZ信息的Java类。 MRZ信息包括存储在com.google.gson.JsonObject
对象中的文档类型,发行国家,文件编号,出生日期和到期日期。
JsonObject mrzInfo = new JsonObject(); ... // Get issuing State infomation String nation = line1.substring(2, 7); pattern = Pattern.compile("[0-9]"); matcher = pattern.matcher(nation); if (matcher.matches()) return null; if (nation.charAt(nation.length() - 1) == '<') { nation = nation.substring(0, 2); } mrzInfo.addProperty("nationality", nation); // Get surname information line1 = line1.substring(5); int pos = line1.indexOf("<<"); String surName = line1.substring(0, pos); pattern = Pattern.compile("[0-9]"); matcher = pattern.matcher(surName); if (matcher.matches()) return null; surName = surName.replace("<", " "); mrzInfo.addProperty("surname", surName); // Get givenname information String givenName = line1.substring(surName.length() + 2); pattern = Pattern.compile("[0-9]"); matcher = pattern.matcher(givenName); if (matcher.matches()) return null; givenName = givenName.replace("<", " "); givenName = givenName.trim(); mrzInfo.addProperty("givenname", givenName); // Get passport number information String passportNumber = ""; passportNumber = line2.substring(0, 9); passportNumber = passportNumber.replace("<", " "); mrzInfo.addProperty("passportnumber", passportNumber); ...
当Java类完成后,我们可以通过运行:
自动生成JNI标头文件
cd src/main/java
javah -o ../../../jni/NativeLabelRecognizer.h com.dynamsoft.dlr.NativeLabelRecognizer
为Dynamoft C ++ OCR SDK编写JNI包装器
我们创建了一个CMAKE项目,以构建具有DynamSoft标签识别器SDK的JNI包装器。
这是CMakeLists.txt
文件:
cmake_minimum_required (VERSION 2.6)
project (dlr)
MESSAGE( STATUS "PROJECT_NAME: " ${PROJECT_NAME} )
find_package(JNI REQUIRED)
include_directories(${JNI_INCLUDE_DIRS})
MESSAGE( STATUS "JAVA_INCLUDE: " ${JAVA_INCLUDE})
# Check lib
if (CMAKE_HOST_WIN32)
set(WINDOWS 1)
elseif(CMAKE_HOST_APPLE)
set(MACOS 1)
elseif(CMAKE_HOST_UNIX)
set(LINUX 1)
endif()
# Set RPATH
if(CMAKE_HOST_UNIX)
SET(CMAKE_CXX_FLAGS "-std=c++11 -O3 -Wl,-rpath=$ORIGIN")
SET(CMAKE_INSTALL_RPATH "$ORIGIN")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
endif()
# Add search path for include and lib files
if(WINDOWS)
link_directories("${PROJECT_SOURCE_DIR}/lib/win/" ${JNI_LIBRARIES})
elseif(LINUX)
link_directories("${PROJECT_SOURCE_DIR}/lib/linux/" ${JNI_LIBRARIES})
endif()
include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/include/")
# Add the library
add_library(dlr SHARED NativeLabelRecognizer.cxx)
if(WINDOWS)
target_link_libraries (${PROJECT_NAME} "DynamsoftLabelRecognizerx64")
else()
target_link_libraries (${PROJECT_NAME} "DynamsoftLabelRecognizer" pthread)
endif()
# Set installation directory
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../src/main/")
set(LIBRARY_PATH "java/com/dynamsoft/dlr/native")
if(WINDOWS)
install (DIRECTORY "${PROJECT_SOURCE_DIR}/lib/win/" DESTINATION "${CMAKE_INSTALL_PREFIX}${LIBRARY_PATH}/win")
install (TARGETS dlr DESTINATION "${CMAKE_INSTALL_PREFIX}${LIBRARY_PATH}/win")
elseif(LINUX)
install (DIRECTORY "${PROJECT_SOURCE_DIR}/lib/linux/" DESTINATION "${CMAKE_INSTALL_PREFIX}${LIBRARY_PATH}/linux")
install (TARGETS dlr DESTINATION "${CMAKE_INSTALL_PREFIX}${LIBRARY_PATH}/linux")
endif()
这是一个共享库项目。 dlr
库是由NativeLabelRecognizer.cxx
文件构建的。构建后,所有共享库将安装到src/main/java/com/dynamsoft/dlr/native
目录:
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
JNI方法在NativeLabelRecognizer.cxx
文件中实现:
-
初始化许可证:
JNIEXPORT jint JNICALL Java_com_dynamsoft_dlr_NativeLabelRecognizer_nativeInitLicense(JNIEnv *env, jclass, jstring license) { const char *pszLicense = env->GetStringUTFChars(license, NULL); char errorMsgBuffer[512]; // Click https://www.dynamsoft.com/customer/license/trialLicense/?product=dlr to get a trial license. int ret = DLR_InitLicense(pszLicense, errorMsgBuffer, 512); printf("DLR_InitLicense: %s\n", errorMsgBuffer); env->ReleaseStringUTFChars(license, pszLicense); return ret; }
-
创建DynamSoft标签识别器的实例:
JNIEXPORT jlong JNICALL Java_com_dynamsoft_dlr_NativeLabelRecognizer_nativeCreateInstance(JNIEnv *, jobject) { return (jlong)DLR_CreateInstance(); }
-
破坏Dynamsoft标签识别器的实例:
JNIEXPORT void JNICALL Java_com_dynamsoft_dlr_NativeLabelRecognizer_nativeDestroyInstance(JNIEnv *, jobject, jlong handler) { if (handler) { DLR_DestroyInstance((void *)handler); } }
-
加载模型文件:
JNIEXPORT jint JNICALL Java_com_dynamsoft_dlr_NativeLabelRecognizer_nativeLoadModel(JNIEnv *env, jobject, jlong handler, jstring filename) { const char *pFileName = env->GetStringUTFChars(filename, NULL); char errorMsgBuffer[512]; int ret = DLR_AppendSettingsFromFile((void*)handler, pFileName, errorMsgBuffer, 512); printf("Load MRZ model: %s\n", errorMsgBuffer); env->ReleaseStringUTFChars(filename, pFileName); return ret; }
-
从图像文件中检测MRZ并返回MRZ结果列表:
JNIEXPORT jobject JNICALL Java_com_dynamsoft_dlr_NativeLabelRecognizer_nativeDetectFile(JNIEnv *env, jobject, jlong handler, jstring filename) { jobject arrayList = NULL; jclass mrzResultClass = env->FindClass("com/dynamsoft/dlr/MrzResult"); if (NULL == mrzResultClass) printf("FindClass failed\n"); jmethodID mrzResultConstructor = env->GetMethodID(mrzResultClass, "<init>", "(ILjava/lang/String;IIIIIIII)V"); if (NULL == mrzResultConstructor) printf("GetMethodID failed\n"); jclass arrayListClass = env->FindClass("java/util/ArrayList"); if (NULL == arrayListClass) printf("FindClass failed\n"); jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", "()V"); if (NULL == arrayListConstructor) printf("GetMethodID failed\n"); jmethodID arrayListAdd = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); if (NULL == arrayListAdd) printf("GetMethodID failed\n"); const char *pFileName = env->GetStringUTFChars(filename, NULL); int ret = DLR_RecognizeByFile((void *)handler, pFileName, "locr"); if (ret) { printf("Detection error: %s\n", DLR_GetErrorString(ret)); } DLR_ResultArray *pResults = NULL; DLR_GetAllResults((void *)handler, &pResults); if (!pResults) { return NULL; } int count = pResults->resultsCount; arrayList = env->NewObject(arrayListClass, arrayListConstructor); for (int i = 0; i < count; i++) { DLR_Result *mrzResult = pResults->results[i]; int lCount = mrzResult->lineResultsCount; for (int j = 0; j < lCount; j++) { DM_Point *points = mrzResult->lineResults[j]->location.points; int x1 = points[0].x; int y1 = points[0].y; int x2 = points[1].x; int y2 = points[1].y; int x3 = points[2].x; int y3 = points[2].y; int x4 = points[3].x; int y4 = points[3].y; jobject object = env->NewObject(mrzResultClass, mrzResultConstructor, mrzResult->lineResults[j]->confidence, env->NewStringUTF(mrzResult->lineResults[j]->text), x1, y1, x2, y2, x3, y3, x4, y4); env->CallBooleanMethod(arrayList, arrayListAdd, object); } } // Release memory DLR_FreeResults(&pResults); env->ReleaseStringUTFChars(filename, pFileName); return arrayList; }
用资源和依赖项构建Java Jar包装
目标软件包应包括Java类,C ++库文件,模型文件和依赖项。默认情况下,Maven仅包括Java类。要包括C ++库文件,模型文件和依赖项,我们需要将以下配置添加到pom.xml
文件:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.md</exclude>
<exclude>**/*.h</exclude>
<exclude>**/*.lib</exclude>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>res</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
-
src/main/java
是包含本地库文件的目录,该目录是在构建JNI包装器后安装的。 -
res
是包含模型文件的目录。它的结构如下:
res │ └───model ├───MRZ.caffemodel ├───MRZ.json ├───MRZ.prototxt └───MRZ.txt
-
maven-assembly-plugin
用于将依赖项构建到目标软件包中以容易部署。
最后,运行mvn install assembly:assembly
命令生成dlr-1.0.0-jar-with-dependencies.jar
文件。
在Java建造MRZ检测器的步骤
现在,让我们创建一个带有几行代码的Java MRZ检测器。
-
获取Dynamsoft标签识别器的30-day FREE trial license,并在Java代码中激活许可证。
NativeLabelRecognizer.setLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==");
-
创建一个
NativeLabelRecognizer
实例。
NativeLabelRecognizer labelRecognizer = new NativeLabelRecognizer();
-
加载MRZ检测模型:
labelRecognizer.loadModel();
-
从图像文件中检测MRZ:
ArrayList<MrzResult> results = (ArrayList<MrzResult>)labelRecognizer.detectFile(fileName);
-
通过解码MRZ线获取MRZ信息:
String[] lines = new String[results.size()]; for (int i = 0; i < results.size(); i++) { lines[i] = results.get(i).text; } JsonObject info = MrzParser.parse(lines);
尝试示例代码
java -cp target/dlr-1.0.0-jar-with-dependencies.jar com.dynamsoft.dlr.Test images/1.png