Dynamsoft Document Normalizer SDK帮助开发人员快速构建文档扫描应用程序。它提供了一组API来检测文档边缘并标准化文档图像。目前,SDK仅支持C/C++
,Android
,iOS
,Xamarin.Forms
和JavaScript
。尽管尚无Java版本可下载,但我们可以自己制作。本文旨在将DynamSoft文档标准器C ++库封装到Java Jar软件包中。 JAR软件包可以在Windows和Linux上的Java应用程序中使用。
先决条件
如何使用CMAKE构建Java JNI项目
首先,我们启动一个新的Java项目,并创建一个定义某些本机方法的NativeDocumentScanner.java
文件。本机方法用于加载本机库并桥接C ++ API。
package com.dynamsoft.ddn;
import java.util.ArrayList;
public class NativeDocumentScanner {
private long nativePtr = 0;
static {
try {
if (NativeLoader.load()) {
System.out.println("Successfully loaded Dynamsoft Document Normalizer.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public NativeDocumentScanner() {
nativePtr = nativeCreateInstance();
}
public void destroyInstance() {
if (nativePtr != 0)
nativeDestroyInstance(nativePtr);
}
public static int setLicense(String license) {
return nativeInitLicense(license);
}
public ArrayList<DocumentResult> detectFile(String fileName) {
return nativeDetectFile(nativePtr, fileName);
}
public String getVersion() {
return nativeGetVersion();
}
public NormalizedImage normalizeFile(String fileName, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
return nativeNormalizeFile(nativePtr, fileName, x1, y1, x2, y2, x3, y3, x4, y4);
}
public int setParameters(String parameters) {
return nativeSetParameters(nativePtr, parameters);
}
public int saveImage(NormalizedImage image, String fileName) {
return nativeSaveImage(image, fileName);
}
private native static int nativeInitLicense(String license);
private native long nativeCreateInstance();
private native void nativeDestroyInstance(long nativePtr);
private native ArrayList<DocumentResult> nativeDetectFile(long nativePtr, String fileName);
private native String nativeGetVersion();
private native NormalizedImage nativeNormalizeFile(long nativePtr, String fileName, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
private native int nativeSetParameters(long nativePtr, String parameters);
private native int nativeSaveImage(NormalizedImage image, String fileName);
}
然后,我们使用javah
工具为Java类NativeDocumentScanner
生成标头文件。
cd src/main/java
javah -o ../../../jni/NativeDocumentScanner.h com.dynamsoft.ddn.NativeDocumentScanner
该工具无需手动编写标头文件。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_dynamsoft_ddn_NativeDocumentScanner */
#ifndef _Included_com_dynamsoft_ddn_NativeDocumentScanner
#define _Included_com_dynamsoft_ddn_NativeDocumentScanner
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeInitLicense
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeInitLicense
(JNIEnv *, jclass, jstring);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeCreateInstance
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeCreateInstance
(JNIEnv *, jobject);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeDestroyInstance
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeDestroyInstance
(JNIEnv *, jobject, jlong);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeDetectFile
* Signature: (JLjava/lang/String;)Ljava/util/ArrayList;
*/
JNIEXPORT jobject JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeDetectFile
(JNIEnv *, jobject, jlong, jstring);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeGetVersion
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeGetVersion
(JNIEnv *, jobject);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeNormalizeFile
* Signature: (JLjava/lang/String;IIIIIIII)Lcom/dynamsoft/ddn/NormalizedImage;
*/
JNIEXPORT jobject JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeNormalizeFile
(JNIEnv *, jobject, jlong, jstring, jint, jint, jint, jint, jint, jint, jint, jint);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeSetParameters
* Signature: (JLjava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeSetParameters
(JNIEnv *, jobject, jlong, jstring);
/*
* Class: com_dynamsoft_ddn_NativeDocumentScanner
* Method: nativeSaveImage
* Signature: (Lcom/dynamsoft/ddn/NormalizedImage;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeSaveImage
(JNIEnv *, jobject, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
创建一个NativeDocumentScanner.cxx
文件以实现本机方法。我们将在后面的部分中讨论实施。
现在,打开CMakeLists.txt
文件并添加以下构建配置:
cmake_minimum_required (VERSION 2.6)
project (ddn)
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(ddn SHARED NativeDocumentScanner.cxx)
if(WINDOWS)
target_link_libraries (${PROJECT_NAME} "DynamsoftCorex64" "DynamsoftDocumentNormalizerx64")
else()
target_link_libraries (${PROJECT_NAME} "DynamsoftCore" "DynamsoftDocumentNormalizer" pthread)
endif()
# Set installation directory
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../src/main/")
set(LIBRARY_PATH "java/com/dynamsoft/ddn/native")
if(WINDOWS)
install (DIRECTORY "${PROJECT_SOURCE_DIR}/lib/win/" DESTINATION "${CMAKE_INSTALL_PREFIX}${LIBRARY_PATH}/win")
install (TARGETS ddn 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 ddn DESTINATION "${CMAKE_INSTALL_PREFIX}${LIBRARY_PATH}/linux")
endif()
-
find_package(JNI REQUIRED)
:在系统中找到JNI库。 -
include_directories
:添加JNI库和DynamSoft文档标准器SDK的包含路径。 -
link_directories
:添加C ++库文件的搜索路径。 -
add_library
:构建共享库。 -
target_link_libraries
:将库与DynamSoft文档标准器SDK链接。 -
install
:将库文件复制到目标文件夹。
运行以下命令以在Windows和Linux上构建JNI项目:
# Windows
mkdir build
cd build
cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
cmake --build . --config Release --target install
# Linux
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
它将生成用于Windows的dnn.dll
和Linux的libdnn.so
。在下一部分中,我们将将本机库包装到JAR文件中。
如何使用Maven使用C ++库来构建Java Jar软件包
创建一个pom.xml
文件,在该文件中,我们定义了本机库文件所在的资源路径,并使用Maven Assembly Plugin
将本机库包装到JAR文件中。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dynamsoft</groupId>
<artifactId>ddn</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.md</exclude>
<exclude>**/*.h</exclude>
<exclude>**/*.lib</exclude>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
之后,运行以下命令生成ddn-1.0.0.jar
:
mvn package
如何从Jar包装加载共享库
JAR文件包含类文件和特定于平台的库。要在Java应用程序中调用这些本机库,我们首先需要将它们提取到临时文件夹,然后致电System.load()
加载本机库。
private static boolean extractResourceFiles(String ddnNativeLibraryPath, String ddnNativeLibraryName,
String tempFolder) throws IOException {
String[] filenames = null;
if (Utils.isWindows()) {
filenames = new String[] {"api-ms-win-core-file-l1-2-0.dll",
"api-ms-win-core-file-l2-1-0.dll",
"api-ms-win-core-localization-l1-2-0.dll",
"api-ms-win-core-processthreads-l1-1-1.dll",
"api-ms-win-core-synch-l1-2-0.dll",
"api-ms-win-core-timezone-l1-1-0.dll",
"api-ms-win-crt-conio-l1-1-0.dll",
"api-ms-win-crt-convert-l1-1-0.dll",
"api-ms-win-crt-environment-l1-1-0.dll",
"api-ms-win-crt-filesystem-l1-1-0.dll",
"api-ms-win-crt-heap-l1-1-0.dll",
"api-ms-win-crt-locale-l1-1-0.dll",
"api-ms-win-crt-math-l1-1-0.dll",
"api-ms-win-crt-multibyte-l1-1-0.dll",
"api-ms-win-crt-runtime-l1-1-0.dll",
"api-ms-win-crt-stdio-l1-1-0.dll",
"api-ms-win-crt-string-l1-1-0.dll",
"api-ms-win-crt-time-l1-1-0.dll",
"api-ms-win-crt-utility-l1-1-0.dll",
"concrt140.dll",
"DynamicImagex64.dll",
"DynamicPdfCorex64.dll",
"DynamicPdfx64.dll",
"DynamsoftCorex64.dll",
"DynamsoftImageProcessingx64.dll",
"DynamsoftIntermediateResultx64.dll",
"msvcp140.dll",
"msvcp140_1.dll",
"msvcp140_2.dll",
"ucrtbase.dll",
"vccorlib140.dll",
"vcomp140.dll",
"vcruntime140.dll", "DynamsoftDocumentNormalizerx64.dll", "ddn.dll"};
}
else if (Utils.isLinux()) {
filenames = new String[] {"libddn.so", "libDynamicImage.so", "libDynamicPdf.so", "libDynamicPdfCore.so", "libDynamsoftCore.so", "libDynamsoftDocumentNormalizer.so", "libDynamsoftImageProcessing.so", "libDynamsoftIntermediateResult.so"};
}
boolean ret = true;
for (String file : filenames) {
ret &= extractAndLoadLibraryFile(ddnNativeLibraryPath, file, tempFolder);
}
return ret;
}
private static boolean extractAndLoadLibraryFile(String libFolderForCurrentOS, String libraryFileName,
String targetFolder) {
String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
String extractedLibFileName = libraryFileName;
File extractedLibFile = new File(targetFolder, extractedLibFileName);
try {
if (extractedLibFile.exists()) {
// test md5sum value
String md5sum1 = md5sum(NativeDocumentScanner.class.getResourceAsStream(nativeLibraryFilePath));
String md5sum2 = md5sum(new FileInputStream(extractedLibFile));
if (md5sum1.equals(md5sum2)) {
return loadNativeLibrary(targetFolder, extractedLibFileName);
} else {
// remove old native library file
boolean deletionSucceeded = extractedLibFile.delete();
if (!deletionSucceeded) {
throw new IOException(
"failed to remove existing native library file: " + extractedLibFile.getAbsolutePath());
}
}
}
// Extract file into the current directory
InputStream reader = NativeDocumentScanner.class.getResourceAsStream(nativeLibraryFilePath);
FileOutputStream writer = new FileOutputStream(extractedLibFile);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, bytesRead);
}
writer.close();
reader.close();
if (!System.getProperty("os.name").contains("Windows")) {
try {
Runtime.getRuntime().exec(new String[] { "chmod", "755", extractedLibFile.getAbsolutePath() })
.waitFor();
} catch (Throwable e) {
}
}
return loadNativeLibrary(targetFolder, extractedLibFileName);
} catch (IOException e) {
System.err.println(e.getMessage());
return false;
}
}
private static synchronized boolean loadNativeLibrary(String path, String name) {
File libPath = new File(path, name);
if (libPath.exists()) {
try {
System.load(new File(path, name).getAbsolutePath());
return true;
} catch (UnsatisfiedLinkError e) {
System.err.println(e);
return false;
}
} else
return false;
}
注意:在Windows上加载DLL文件时,加载DLL文件的顺序很重要。首先加载的DLL文件不应取决于其他DLL文件。例如,ddn.dll
取决于DynamsoftDocumentNormalizerx64.dll
,因此应首先加载DynamsoftDocumentNormalizerx64.dll
。如果您错误的加载顺序,您将看到unsatisfiedLinkError
。
如何实施JNI API进行文档边缘检测和归一化
在本节中,您将看到如何在Java和C ++中实现API来进行文档边缘检测,透视校正和图像增强。
初始化DynamSoft文档标准器
由于许可证在全球范围内工作,因此我们创建了一个静态方法来设置许可证。该方法只需要一次调用。
private native static int nativeInitLicense(String license);
JNIEXPORT jint JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_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=ddn to get a trial license.
int ret = DC_InitLicense(pszLicense, errorMsgBuffer, 512);
printf("DC_InitLicense: %s\n", errorMsgBuffer);
env->ReleaseStringUTFChars(license, pszLicense);
return ret;
}
GetStringUTFChars
方法用于将Java字符串转换为C字符串。使用后,不要忘记释放内存。
创建和破坏文档扫描仪实例
在C/C ++中,我们使用DDN_CreateInstance
创建文档扫描仪实例并使用DDN_DestroyInstance
销毁该实例。
JNIEXPORT jlong JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeCreateInstance(JNIEnv *, jobject)
{
return (jlong)DDN_CreateInstance();
}
JNIEXPORT void JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeDestroyInstance(JNIEnv *, jobject, jlong handler)
{
if (handler)
{
DDN_DestroyInstance((void *)handler);
}
}
创建文档扫描仪实例时,nativeCreateInstance()
方法在DocumentScanner
类的构造函数中调用。本机指针保存在Java中。
private long nativePtr = 0;
public NativeDocumentScanner() {
nativePtr = nativeCreateInstance();
}
C ++对象一直保持在内存中,直到调用destroyInstance()
方法为止。
public void destroyInstance() {
if (nativePtr != 0)
nativeDestroyInstance(nativePtr);
}
配置文档标准器的参数
参数配置允许您更改文档扫描仪的行为。
private native int nativeSetParameters(long nativePtr, String parameters);
JNIEXPORT jint JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeSetParameters(JNIEnv *env, jobject, jlong ptr, jstring parameters)
{
if (ptr)
{
void *handler = (void *)ptr;
const char *params = env->GetStringUTFChars(parameters, NULL);
char errorMsgBuffer[512];
int ret = DDN_InitRuntimeSettingsFromString(handler, params, errorMsgBuffer, 512);
printf("Init runtime settings: %s\n", errorMsgBuffer);
env->ReleaseStringUTFChars(parameters, params);
return ret;
}
return -1;
}
例如,您可以更改归一化的图像颜色模式:
public final static String binary = "{\"GlobalParameter\":{\"Name\":\"GP\"},\"ImageParameterArray\":[{\"Name\":\"IP-1\",\"NormalizerParameterName\":\"NP-1\"}],\"NormalizerParameterArray\":[{\"Name\":\"NP-1\",\"ColourMode\": \"ICM_BINARY\" }]}";
public final static String color = "{\"GlobalParameter\":{\"Name\":\"GP\"},\"ImageParameterArray\":[{\"Name\":\"IP-1\",\"NormalizerParameterName\":\"NP-1\"}],\"NormalizerParameterArray\":[{\"Name\":\"NP-1\",\"ColourMode\": \"ICM_COLOUR\" }]}";
public final static String grayscale = "{\"GlobalParameter\":{\"Name\":\"GP\"},\"ImageParameterArray\":[{\"Name\":\"IP-1\",\"NormalizerParameterName\":\"NP-1\"}],\"NormalizerParameterArray\":[{\"Name\":\"NP-1\",\"ColourMode\": \"ICM_GRAYSCALE\"}]}";
文档边缘检测
使用DDN_DetectQuadFromFile()
方法来检测图像文件的文档边缘。
private native ArrayList<DocumentResult> nativeDetectFile(long nativePtr, String fileName);
JNIEXPORT jobject JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeDetectFile(JNIEnv *env, jobject, jlong ptr, jstring fileName)
{
jobject arrayList = NULL;
if (ptr)
{
jclass documentResultClass = env->FindClass("com/dynamsoft/ddn/DocumentResult");
if (NULL == documentResultClass)
printf("FindClass failed\n");
jmethodID documentResultConstructor = env->GetMethodID(documentResultClass, "<init>", "(IIIIIIIII)V");
if (NULL == documentResultConstructor)
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");
void *handler = (void *)ptr;
const char *pszFileName = env->GetStringUTFChars(fileName, NULL);
DetectedQuadResultArray *pResults = NULL;
int ret = DDN_DetectQuadFromFile(handler, pszFileName, "", &pResults);
if (ret)
{
printf("Detection error: %s\n", DC_GetErrorString(ret));
}
if (pResults)
{
int count = pResults->resultsCount;
arrayList = env->NewObject(arrayListClass, arrayListConstructor);
for (int i = 0; i < count; i++)
{
DetectedQuadResult *quadResult = pResults->detectedQuadResults[i];
int confidence = quadResult->confidenceAsDocumentBoundary;
DM_Point *points = quadResult->location->points;
int x1 = points[0].coordinate[0];
int y1 = points[0].coordinate[1];
int x2 = points[1].coordinate[0];
int y2 = points[1].coordinate[1];
int x3 = points[2].coordinate[0];
int y3 = points[2].coordinate[1];
int x4 = points[3].coordinate[0];
int y4 = points[3].coordinate[1];
jobject object = env->NewObject(documentResultClass, documentResultConstructor, confidence, x1, y1, x2, y2, x3, y3, x4, y4);
env->CallBooleanMethod(arrayList, arrayListAdd, object);
}
}
if (pResults != NULL)
DDN_FreeDetectedQuadResultArray(&pResults);
env->ReleaseStringUTFChars(fileName, pszFileName);
}
return arrayList;
}
我们需要创建一个DocumentResult
类来存储检测结果。
public class DocumentResult {
public int confidence;
public int x1, y1, x2, y2, x3, y3, x4, y4;
public DocumentResult(int confidence, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
this.confidence = confidence;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
this.x4 = x4;
this.y4 = y4;
}
}
记录归一化
获得文档的四边形坐标后,我们致电DDN_NormalizeFile()
使文档正常化。
private native NormalizedImage nativeNormalizeFile(long nativePtr, String fileName, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
JNIEXPORT jobject JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeNormalizeFile(JNIEnv *env, jobject, jlong ptr, jstring fileName, jint x1, jint y1, jint x2, jint y2, jint x3, jint y3, jint x4, jint y4)
{
if (ptr)
{
jclass normalizedImageClass = env->FindClass("com/dynamsoft/ddn/NormalizedImage");
if (NULL == normalizedImageClass)
printf("FindClass failed\n");
jmethodID normalizedImageConstructor = env->GetMethodID(normalizedImageClass, "<init>", "(IIII[BII)V");
if (NULL == normalizedImageConstructor)
printf("GetMethodID failed\n");
const char *pszFileName = env->GetStringUTFChars(fileName, NULL);
void *handler = (void *)ptr;
Quadrilateral quad;
quad.points[0].coordinate[0] = x1;
quad.points[0].coordinate[1] = y1;
quad.points[1].coordinate[0] = x2;
quad.points[1].coordinate[1] = y2;
quad.points[2].coordinate[0] = x3;
quad.points[2].coordinate[1] = y3;
quad.points[3].coordinate[0] = x4;
quad.points[3].coordinate[1] = y4;
NormalizedImageResult* normalizedResult = NULL;
int errorCode = DDN_NormalizeFile(handler, pszFileName, "", &quad, &normalizedResult);
if (errorCode != DM_OK)
printf("%s\r\n", DC_GetErrorString(errorCode));
ImageData *imageData = normalizedResult->image;
int width = imageData->width;
int height = imageData->height;
int stride = imageData->stride;
int format = (int)imageData->format;
unsigned char* data = imageData->bytes;
int orientation = imageData->orientation;
int length = imageData->bytesLength;
jbyteArray byteArray = env->NewByteArray(length);
env->SetByteArrayRegion(byteArray, 0, length, (jbyte *)data);
jobject object = env->NewObject(normalizedImageClass, normalizedImageConstructor, width, height, stride, format, byteArray, orientation, length);
env->ReleaseStringUTFChars(fileName, pszFileName);
if (normalizedResult != NULL)
DDN_FreeNormalizedImageResult(&normalizedResult);
return object;
}
return NULL;
}
归一化图像数据存储在NormalizedImage
类中。
public class NormalizedImage {
public int width;
public int height;
public int stride;
public int format;
public byte[] data;
public int orientation;
public int length;
public NormalizedImage(int width, int height, int stride, int format, byte[] data, int orientation, int length) {
this.width = width;
this.height = height;
this.stride = stride;
this.format = format;
this.data = data;
this.orientation = orientation;
this.length = length;
}
}
保存归一化文档
NormalizedImage
类包含图像数据和图像格式。因此,我们可以将NormalizedImage
转换为BufferedImage
并使用ImageIO
将图像数据保存到文件。
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
public void saveImage(String formatName, String fileName) {
BufferedImage image = null;
byte[] imageData = null;
int[] pixels = new int[width * height];
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
if (format == ImagePixelFormat.IPF_RGB_888) {
imageData = data;
for (int i = 0; i < width * height; i++) {
int r = imageData[i * 3] & 0xFF;
int g = imageData[i * 3 + 1] & 0xFF;
int b = imageData[i * 3 + 2] & 0xFF;
pixels[i] = (r << 16) | (g << 8) | b;
}
}
else if (format == ImagePixelFormat.IPF_GRAYSCALED) {
imageData = data;
for (int i = 0; i < width * height; i++) {
int gray = imageData[i] & 0xFF;
pixels[i] = (gray << 16) | (gray << 8) | gray;
}
}
else if (format == ImagePixelFormat.IPF_BINARY) {
imageData = binary2Grayscale();
for (int i = 0; i < width * height; i++) {
int gray = imageData[i] & 0xFF;
pixels[i] = (gray << 16) | (gray << 8) | gray;
}
}
image.setRGB(0, 0, width, height, pixels, 0, width);
Utils.display(image, "Normalized Image");
try {
ImageIO.write(image, formatName, new java.io.File(fileName));
} catch (Exception e) {
e.printStackTrace();
}
}
ImageIO
类不支持PDF
,但是DynamSoft Document normorizer确实可以。本机方法NormalizedImageResult_SaveToFile()
可以将归一化文档保存为BMP,PNG,JPEG和PDF文件。该代码有点复杂,我们需要从Java NormalizedImage
对象构造C ++ ImageData
类型。
private native int nativeSaveImage(NormalizedImage image, String fileName);
JNIEXPORT jint JNICALL Java_com_dynamsoft_ddn_NativeDocumentScanner_nativeSaveImage(JNIEnv *env, jobject, jobject obj, jstring fileName)
{
jclass normalizedImageClass = env->FindClass("com/dynamsoft/ddn/NormalizedImage");
if (NULL == normalizedImageClass)
printf("FindClass failed\n");
jfieldID fid = env->GetFieldID(normalizedImageClass, "width", "I");
if (NULL == fid)
printf("Get width failed\n");
jint width = env->GetIntField(obj, fid);
fid = env->GetFieldID(normalizedImageClass, "height", "I");
if (NULL == fid)
printf("Ge height failed\n");
jint height = env->GetIntField(obj, fid);
fid = env->GetFieldID(normalizedImageClass, "stride", "I");
if (NULL == fid)
printf("Get stride failed\n");
jint stride = env->GetIntField(obj, fid);
fid = env->GetFieldID(normalizedImageClass, "format", "I");
if (NULL == fid)
printf("Get format failed\n");
jint format = env->GetIntField(obj, fid);
fid = env->GetFieldID(normalizedImageClass, "data", "[B");
if (NULL == fid)
printf("Get data failed\n");
jbyteArray byteArray = (jbyteArray)env->GetObjectField(obj, fid);
jbyte *bytes = env->GetByteArrayElements(byteArray, NULL);
fid = env->GetFieldID(normalizedImageClass, "orientation", "I");
if (NULL == fid)
printf("Get orientation failed\n");
jint orientation = env->GetIntField(obj, fid);
fid = env->GetFieldID(normalizedImageClass, "length", "I");
if (NULL == fid)
printf("Get length failed\n");
jint length = env->GetIntField(obj, fid);
ImageData data;
data.bytes = (unsigned char *)bytes;
data.width = width;
data.height = height;
data.stride = stride;
data.format = (ImagePixelFormat)format;
data.orientation = orientation;
data.bytesLength = length;
const char *pszFileName = env->GetStringUTFChars(fileName, NULL);
NormalizedImageResult normalizedResult;
normalizedResult.image = &data;
int ret = NormalizedImageResult_SaveToFile(&normalizedResult, pszFileName);
if (ret != DM_OK)
printf("NormalizedImageResult_SaveToFile: %s\r\n", DC_GetErrorString(ret));
env->ReleaseStringUTFChars(fileName, pszFileName);
env->ReleaseByteArrayElements(byteArray, bytes, 0);
return ret;
}
除了将图像保存到文件外,我们还可以使用JFrame
在屏幕上显示它们:
public static void display(BufferedImage image, String title) {
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new FlowLayout());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setTitle(title);
frame.getContentPane().add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setVisible(true);
}
如何在Windows和Linux上构建Java文档扫描仪应用程序
-
获取30-day free trial license并致电
setLicense()
激活SDK。
int ret = NativeDocumentScanner.setLicense(license);
-
创建一个
NativeDocumentScanner
对象:
NativeDocumentScanner scanner = new NativeDocumentScanner();
-
设置参数。默认情况下,归一化图像是二进制图像。您可以通过致电
setParameters()
将其更改为灰度或颜色。
String template = "{\"GlobalParameter\":{\"Name\":\"GP\"},\"ImageParameterArray\":[{\"Name\":\"IP-1\",\"NormalizerParameterName\":\"NP-1\"}],\"NormalizerParameterArray\":[{\"Name\":\"NP-1\",\"ColourMode\": \"ICM_COLOUR\" }]}"; scanner.setParameters(template);
-
检测文档边缘:
ArrayList<DocumentResult> results = (ArrayList<DocumentResult>)scanner.detectFile(fileName);
-
根据四边形对文档进行标准化:
NormalizedImage normalizedImage = scanner.normalizeFile(fileName, result.x1, result.y1, result.x2, result.y2, result.x3, result.y3, result.x4, result.y4);
-
将归一化图像保存为PDF,JPEG或PNG:
scanner.saveImage(normalizedImage, "normalized.pdf");
构建和测试Java文档扫描仪SDK的整个步骤
cd jni
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
cd ../../
mvn package
java -cp target/ddn-1.0.0.jar com.dynamsoft.ddn.Test images/sample-image.png <optional: template.json> <optional: license key>