HotPDF пуска Tesseract вътре в вашия Delphi процес чрез HPDFCreateTesseractDLLOCREngine — фабрика, добавена във v2.772.0, която динамично зарича DLL, съвместим с Tesseract 5, задвижва C API-я му (TessBaseAPIInit2, TessBaseAPIRecognize, резултатният итератор) и връща IHPDFOCREngine. THotPDF.ApplyLoadedOCRTextLayer ползва този engine, за да добави невидим, търсим Unicode текстов слой към сканираните PDF страници
Същият разпознавач вече беше достижим през външния tesseract.exe adapter, който пише BMP и парсва TSV. Този път работи, но всяка страница плаща за стартиране на процес, временен bitmap файл и текстов формат без базови линии и без контрол върху страничната сегментация. Викането на DLL-а маха и трите. Маха и стената на процеса, което значи, че Pascal свързване седи директно върху C структури, C булеви стойности и C-заделени низове. По-голямата част от това, което си заслужава да знаете за този adapter, е къде това свързване може тихо да се обърка
Как пускате Tesseract in-process от Delphi с HotPDF?
Пускането на Tesseract in-process с HotPDF струва едно фабрично извикване в unit-а HPDFTesseractRecognition и същия разговор ApplyLoadedOCRTextLayer, който всеки HotPDF OCR engine ползва. Фабриката валидира предварително. Файлът DLL и директорията tessdata трябва да съществуват, езиковият идентификатор може да съдържа само ASCII букви, цифри, _ и +, всеки модел в комбинация като chi_sim+eng трябва да има съответстващ файл .traineddata, а всички 21 задължителни export-и трябва да се намерят, преди engine-ът да бъде върнат. Грешки в конфигурацията вдигат EArgumentException; DLL, който се проваля да се зареди, вдига EOSError с кода за грешка на Windows и намек да проверите архитектурата и зависимостите
uses
SysUtils, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Win64 приложение се нуждае от 64-битов DLL; зависимите DLL-и до него
Engine := HPDFCreateTesseractDLLOCREngine('C:\OCR\Win64\libtesseract-5.dll',
'C:\OCR\tessdata', 'chi_sim+eng'); // THPDFTesseractOptions.Default
Doc := THotPDF.Create(nil);
try
Doc.AutoLaunch := False;
if Doc.LoadFromFile(SourceFile) < 1 then
raise Exception.Create('Cannot load ' + SourceFile);
Options := THPDFOCRTextLayerOptions.Default; // 300 DPI, MinimumConfidence 0.5
// Празен списък страници значи всяка страница; страници с текст се прескачат
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
raise Exception.Create(string(Info.Diagnostic));
Writeln(string(Info.EngineName), ': ', Info.AcceptedWordCount,
' words accepted, ', Info.DroppedWordCount, ' dropped');
Doc.SaveLoadedDocument(TargetFile);
finally
Doc.Free;
end;
end;
THPDFTesseractOptions.Default задава PageSegMode на tpsAuto, EngineMode на temDefault, TimeoutMilliseconds на 60,000 и MaxPixels на 16,777,216. Пикселният бюджет има повече значение, отколкото изглежда. US Letter страница при дефолтните 300 DPI се рендира в 2,550 × 3,300 пиксела, около 8.4 милиона, което се събира. Същата страница при 600 DPI е 5,100 × 6,600, около 33.7 милиона, и adapter-ът я отказва, преди Tesseract да е видял и пиксел. Вдигнете MaxPixels (таванът е 67,108,864) или дръжте DPI-я където е; всяка страна също е ограничена до 32,767 пиксела
DLL-ът се зарича с LoadLibraryEx със search флаговете за собствената му папка плюс директориите по подразбиране за безопасно търсене, така че image библиотеките, от които Tesseract зависи, могат да живеят до него без пипане по PATH или текущата директория. HotPDF не опакова и не тегли никаква OCR среда или модел; и двете ги осигурявате вие
Какво се мени в сравнение с tesseract.exe adapter-а?
DLL adapter-ът разменя процесна изолация за по-богат изход и по-нисък разход на страница. И двата adapter-а се включват в същия тръбопровод на текстовия слой, така че картирането на координати, филтрирането по увереност и записът всичко-или-нищо са идентични; различното е как пикселите влизат и думите излизат
| Aspect | tesseract.exe adapter | Tesseract DLL adapter |
|---|---|---|
| Фабрика | HPDFCreateTesseractOCREngine | HPDFCreateTesseractDLLOCREngine |
| Пиксели вход | BMP файл в лична временна директория | 8-битов grayscale буфер в паметта |
| Думи изход | TSV на ниво дума, ограничен до 64 MiB | Резултатен итератор, UTF-8 на дума |
| Базови линии | Няма налични | Предавани от TessPageIteratorBaseline |
| Сегментация на страница и режим на engine | Само автоматична сегментация | THPDFTesseractPageSegMode, THPDFTesseractEngineMode |
| Timeout | Твърд: дочерният процес се прекратява | Кооперативен: Tesseract трябва да забележи |
| Изолация при срив и памет | Отделен процес | Няма, споделя вашето адресно пространство |
Една цена не изчезва. Всеки разговор Recognize създава собствена инстанция на API-я и вика TessBaseAPIInit2, така че езиковите модели се инициализират на страница, а не веднъж на engine. Файловият кеш на операционната система омекотява презареждането, но при големи многомезични набори от модели това остава доминиращата фиксирана цена на страница и брои срещу крайния срок за разпознаване. In-process RapidOCR DLL engine-ът взима противоположния дизайн и държи ONNX моделите си резидентни през живота на engine-а; граничните проблеми (C ABI, заети буфери, непрекъсваема native работа) са от същото семейство
Защо Delphi не може да копира monitor структурата на Tesseract?
Delphi не може безопасно да огледали progress monitor-а на Tesseract, защото ETEXT_DESC съдържа зависещи от версията вътрешни полета, така че ръчно копиран запис поставя cancel callback-а и крайния срок на грешните отмествания на някои билдове. Нищо не се проваля гръмко, когато това стане. Tesseract просто прочита указателя към вашия callback от поле, което вече държи нещо друго, или изобщо никога не вижда крайния срок
HotPDF затова третира monitor-а като непрозрачен указател и го пипа само през експортирани функции: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs и TessMonitorDelete. Ако сами свържете C API-я за друга цел, същият образец важи. Скицата по-долу е ваш собствен свързващ код, не HotPDF API, и огледалява декларациите, които HotPDF ползва вътрешно
type
// C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
TTessMonitorCreate = function: Pointer; cdecl; // ETEXT_DESC*, никога не дереферирайте
TTessMonitorDelete = procedure(Monitor: Pointer); cdecl;
TTessMonitorSetCancelFunc = procedure(Monitor: Pointer; Func: TTessCancelFunc); cdecl;
TTessMonitorSetCancelThis = procedure(Monitor, CancelThis: Pointer); cdecl;
TTessMonitorSetDeadlineMSecs = procedure(Monitor: Pointer; MSecs: Integer); cdecl;
TTessBaseAPIRecognize = function(Handle, Monitor: Pointer): Integer; cdecl;
TOCRJob = record
CancelRequested: Boolean;
DeadlineTick: UInt64;
end;
POCRJob = ^TOCRJob;
function ShouldCancel(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
begin
// Върви на стека на Tesseract: четете флагове и часовник, никога не вдигайте
Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
(GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;
// Употреба: указателите към функциите се извличат чрез GetProcAddress:
// Monitor := MonitorCreate();
// try
// MonitorSetCancelThis(Monitor, @Job);
// MonitorSetCancelFunc(Monitor, ShouldCancel);
// MonitorSetDeadlineMSecs(Monitor, RemainingMs);
// RC := BaseAPIRecognize(API, Monitor);
// finally
// MonitorDelete(Monitor);
// end;
Две подробности в тази скица са нарочно. Callback-ът връща Boolean, който е един байт и в Delphi, и в Free Pascal, съвпадайки с C bool в TessCancelFunc. Четирибайтовият Windows BOOL или Delphi LongBool изглежда взаимозаменяем и не е: когато едната страна пише един байт, а другата чете четири, горните байтове на връщащия регистър са каквото и да е останало там, и false може да пристигне като true. Същият header още усложнява, защото функции като TessPageIteratorBoundingBox връщат int, който HotPDF декларира като Integer. Четете C типа на всяка връщана стойност, вместо да предполагате една конвенция за целия API
Втората подробност е, че callback-ът никога не вдига. Delphi изключение, размотаващо се през C++ рамките на Tesseract, е недефинирано поведение, така че callback-ът на HotPDF чете само токена за отмяна и монотонна стойност GetTickCount64. Adapter-ът превръща резултата в диагностика за отмяна или timeout, след като TessBaseAPIRecognize се върне, и извършва тази проверка независимо от native кода за връщане
Кои native указатели притежава страната на Delphi?
HotPDF Tesseract DLL adapter-ът притежава три native обекта на заявка — инстанцията на API-я, monitor-ът и резултатният итератор — и заема всичко останало. Всеки разговор Recognize създава собствен набор и го освобождава в блок finally: TessResultIteratorDelete, после TessMonitorDelete, после TessBaseAPIDelete. Освобождаването на интерфейса на engine-а разтоварва библиотеката
TessResultIteratorGetPageIteratorвръща зает изглед в резултатния итератор, не нов обект. HotPDF го ползва заTessPageIteratorBoundingBoxиTessPageIteratorBaselineи никога не го освобождава; отделното му изтриване би освободило същата памет два пътиTessResultIteratorGetUTF8Textвръща низ, заделен от собствения runtime на DLL-а. HotPDF го копира и го връща презTessDeleteTextв блокfinally; PascalFreeMemби го освободил на грешния heap- Текстът на думите се декодира със строга UTF-8 валидация и проверена дължина преди конверсията. Думи с контролни символи, развален UTF-8, кутии извън изображението, обърнати правоъгълници или увереност извън 0–100 провалят заявката вместо мълчаливо да бъдат закърпени
- Общият текст на заявка е ограничен до 1,048,576 UTF-16 кодови единици, а броят думи трябва да се събере в бюджета на заявката, подаден надолу от
ApplyLoadedOCRTextLayer
Увереността пристига като 0–100 и се мащабира до 0–1, така че THPDFOCRTextLayerOptions.MinimumConfidence значи едно и също за всеки engine. Когато Tesseract докладва базова линия, двата края се предават; иначе тръбопроводът на текстовия слой пада обратно на геометричната си оценка, точно както прави за TSV вход
Защо да валидираме enum, преди да стигне до DLL-а?
HotPDF копира суровия ordinal на PageSegMode и EngineMode в Integer преди проверката на диапазона, защото компилатор може да предположи, че променлива от enum винаги държи обявена стойност и да сгъне Ord(X) > Ord(High(T)) до константа false. Ordinal-ите не са украса: THPDFTesseractPageSegMode следва нумерацията на страничната сегментация на Tesseract от 0 до 13, THPDFTesseractEngineMode следва нумерацията на режимите на engine от 0 до 3, и двете отиват при DLL-а като обикновени цели числа. Запис с опции, построен с FillChar, попълнен от stream или подаден от C++Builder с кастнато цяло число, може да носи байт като 200. Валидирането на копирания ordinal превръща това в EArgumentException по време на фабриката вместо в недефиниран режим вътре в native кода. Фабриката също отхвърля tpsOSDOnly и tpsAutoOnly, които не произвеждат думи, и изисква osd.traineddata за tpsAutoOSD и tpsSparseTextOSD
Какво всъщност гарантира timeout-ът за разпознаване?
Timeout-ът на Tesseract DLL-а е кооперативен: HotPDF може да спре собствената си работа и да помоли Tesseract да спре, но не може да принуди native кода да се върне. Часовникът тръгва, когато Recognize започне, така че конверсията в bitmap и инициализацията на модела изразходват същия бюджет като разпознаването. HotPDF проверява изминалото време и токена за отмяна по време на grayscale конверсията и между думите, докато обхожда резултатите, и подава оставащите милисекунди на TessMonitorSetDeadlineMSecs, преди да извика TessBaseAPIRecognize
Пролуката е вътре в native разговора. Monitor-ът на Tesseract се пита по време на разпознаване на думи, не по време на TessBaseAPIInit2 или анализа на страничното оформление, така че бавно зареждане на модел или патологично оформление може да мине крайния срок, преди timeout-ът да бъде докладван. Пикселният и изходният бюджет също не ограничават собствения разход на памет на native библиотеката. Ако ви трябва работник, който можете да убиете, ползвайте process adapter-а; това е честният компромис, не липсваща възможност
Страничната сегментация е мястото, където DLL adapter-ът си заслужава на труден вход. Формуляри, етикети и сканирани таблици с разпръснати полета често се разпознават по-добре с tpsSparseText, отколкото с автоматичната сегментация, която се опитва да сглоби колони и параграфи, които ги няма
procedure OCRFormPages(Doc: THotPDF; const Pages: array of Integer);
var
Engine: IHPDFOCREngine;
TessOptions: THPDFTesseractOptions;
LayerOptions: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
TessOptions := THPDFTesseractOptions.Default;
TessOptions.PageSegMode := tpsSparseText; // разпръснати полета, без сглобяване на колони
TessOptions.EngineMode := temLSTMOnly; // иска LSTM модели в tessdata
TessOptions.TimeoutMilliseconds := 20000; // включва инициализацията на модела
Engine := HPDFCreateTesseractDLLOCREngine('C:\OCR\Win64\libtesseract-5.dll',
'C:\OCR\tessdata', 'eng+deu', TessOptions);
LayerOptions := THPDFOCRTextLayerOptions.Default;
LayerOptions.MinimumConfidence := 0.6;
if not Doc.ApplyLoadedOCRTextLayer(Pages, Engine, LayerOptions, Info) then
case Info.Status of
otlsCancelled:
Writeln('OCR cancelled, document unchanged');
otlsEngineError:
Writeln('Tesseract failed or timed out: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
end;
Timeout излиза като otlsEngineError с диагностиката Tesseract DLL OCR timed out, а отменен токен излиза като otlsCancelled. И в двата случая ApplyLoadedOCRTextLayer е разпознал всяка избрана страница, преди да започне транзакцията за запис, така че провал на страница 40 от 50 оставя заредения документ точно както беше. Имайте предвид, че tpsSingleLine, tpsSingleBlock и tpsSparseText сменят само сегментацията; нито едно не изправя наклонено сканиране
Free Pascal и Lazarus: остарели пиксели и изгубен китайски
И двете Tesseract фабрики работят в Windows Free Pascal и Lazarus Win32 и Win64 билдове от v2.772.1, след две FPC-специфични поправки. Пребилднете първо Lazarus пакета за целевата архитектура; общият порт е разгледан в HotPDF върху Free Pascal и Lazarus Win64
Първата поправка засяга пикселите. LCL TBitmap, писан през scanlines, може да обнови суровото си изображение, без да обнови Windows bitmap манипулатора, така че GetDIBits върху този манипулатор връща старите пиксели. Симптомът беше озадачаващ: текст, начертан директно върху bitmap, се разпознаваше, докато страница, рендирана от PDF рендерера на HotPDF, произвеждаше празен списък с думи. На FPC adapter-ът вече чете формат-съзнателен snapshot през CreateIntfImage, който зачита пикселния формат и реда на редовете на суровото изображение. Delphi билдът пази пътя GetDIBits върху частно 24-битово копие. Нито един билд не модифицира bitmap-а на извикващия
Втората поправка принадлежи на tesseract.exe adapter-а. TStringList на FPC съхранява ANSI низове, така че задаването на декодиран UTF-8 TSV текст на Lines.Text мълчаливо изпускаше всеки китайски или допълнително-равнинен символ, който системната ANSI кодова страница не можеше да представи. Пътят на FPC вече пази TSV като UTF-8 байтове, маха BOM на байтово ниво и декодира всяка дума поотделно в UnicodeString. DLL adapter-ът никога не е имал този проблем, защото декодира всяка дума директно от итератора
Бърза справка
- Фабрика:
HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options])вHPDFTesseractRecognition, добавена във v2.772.0, FPC поддръжка във v2.772.1 - Дефолти:
tpsAuto,temDefault, 60,000 ms, 16,777,216 пиксела; диапазон на timeout 1–3,600,000 ms, пикселен таван 67,108,864 - Съобразете битовостта на DLL-а с приложението и сложете зависимите DLL-и до Tesseract DLL-а
- Третирайте monitor-а като непрозрачен; никога не копирайте
ETEXT_DESCв Pascal запис - Обявете cancel callback-а
cdeclс еднобайтов резултатBooleanи никога не пускайте изключение да избяга от него - Освобождавайте текста на итератора с
TessDeleteText; никога не освобождавайте страницния итератор, получен от резултатния - Очаквайте крайният срок да е кооперативен: инициализацията на модела и анализът на оформлението могат да го надхвърлят
- Ползвайте tesseract.exe adapter-а, когато имате нужда от твърдо спиране или изолация при срив
Tesseract DLL adapter-ът, process adapter-ите и вграденият OCR engine идват всички с HotPDF Delphi PDF компонента за Delphi, C++Builder и Free Pascal; вижте продуктовата страница на HotPDF за издания и изтегляния