Page 1 of 1

Can anyone implement a simple tesseract dllcall example?

Posted: 27 Jul 2021, 19:08
by william_ahk
I tried Vis2 but it's quite slow because each time it runs tesseract cli binary. The UWP OCR is fast but it won't recognize certain texts and is less accurate than tesseract. It would be great if ahk can directly interop with the tesseract library.
There is a C++ example here but unfortunately I don't know much C++ :D
It will be a big step forward if this is implemented (accurate and fast OCR library).

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 28 Jul 2021, 14:51
by swagfag
in a folder, download and put: run with x64. error handling omitted

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 01 Aug 2021, 08:33
by william_ahk
@swagfag Thank you so much!

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 01 Aug 2021, 23:45
by william_ahk
@swagfag Is it possible to pass an image from memory to pixRead? That way we can recognize texts on screen directly without saving the screenshot as a file. Looks like there is a pixReadMem function in the leptonica library.

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 02 Aug 2021, 14:39
by malcev
pixReadMem - reads bitmap file from memory.
So You need create BITMAPFILEHEADER and BITMAPINFOHEADER structures and combine with bitmap data by Yourself.

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 02 Aug 2021, 14:50
by swagfag

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 03 Aug 2021, 03:38
by william_ahk
Thanks! Should I pass the image as a buffer? How do I get bytes_per_pixel, bytes_per_line for the image?

Code: Select all

DllCall("libtesseract500\TessBaseAPISetImage", "Ptr", hAPI, "Ptr", hImg, "Int", image_width, "Int", image_height, "Int", , "Int", )

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 03 Aug 2021, 04:04
by swagfag
u loaded the image. either u know these properties ahead of time, or if theyre encoded in the image's metadata u can read them off, or u could derive them according to the image's format specification or u use whatever image introspection functions are available to u(if any, and presumably from the same set of APIs that allowed u to load the image in the first place)

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 03 Aug 2021, 04:15
by malcev
Should I pass the image as a buffer?
You should pass pointer to a bit array of Your bitmap.

Code: Select all

bytes_per_pixel := bits_per_pixel/8
bytes_per_line := bytes_per_pixel*image_width

Re: Can anyone implement a simple tesseract dllcall example?  Topic is solved

Posted: 19 Oct 2021, 08:05
by MrHue
Thanks to swagfag I made sample script below to demonstrate use of using Tesseract to ocr a file and screenshot using pixReadMemBmp / TessBaseAPISetImage. Uncomment the line with out.bmp to see output of screenshot

Code: Select all

#NoEnv	; Don't check empty variables to see if they are environment variables (significantly improves performance)
; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=93120
; download to same folder
; https://github.com/tesseract-ocr/tessdata/blob/master/eng.traineddata
; https://github.com/nguyenq/tess4j/blob/master/src/main/resources/win32-x86-64/libtesseract500.dll (dont forget to Unblock)
; https://github.com/nguyenq/lept4j/blob/master/src/main/resources/win32-x86-64/liblept1820.dll (same)
; https://github.com/tesseract-ocr/test/blob/master/testing/phototest.tif

msgbox % ocr("phototest.tif")	; ocr file
msgbox % ocr()				; ocr screen
return

ocr(win_id="",x1=0,y1=0,x2="",y2="",whitelist="",bw="",v=20,leptonica=0)
{
	static hAPI, header
	if !hAPI
		LoadLibrary("libtesseract500.dll")
		, LoadLibrary("liblept1820.dll")
		, hAPI := DllCall("libtesseract500\TessBaseAPICreate", "Ptr")
		, DllCall("libtesseract500\TessBaseAPIInit3", "Ptr", hAPI, "AStr", A_ScriptDir, "AStr", "eng")
		, VarSetCapacity(header, 54, 0)
		, NumPut(0x360000, NumPut(1, NumPut(0x5FC64D42, header, "UInt"), "UInt"), "UInt")	; Bitmap file header (14 bytes)

	if whitelist
		DllCall("libtesseract500\TessBaseAPISetVariable", "Ptr", hAPI, "Str", "tessedit_char_whitelist", "Str", whitelist)

	if (win_id<>"") && FileExist(win_id)
		hPix := DllCall("liblept1820\pixRead", "AStr", win_id, "Ptr")
		, DllCall("libtesseract500\TessBaseAPISetImage2", "Ptr", hAPI, "Ptr", hPix)
		, str:= DllCall("libtesseract500\TessBaseAPIGetUTF8Text", "Ptr", hAPI, "AStr")
	else {
		if (win_id=="")
			win_id:=WinExist("A")
		if (x2=="")
			WinGetPos,,,x2,,ahk_id %win_id%
		if (y2=="")
			WinGetPos,,,,y2,ahk_id %win_id%

		x:=min(x1,x2), y:=min(y1,y2), w:=abs(x2-x1)+1, h:=abs(y2-y1)+1
		, bh := -h	; must be -h for Tesseract
		, NumPut(32, NumPut(1, NumPut(bh, NumPut(w, NumPut(40, header, 14, "uint"), "int"), "int"), "UShort"), 0, "UShort") ; bitmap info for CreateDIB (40 bytes) positive h for Capture2Text to work
		, src := DllCall("user32.dll\GetDCEx", "Ptr", win_id, "Ptr", 0, "UInt", 3, "Ptr")
		, dst := DllCall("CreateCompatibleDC", "Ptr", src, "Ptr")
		, DIB := DllCall("CreateDIBSection", "Ptr", dst, "Ptr", &header+14, "UInt", 0, "Ptr*", pbits, "Ptr", 0, "UInt", 0, "Ptr")
		, DllCall("SelectObject", "Ptr", dst, "Ptr", DIB, "Ptr")
		, DllCall("BitBlt", "ptr", dst, "int", 0, "int", 0, "int", w, "int", h, "ptr", src, "int", x, "int", y, "Uint", 0xCC0020)
		if (bw<>"")
		{
			b:=(bw>>16)&x0xFF, g:=(bw>>8)&0xFF, r:=bw&0xFF
			Loop % w*h
			{
				Addr := pbits + A_Index*4
				if (abs(r - NumGet(Addr+2, 0, "UChar"))<=v)
				&& (abs(g - NumGet(Addr+1, 0, "UChar"))<=v)
				&& (abs(b - NumGet(Addr+0, 0, "UChar"))<=v)
					Numput(0, Addr+0, "UInt")
				else	Numput(0xFFFFFF, Addr+0, "UInt")			
			}
		}
		if leptonica
		{
			hData := DllCall("GlobalAlloc", "uint", 0x2, "uint", 54 + w*h*4, "ptr")
			, pData := DllCall("GlobalLock", "ptr", hData, "ptr")
			, DllCall("RtlMoveMemory", "ptr", pData + 0, "ptr", &header, "UInt", 54)		; destination, source, length
			, DllCall("RtlMoveMemory", "ptr", pData + 54, "ptr", pbits, "UInt", w*h*4)
			, hPix := DllCall("liblept1820\pixReadMemBmp", "Ptr", pData, "UInt", 54+w*h*4, "Ptr")
			, DllCall("libtesseract500\TessBaseAPISetImage2", "Ptr", hAPI, "Ptr", hPix)
			; , out := FileOpen("out.bmp", "w"), out.Rawwrite(pData+0,54+w*h*4), out.Close()
			, DllCall("GlobalUnlock", "ptr", hData)		; don't unlock immediately after rtlmovememory, wait a little bit
		} else	DllCall("libtesseract500\TessBaseAPISetImage", "Ptr", hAPI, "Ptr", pbits, "UInt", w, "UInt", h, "UInt", bytes_per_pixel := 4, "UInt", bytes_per_line := 4*w)
		str := DllCall("libtesseract500\TessBaseAPIGetUTF8Text", "Ptr", hAPI, "AStr")

		DllCall("DeleteDC", "ptr", dst)
		, DllCall("DeleteObject", "ptr", dib)
		, DllCall("ReleaseDC", "ptr", win_id, "ptr", src)
	}
	return str
}

