Differenze tra le versioni di "Eseguire una scansione e riconoscimento caratteri (OCR) con l'API di Sane, Tesseract e Leptonica"

Da Gambas-it.org - Wikipedia.
 
Riga 3: Riga 3:
 
In particolare con le funzioni esterne della libreria ''libsane'' si eseguirà la scansione attraverso uno scanner; con le funzioni esterne della libreria ''libleptonica'' si caricherà l'immagine ottenuta dalla scansione del documento, e con le funzioni esterne della libreria ''libtesseract'' si eseguirà il riconoscimento ottico dei caratteri presenti nell'immagine caricata.
 
In particolare con le funzioni esterne della libreria ''libsane'' si eseguirà la scansione attraverso uno scanner; con le funzioni esterne della libreria ''libleptonica'' si caricherà l'immagine ottenuta dalla scansione del documento, e con le funzioni esterne della libreria ''libtesseract'' si eseguirà il riconoscimento ottico dei caratteri presenti nell'immagine caricata.
  
E' necessario avere installate nel sistema e richiamare in Gambas le seguenti librerie condivise:
+
E' necessario avere installate nel sistema e richiamare in Gambas le seguenti librerie condivise: "''libsane.so.1.3.1'' ", "''libtesseract.so.5.0.3'' " e "''libleptonica.so.6.0.0'' ".
<BR>"''libsane.so.1.3.1'' ", "''libtesseract.so.5.0.3'' " e "''libleptonica.so.6.0.0'' ".
 
  
 
Mostriamo un esempio pratico:
 
Mostriamo un esempio pratico:

Versione attuale delle 13:03, 8 giu 2024

Usando le risorse esterne del API di Sane, Tesseract e Leptonica è possibile eseguire una scansione con la Scanner e il successivo riconoscimento caratteri (OCR) di un documento testuale.

In particolare con le funzioni esterne della libreria libsane si eseguirà la scansione attraverso uno scanner; con le funzioni esterne della libreria libleptonica si caricherà l'immagine ottenuta dalla scansione del documento, e con le funzioni esterne della libreria libtesseract si eseguirà il riconoscimento ottico dei caratteri presenti nell'immagine caricata.

E' necessario avere installate nel sistema e richiamare in Gambas le seguenti librerie condivise: "libsane.so.1.3.1 ", "libtesseract.so.5.0.3 " e "libleptonica.so.6.0.0 ".

Mostriamo un esempio pratico:

Private Const BUFFER As Integer = 65536
Private Const MODUS As String = "Color"   ' Color  -  Gray  -  Lineart
Private RISOLUZIONE As Integer = 300
Private Const OCR_BOOL As Boolean = True

Library "libsane:1.3.1"

Public Struct SANE_Option_Descriptor
  name As Pointer
  title As Pointer
  desc As Pointer
  type As Integer
  unit As Integer
  size As Integer
  cap As Integer
  constraint_type As Integer
  union As Pointer
End Struct

Public Struct SANE_Parameters
  sformat As Integer
  last_frame As Integer
  bytes_per_line As Integer
  pixels_per_line As Integer
  lines As Integer
  depth As Integer
End Struct

Private Enum SANE_ACTION_GET_VALUE = 0, SANE_ACTION_SET_VALUE, SANE_ACTION_SET_AUTO

' SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
' This function must be called before any other SANE function can be called.
Private Extern sane_init(version_code As Pointer, authorize As Pointer) As Integer

' SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
' To query the list of devices that are available.
Private Extern sane_get_devices(device_list As Pointer, local_only As Boolean) As Integer

' SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle)
' To establish a connection to a particular device.
Private Extern sane_open(devicename As String, handle As Pointer) As Integer

' SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info)
' To set or inquire the current value of option number n of the device represented by handle h.
Private Extern sane_control_option(handle As Pointer, option As Integer, action As Integer, value As Pointer, info As Pointer) As Integer

' const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
' To access option descriptors.
Private Extern sane_get_option_descriptor(handle As Pointer, option As Integer) As Pointer

' SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
' To obtain the current scan parameters.
Private Extern sane_get_parameters(handle As Pointer, params As SANE_Parameters) As Integer

' SANE_Status sane_start (SANE_Handle handle)
' Initiates aquisition of an image from the device represented by handle h.
Private Extern sane_start(handle As Pointer) As Integer

' SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length)
' Reads image data from the device represented by handle h.
Private Extern sane_read(handle As Pointer, data As Pointer, max_length As Integer, length As Pointer) As Integer

' void sane_cancel (SANE_Handle handle)
' Cancels the currently pending operation of the device.
Private Extern sane_cancel(handle As Pointer)

' void sane_close (SANE_Handle h)
' Terminates the association between the device handle passed in argument h and the device it represents.
Private Extern sane_close(h As Pointer)

' void sane_exit (void)
' To terminate use of a backend.
Private Extern sane_exit()


