momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Recunoașterea textului și extragerea datelor unui orar școlar prezentat în format PDF (VI)

Bash | ImageMagick | Tesseract
2024 sep

[1] v. partea a IV-a

În [1] am antrenat Tesseract (rezultând hwr.traineddata) pe baza unui set de vreo 100 de exemple de recunoaștere corectă a unor litere sau cuvinte decupate de pe imaginile din CN.pdf și a unei liste "new_words.txt" a tuturor denumirilor (de discipline), prenumelor și numelor (de profesori) care apar pe orarele respective.
Aplicând tesseract cu "-l hwr", ne-a rezultat fișierul "CN.txt" conținând unul sub celălalt orarele tuturor claselor, așa cum au fost ele recunoscute (corect sau nu) de pe imagini. Următorul program $\mathbf{R}$ (anticipat în [1]-V prin "word_dis.R") constituie un "tabel" W care conține cuvintele distincte din CN.txt împreună cu frecvențele corespunzătoare și apoi evidențiază pe acelea care nu figurează în new_words.txt (deci sau reprezintă „stâlcit” un cuvânt din new_words.txt, sau eventual, reprezintă —corect sau nu— un cuvânt care ne-a scăpat totuși din vedere, când am formulat lista "tuturor" cuvintelor din orare):

library(tidyverse)
library(tidytext)  # https://cran.r-project.org/web/packages/
library(readtext)
W <- readtext("~/24sep/CN.txt") %>%
     unnest_tokens(output = word, input = text, to_lower=FALSE) %>%
     count(word, sort = TRUE) %>%
     as.data.frame()  # "fizica" 83, "romana" 81, "matematica" 77, "cds" 61 ...
dict <- readtext("~/24sep/new_words.txt")$text %>%
        strsplit(split="\n") %>% unlist()
     # "fizica" "romana" "matematica" "cds" "engleza" "Cojocaru" "Carmen" ...
wdf <- setdiff(W$word, dict) %>% sort()

Pe orarele inițiale un același cuvânt apare de obicei de mai multe ori — dar nu neapărat totdeauna, el este și corect recunoscut (ci este uneori „stâlcit”, fiindcă zonele de pixeli de pe care este „citit” diferă totuși, mai mult sau mai puțin, între ele); următoarea funcție primește un prefix de cuvânt și evidențiază variațiile de recunoștere existente pentru cuvântul respectiv:

words_with_prefix <- function(pre) {
    W %>% 
    filter(., substr(word, 1, nchar(pre)) == pre) %>%
    mutate(ground = !(word %in% wdf))
}
> words_with_prefix("Ho")
        word  n ground
     Hostiuc 19   TRUE
     Hostiue  7  FALSE
     Hostine  1  FALSE

De exemplu, cuvântul "Hostiuc" este cel corect (este și înregistrat în subdirectorul de exemple hwr-ground-truth/) și vedem mai sus că a fost recunoscut ca atare de 19 ori; însă de 8 ori, recunoașterea scrierii „de mână” a acestuia a eșuat (confundând ultima literă "c" cu "e" pe un număr de 7 imagini, sau pe o altă imagine, confundând "uc" cu "ne").

N-ar fi greu să depistăm în CN.txt o clasă și apoi zona din imaginea orarului acelei clase, de pe care tesseract a recunoscut "Hostiue" (în loc de "Hostiuc"); n-avem decât să decupăm zona respectivă și să o adăugăm exemplelor din hwr-ground-truth/ (împreună cu recunoașterea corectă a zonei respective, "Hostiuc"; v. [1]).
N-avem apoi decât să refolosim "make training", pentru a ne asigura că hwr.traineddata ține seama și de exemplul de recunoaștere tocmai adăugat; reluând tesseract, vom avea șansa ca "Hostiuc" să apară în "CN.txt" de $19+7=26$ ori (eliminând apariția "Hostiue").

În cele ce urmează ducem la extrem ideea evidențiată mai sus: izolăm și decupăm toate zonele de pixeli corespunzătoare cuvintelor existente pe imaginile orarelor, înregistrându-le pe toate în subdirectorul tipic "-ground-truth/" (ca exemple de recunoaștere corectă); apoi lansăm "make training". Ar fi de așteptat ca folosind fișierul ".traineddata" rezultat astfel, tesseract să recunoască acum fără greșală, toate orarele din imaginile respective.

vezi Cărţile mele (de programare)

docerpro | Prev | Next