LoadLibrary(dll)
{
	if !hModule:=DllCall("LoadLibrary", "Str", dll, "Ptr")
		msgbox % "LoadLibrary(" dll "): " GetLastError(A_LastError)
	return hModule
}

GetLastError(Error=0)
{
	VarSetCapacity(ErrorString, 1024)
	IfEqual, Error, 0, SetEnv, Error, %A_LastError%
	if DllCall("FormatMessage" 
		, "UINT", 0x1000	; FORMAT_MESSAGE_FROM_SYSTEM: The function should search the system message-table resource(s) for the requested message. 
		, "PTR", 0		; A handle to the module that contains the message table to search. 
		, "UINT", Error
		, "UINT", 0             ; Language-ID is automatically retreived 
		, "Str",  ErrorString 
		, "UINT", 1024          ; Buffer-Length 
		, "STR", "")		; "str",  "")            ;An array of values that are used as insert values in the formatted message. (not used) 
		return ErrorString
}

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 19 Oct 2021, 16:22
by teadrinker
swagfag wrote: MsgBox % DllCall("libtesseract500\TessBaseAPIGetUTF8Text", "Ptr", hAPI, "AStr")
This is most likely incorrect, should be

Code: Select all

pStr := DllCall("libtesseract500\TessBaseAPIGetUTF8Text", "Ptr", hAPI, "Ptr")
MsgBox, % StrGet(pStr, "UTF-8")

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 30 Nov 2022, 22:56
by andreas_s_
Can somebody help me,

1. Where to get liplept dll ?
2. i cant load tesseract dll and always got error code 126, how to resolve that ?

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 01 Dec 2022, 00:58
by malcev

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 01 Dec 2022, 01:38
by andreas_s_
Thanks @malcev now its working,

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 17 Apr 2023, 04:07
by swagfag
teadrinker wrote:
19 Oct 2021, 16:22
swagfag wrote: MsgBox % DllCall("libtesseract500\TessBaseAPIGetUTF8Text", "Ptr", hAPI, "AStr")
This is most likely incorrect, should be

Code: Select all

pStr := DllCall("libtesseract500\TessBaseAPIGetUTF8Text", "Ptr", hAPI, "Ptr")
MsgBox, % StrGet(pStr, "UTF-8")
wasnt really the point of the example, but yes its incorrect. should be freed with:

Code: Select all

DllCall("libtesseract500\TessDeleteText", "Ptr", pStr)

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 31 May 2023, 13:21
by ananthuthilakan

Code: Select all

~ObjectCache(): WARNING! LEAK! object 00000000009AE440 still has count 1
can be fixed by

Code: Select all

DllCall("libtesseract531\TessBaseAPIDelete","Ptr", hAPI)

Re: Can anyone implement a simple tesseract dllcall example?

Posted: 05 Jun 2023, 12:33
by malcev
I free like this:

Code: Select all

DllCall("libtesseract500\TessDeleteText", "ptr", pStr)
DllCall("libtesseract500\TessBaseAPIClear", "ptr", hApi)
DllCall("libtesseract500\TessBaseAPIEnd", "ptr", hApi)
DllCall("libtesseract500\TessBaseAPIDelete", "ptr", hApi)