Public Sub Main()
 
 Dim i, n, maxlun, lun As Integer
 Dim dev, hnd, md, buf, plun As Pointer
 Dim nom As String
 Dim sod As SANE_Option_Descriptor
 Dim par As New SANE_Parameters
 Dim fl As File
 
 Write "\e[5mRestare in attesa...\e[0m"
 Flush
 
 i = sane_init(0, 0)
 If i > 0 Then GestErrore("sane_init()")

 i = sane_get_devices(VarPtr(dev), False)
 If i > 0 Then GestErrore("sane_get_devices()")
 
 While Pointer@(dev + i) > 0
   nom = String@(Pointer@(Pointer@(dev + i)))
   i += 8
 Wend
 Write "\rDispositivo:     \e[31m" & nom

 i = sane_open(nom, VarPtr(hnd))
 If i > 0 Then
   GestErrore("sane_open()")
   saneUscita(hnd)
 Endif

 i = sane_control_option(hnd, 0, SANE_ACTION_GET_VALUE, VarPtr(n), 0)
 If i > 0 Then
   GestErrore("sane_control_option()")
   saneUscita(hnd)
 Endif
 
 Print "\n\n\e[0mPossibili opzioni:\e[32m"
 For i = 0 To n - 1
   sod = sane_get_option_descriptor(hnd, i)
   If IsNull(String@(sod.name)) Then Continue
   Print "   " & String@(sod.name)
   Select Case String@(sod.name)
     Case "mode"                    ' Color  -  Gray  -  Lineart
       md = Alloc(MODUS)
       sane_control_option(hnd, i, SANE_ACTION_SET_VALUE, md, 0)
     Case "resolution"
       sane_control_option(hnd, i, SANE_ACTION_SET_VALUE, VarPtr(RISOLUZIONE), 0)
   End Select
 Next

 i = sane_get_parameters(hnd, par)
 If i > 0 Then
   GestErrore("sane_get_parameters()")
   saneUscita(hnd)
 Endif

 With par
   Dim px As Byte = .bytes_per_line / .pixels_per_line
   Dim rgb As String = " byte)"
   If px == 3 Then rgb = " byte = RGB)"
   Print "\n\e[0mColonne:          "; .pixels_per_line; " (1 colonna = 1 pixel = "; px; RGB
   Print "Righe:            "; .lines
   Print "Byte per riga:    "; .bytes_per_line; " (colonne x "; px; Left(rgb, 5); ")"
   Print "Bit per campione: "; .depth
   Print "Modalità:        "; MODUS
   Print "Risoluzione:     "; RISOLUZIONE; " dpi\n"
 End With
 
 i = sane_start(hnd)
 If i > 0 Then
   GestErrore(hnd, "sane_start()")
   saneUscita(hnd)
 Endif

 buf = Alloc(SizeOf(gb.Byte), BUFFER)
 plun = Alloc(SizeOf(gb.Integer), 1)
 maxlun = BUFFER

 fl = Open "/tmp/imm" For Create

 Repeat
   i = sane_read(hnd, buf, maxlun, plun)
   Select Case i
     Case 1
       GestErrore("sane_read()")
       saneUscita(hnd)
     Case 3 To 4
       GestErrore("sane_read()")
       saneUscita(hnd)
     Case 6 To 11
       GestErrore("sane_read()")
       saneUscita(hnd)
   End Select
   lun = Int@(plun)
   Write #fl, buf, lun
 Until lun = 0

 CreaFileImmagine(par)
 
' Libera la memoria precedentemente allocata:
 fl.Close
 Free(md)
 Free(plun)
 Free(buf)
 saneUscita(hnd)

' Chiama la funzione per il riconoscimento OCR:
 If OCR_BOOL = True Then Ocr()
 
End

 
Private Procedure GestErrore(fnz As String)

 Error.Raise("Errore alla funzione: " & fnz)

End


Private Procedure saneUscita(handle As Pointer)

 sane_cancel(handle)
 sane_close(handle)
 sane_exit()

End

Private Procedure CreaFileImmagine(prmt As SANE_Parameters)

 Dim s, finale, mg As String
 Dim pro, p4, colonne, righe As Integer
 Dim bBN As Byte

 pro = prmt.lines * prmt.bytes_per_line
 s = File.Load("/tmp/imm")

 Print "Dati grezzi: "; Len(s); " byte (Colonne x Righe x 3 byte_RGB)"

 colonne = prmt.pixels_per_line
 righe = prmt.lines

 Select Case MODUS
   Case "Color"
     mg = "P6 "
' Se il numero dei dati grezzi è inferiore a (Colonne x Righe) x 3, allora colma la differenza:
     If Len(s) < ((pro / 8) * 3) Then s = s & String(((pro) * 3) - Len(s), Chr(255))
' Se invece il numero dei dati grezzi è maggiore di (Colonne x Righe) x 3, allora colma la differenza, ma tagliando i dati grezzi dalla fine:
     If Len(s) > ((pro) * 3) Then s = Left(s, pro * 3)
   Case "Gray"
     mg = "P5 "
