使用自动识别管理器的多线程 OCR - Windows C DLL
本教程介绍如何在 C/C++ Windows API 应用程序中使用 LEADTOOLS SDK 对 OCR 引擎进行多线程并行处理。
概述 | |
---|---|
概括 | 本教程介绍如何使用 Windows C DLL 应用程序中的 LEAD 自动识别管理器对 OCR 引擎进行多线程处理。 |
完成时间 | 45分钟 |
Visual Studio 项目 | 下载教程项目 (19 KB) |
平台 | Windows C DLL 应用程序 |
集成开发环境 | Visual Studio 2017、2019、2022 |
开发许可证 | 下载 LEADTOOLS |
尝试使用其他语言 |
|
所需知识
在使用自动识别管理器 - Windows C DLL教程进行多线程 OCR 之前,请先通过查看添加引用和设置许可证教程熟悉创建项目的基本步骤。
创建项目并添加 LEADTOOLS 引用
从“添加引用”和“设置许可证”教程中创建的 64 位 Windows API 项目的副本开始。 Start with a copy of the 64-bit Windows API project created in the Add References and Set a License tutorial. 如果该项目不可用,请按照该教程中的步骤创建它。
要使用 LEADTOOLS OCR 功能,需要额外的头文件和 DLL 文件。打开预编译头文件(或pch.h
,stdafx.h
取决于所使用的 Visual Studio 版本)并添加以下几行:
#include "C:\LEADTOOLS23\Include\ltocr.h"
#pragma comment (lib, "C:\\LEADTOOLS23\\Lib\\CDLL\\x64\\Ltocr_x.lib") // OCR support
注意:有关 OCR 应用程序所需的 DLL 文件的完整列表,请参阅应用程序中包含的文件。
设置许可证文件
许可证用于解锁项目所需的功能。必须在调用任何工具包函数之前设置许可证。有关详细信息(包括不同平台的教程),请参阅设置运行时许可证。
运行时许可证有两种类型:
- 评估许可证,在下载评估工具包时获得。它允许对工具包进行评估。
- 部署许可证。如果需要部署许可证文件和开发者密钥,请参阅获取许可证。
注意:在添加引用和设置许可证教程中更详细地介绍了添加 LEADTOOLS 引用和设置许可证。
添加多线程OCR代码
现在已经添加了 LEADTOOLS 引用并设置了许可证,可以开始编码了。
以下步骤适用于 Visual Studio 2019;对于其他版本的 Visual Studio,它们可能有所不同。
转到解决方案资源管理器并双击资源文件 (.rc)。
展开资源树中的菜单,双击菜单资源即可在设计器界面中打开。
在“Exit”项下方的空白处,点击并输入“Multithread &OCR”。将新项拖到“Exit”上方。这会导致该项的 ID 变为ID_FILE_MULTITHREADOCR
。
转到项目的主 CPP 文件,然后导航到WndProc
主窗口函数。在switch (wmId)
语句下方(即WM_COMMAND
案例下方),添加一个新案例:
switch (wmId)
{
case ID_FILE_MULTITHREADOCR:
{
TCHAR szFolderIn[1024] = TEXT(""); // Input files location
// Choose input files folder
if (SUCCESS != GetSourceFolder(hWnd, szFolderIn, ARRAYSIZE(szFolderIn)))
break;
float time1 = ProcessFolder(hWnd, szFolderIn, false, false);
if (time1 < 0)
break;
float time2 = ProcessFolder(hWnd, szFolderIn, false, true);
if (time2 < 0)
break;
float time3 = ProcessFolder(hWnd, szFolderIn, true, false);
if (time3 < 0)
break;
float time4 = ProcessFolder(hWnd, szFolderIn, true, true);
if (time4 < 0)
break;
TCHAR szResult[2000];
_stprintf_s(szResult, ARRAYSIZE(szResult), TEXT("Test times in seconds:\n"
"%.1f\tBoth sequential\n"
"%.1f\tParallel pages\n"
"%.1f\tParallel documents\n"
"%.1f\tBoth parallel"), time1, time2, time3, time4);
MessageBox(hWnd, szResult, TEXT("Recognition Time"), MB_ICONINFORMATION);
}
break;
// Keep rest of the code as is
LEADTOOLS OCR 自动识别管理器支持在处理作业时使用多个线程,可以使用L_OcrAutoRecognizeManagerOptions::UseThreads
结构成员启用该功能。
也可以在自己的线程中执行每个作业,这在下面的代码中使用CreateThread()
函数完成。
在函数上方添加两个名为ProcessFolder
和 的函数,并在其中添加以下代码:RecognizeTif
WndProc
struct RECOGNIZE_PARAMS
{
L_OcrAutoRecognizeManager autoRecognizeManager;
TCHAR* pszTifFile;
TCHAR* pszPdfFile;
};
DWORD WINAPI RecognizeTif(void* pParam)
{
RECOGNIZE_PARAMS* pRecParam = (RECOGNIZE_PARAMS*)pParam;
L_OcrAutoRecognizeManager_Run(pRecParam->autoRecognizeManager, pRecParam->pszTifFile, pRecParam->pszPdfFile, DOCUMENTFORMAT_PDF, NULL);
return 0;
}
//
// FUNCTION: ProcessFolder
// Return Value:
// < 0 : Error
// >= 0 : Elapsed Time
//
float ProcessFolder(HWND hWnd, TCHAR* pszFolderIn, bool parallelPages, bool parallalDocuments)
{
TCHAR szFolderSearch[1000] = TEXT(""); // Input files search location and pattern
_tcscpy_s(szFolderSearch, ARRAYSIZE(szFolderSearch), pszFolderIn);
_tcscat_s(szFolderSearch, ARRAYSIZE(szFolderSearch), TEXT("\\*.tif"));
WIN32_FIND_DATA FindFileData = { 0 };
HANDLE hFind = FindFirstFile(szFolderSearch, &FindFileData);
if (INVALID_HANDLE_VALUE == hFind)
{
MessageBox(hWnd, TEXT("No TIF files found in folder"), TEXT("LEADTOOLS Demo"), MB_ICONERROR);
return -1;
}
L_OcrEngine ocrEngine = NULL;
L_OcrAutoRecognizeManager autoRecognizeManager = NULL;
L_OcrAutoRecognizeManagerOptions autoRecognizeOptions = { 0 };
L_OcrDocumentManager ocrDocumentManager = NULL;
if (L_OcrEngineManager_CreateEngine(L_OcrEngineType_LEAD, &ocrEngine) != SUCCESS)
return -1;
L_OcrEngine_Startup(ocrEngine, NULL, TEXT("C:\\LEADTOOLS23\\Bin\\Common\\OcrLEADRuntime"));
L_OcrEngine_GetAutoRecognizeManager(ocrEngine, &autoRecognizeManager);
autoRecognizeOptions.StructSize = sizeof L_OcrAutoRecognizeManagerOptions;
L_OcrAutoRecognizeManager_GetOptions(autoRecognizeManager, &autoRecognizeOptions);
autoRecognizeOptions.UseThreads = parallelPages;
// Deskew and auto-orient all pages before recognition
autoRecognizeOptions.PreprocessPageCommands = L_OcrAutoPreprocessPageCommands_Deskew | L_OcrAutoPreprocessPageCommands_Rotate;
L_OcrAutoRecognizeManager_SetOptions(autoRecognizeManager, &autoRecognizeOptions);
// Create PDFs with Image/Text option
L_OcrEngine_GetDocumentManager(ocrEngine, &ocrDocumentManager);
DOCWRTPDFOPTIONS pdfOptions;
pdfOptions.Options.uStructSize = sizeof DOCWRTPDFOPTIONS;
L_OcrDocumentManager_GetFormatOptions(ocrDocumentManager, DOCUMENTFORMAT_PDF, &pdfOptions.Options);
// Set the specific PDF options we want
pdfOptions.bImageOverText = true;
pdfOptions.PdfProfile = DOCWRTPDFPROFILE_PDF;
L_OcrDocumentManager_SetFormatOptions(ocrDocumentManager, DOCUMENTFORMAT_PDF, &pdfOptions.Options);
ULONGLONG timeStart = GetTickCount64();
int nThreadCount = 0;
HANDLE hThreads[MAXIMUM_WAIT_OBJECTS] = { 0 };
do
{
if (nThreadCount >= MAXIMUM_WAIT_OBJECTS)
{
MessageBox(hWnd, TEXT("Too many documents for this test"), TEXT("LEADTOOLS Demo"), MB_ICONERROR);
return -1;
}
TCHAR szTifFile[1024] = TEXT(""); // Input file full location and name
_tcscpy_s(szTifFile, ARRAYSIZE(szTifFile), pszFolderIn);
_tcscat_s(szTifFile, ARRAYSIZE(szTifFile), TEXT("\\"));
_tcscat_s(szTifFile, ARRAYSIZE(szTifFile), FindFileData.cFileName);
TCHAR szPdfFile[1024] = TEXT(""); // Output file full location and name
_tcscpy_s(szPdfFile, ARRAYSIZE(szPdfFile), szTifFile);
_tcscat_s(szPdfFile, ARRAYSIZE(szPdfFile), TEXT(".pdf"));
RECOGNIZE_PARAMS param = { 0 };
param.autoRecognizeManager = autoRecognizeManager;
param.pszTifFile = szTifFile;
param.pszPdfFile = szPdfFile;
if (parallalDocuments) // Call in a new thread
{
hThreads[nThreadCount] = CreateThread(NULL, 0, RecognizeTif, ¶m, 0, NULL);
nThreadCount++;
}
else // Sequential documents, call directly
RecognizeTif(¶m);
} while (FindNextFile(hFind, &FindFileData) != 0);
if (nThreadCount > 0)
{
// Do NOT proceed to shutdown OCR before all threads exit
WaitForMultipleObjects(nThreadCount, hThreads, TRUE, INFINITE);
// Free all threads handles
while (nThreadCount > 0)
{
nThreadCount--;
CloseHandle(hThreads[nThreadCount]);
}
}
ULONGLONG timeElapsed = GetTickCount64() - timeStart;
L_OcrEngine_Shutdown(ocrEngine);
L_OcrEngine_Destroy(ocrEngine);
return timeElapsed / 1000.0;
}
该GetSourceFolder()
函数可以是任何使用szFolderIn
包含 TIFF 文件的有效文件夹名称填充变量的函数。要显示“选择文件夹”对话框以获取文件夹名称,请添加以下代码:
#include <shobjidl_core.h>
L_INT GetSourceFolder(HWND hWnd, TCHAR* pszFolder, rsize_t string_size)
{
L_INT nRet = FAILURE;
IFileOpenDialog* pFileOpenDialog = NULL;
IShellItem* pShellItem = NULL;
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void**)&pFileOpenDialog);
if (pFileOpenDialog)
{
pFileOpenDialog->SetOptions(FOS_PICKFOLDERS);
if (SUCCEEDED(pFileOpenDialog->Show(hWnd)))
{
pFileOpenDialog->GetResult(&pShellItem);
LPWSTR pFolder = NULL;
pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pFolder);
if (pFolder)
{
_tcscpy_s(pszFolder, string_size, pFolder);
CoTaskMemFree(pFolder);
nRet = SUCCESS;
}
}
}
if (pShellItem)
pShellItem->Release();
if (pFileOpenDialog)
pFileOpenDialog->Release();
return nRet;
}
运行项目
按F5或选择“调试”->“开始调试”来运行项目。
如果正确执行这些步骤,应用程序将运行并允许用户选择输入文件夹。然后,应用程序将处理输入文件夹中的每个 TIFF 文件,并据此创建一个新的 PDF 文件。此过程将使用不同的顺序和并行处理组合重复四次。
注意:在图片所示的测试中,并行处理目录文档以及每个文档的页面时获得了最佳结果。但是,不同的图像集或具有不同 CPU 核心数的不同计算机可能会产生不同的结果。因此,务必在应用程序将要使用的实际硬件上使用代表用例的实际图像进行测试。
本教程展示了如何创建使用 LEAD OCR 引擎的 Windows C++ OCR 应用程序,以比较使用不同的顺序和并行处理技术时的性能。