' Se il numero dei dati grezzi è inferiore a Colonne x Righe, allora colma la differenza:
     If Len(s) < (pro) Then s = s & String((pro) - Len(s), Chr(255))
' Se invece il numero dei dati grezzi è maggiore di Colonne x Righe, allora colma la differenza, ma tagliando i dati grezzi dalla fine:
     If Len(s) > (pro) Then s = Left(s, pro)
   Case "Lineart"
     mg = "P4 "
' Se il numero dei dati grezzi è inferiore a (Colonne x Righe) / 8, allora colma la differenza:
     If Len(s) < (pro / 8) Then
       p4 = pro \ 8
       s = s & String$(p4 - Len(s), Chr(255))
     Endif
' Se il numero dei dati grezzi è maggiore di (Colonne x Righe) / 8, allora colma la differenza:
     If Len(s) > (pro / 8) Then
       For bBN = 1 To 8
         If (Len(s) * 8) Mod (prmt.lines + bBN) == 0
           colonne = prmt.lines + bBN
           righe = Len(s) * 8 / prmt.lines
         Endif
       Next
     Endif
 End Select
' Quindi, crea l'intero file .PNM finale, costituito da:
' l'header:
'   - numero Magico + spazio;
'   - numero colonne;
'   - spazio;
'   - numero righe;
'   - spazio;
'   - profondità colore;
'   - chiusura con "salto di riga";
' i dati grezzi dell'immagine scansita;
' le informazioni e commento finale:
 finale = mg & CStr(colonne) & Chr(&20) & CStr(righe) & " 255" & Chr(10) & s & Chr(10) &
          "# file creato da " & Application.Name & " - " & CStr(Now)
' ...e lo salva:
 File.Save("/tmp/scansione.pnm", finale)
 
End


Public Struct Pix
  w As Integer
  h As Integer
  d As Integer
  wpl As Integer
  refcount As Integer
  xres As Integer
  yres As Integer
  informat As Integer
  text As Pointer
  colormap As Pointer
  data As Pointer
End Struct

Library "libtesseract:5.0.3"

' TessBaseAPI* TessBaseAPICreate()
Private Extern TessBaseAPICreate() As Pointer

' int TessBaseAPIInit3(TessBaseAPI* handle, const char* datapath, const char* language)
Private Extern TessBaseAPIInit3(handle As Pointer, datapath As String, language As String) As Integer

' void TessBaseAPISetImage2(TessBaseAPI* handle, struct Pix* pix)
Private Extern TessBaseAPISetImage2(handle As Pointer, pix As Pix)

' void TessBaseAPISetSourceResolution(TessBaseAPI* handle, int ppi)
Private Extern TessBaseAPISetSourceResolution(handle As Pointer, ppi As Integer)

' int TessBaseAPIRecognize(TessBaseAPI* handle, ETEXT_DESC* monitor)
Private Extern TessBaseAPIRecognize(handle As Pointer, monitor As Pointer) As Integer

' char* TessBaseAPIGetUTF8Text(TessBaseAPI* handle)
Private Extern TessBaseAPIGetUTF8Text(handle As Pointer) As String

' void TessBaseAPIEnd(TessBaseAPI* handle)
Private Extern TessBaseAPIEnd(handle As Pointer)

' void TessBaseAPIDelete(TessBaseAPI* handle)
Private Extern TessBaseAPIDelete(handle As Pointer)


Library "libleptonica:6.0.0"

' PIX * pixRead (const char *filename)
Private Extern pixRead(filename As String) As Pix

' void pixDestroy (PIX **ppix)
Private Extern pixDestroy(ppix As Pointer)
 
 
Private Procedure Ocr()
 
 Dim err As Integer
 Dim tes, p As Pointer 
 Dim pis As Pix
 Dim s As String
 
 pis = pixRead("/tmp/scansione.pnm")
 
 tes = TessBaseAPICreate()
 If tes == 0 Then Error.Raise("Errore !")
 
 err = TessBaseAPIInit3(tes, Null, "eng")
 If err <> 0 Then Error.Raise("Errore !")
 
 TessBaseAPISetImage2(tes, pis)
 Write "Riconoscimento caratteri.\n\e[5m  Restare in attesa...\e[0m\r"
 Flush
 
 TessBaseAPISetSourceResolution(tes, RISOLUZIONE)
 
 err = TessBaseAPIRecognize(tes, 0)
 If err <> 0 Then Error.Raise("Errore !")
 
' Scansione OCR:
 s = TessBaseAPIGetUTF8Text(tes)
 Print Space(Len("  Restare in attesa..."))
 Print
 Print s
 
 TessBaseAPIEnd(tes)
 TessBaseAPIDelete(tes)
 pixDestroy(Object.Address(pis) + (SizeOf(gb.Pointer) * 3))
 
End