de Berk Kaan Kuguoglu

Cum se utilizeaza preprocesarea imaginilor pentru a imbunatati precizia Tesseract

Cum se utilizează preprocesarea imaginilor pentru a îmbunătăți precizia Tesseract

Anterior, pe Cum să începeți cu Tesseract, Ți-am oferit un tutorial practic de pornire rapidă despre Tesseract folosind Python. Este o imagine de ansamblu destul de simplă, dar ar trebui să vă ajute să începeți cu Tesseract și să eliminați câteva obstacole cu care m-am confruntat când eram în pielea voastră. Acum sunt dornic să vă arăt câteva alte trucuri și lucruri pe care le puteți face cu Tesseract și OpenCV pentru a vă îmbunătăți precizia generală.

Unde am plecat data trecută?

În povestea anterioară, Nu m-am deranjat să intru în detalii în cea mai mare parte. Dar dacă ți-a plăcut prima poveste, aici vine continuarea! Deci, unde am plecat?

Ah, am avut o scurtă privire de ansamblu asupra redimensionării, eliminării zgomotului și binarizării. Acum, este timpul să treci la detalii și să-ți arăt câteva setări cu care poți juca.

Rescalare

Imaginile care sunt redimensionate sunt fie micșorate, fie mărite. Dacă sunteți interesat să vă micșorați imaginea, INTER_AREA este calea de urmat pentru tine. (Btw, parametrii fx și fy indicați factorul de scalare în funcția de mai jos.)

ad-banner
img = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

Pe de altă parte, ca în majoritatea cazurilor, poate fi necesar să vă scalați imaginea la o dimensiune mai mare pentru a recunoaște caracterele mici. În acest caz, INTER_CUBIC în general funcționează mai bine decât alte alternative, deși este și mai lent decât altele.

img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)

Dacă doriți să schimbați o parte din calitatea imaginii pentru o performanță mai rapidă, vă recomandăm să încercați INTER_LINEAR pentru mărirea imaginilor.

img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)

Estompare

Merită menționat faptul că există câteva filtre de estompare disponibile în Biblioteca OpenCV. Estomparea imaginii se obține de obicei prin convoluarea imaginii cu un nucleu de filtru trece-jos. În timp ce filtrele sunt de obicei utilizate pentru a estompa imaginea sau pentru a reduce zgomotul, există câteva diferențe între ele.

1. Medie

După ce convolți o imagine cu un filtru tip casetă normalizată, aceasta ia pur și simplu media tuturor pixelilor de sub zona nucleului și înlocuiește elementul central. Cred că se explică de la sine.

img = cv.blur(img,(5,5))

2. Estompare gaussiană

Acest lucru funcționează într-un mod similar cu Averaging, dar folosește nucleul Gaussian, în loc de un filtru de cutie normalizat, pentru convoluție. Aici, dimensiunile nucleului și abaterile standard în ambele direcții pot fi determinate independent. Estomparea Gaussiană este foarte utilă pentru eliminare – ghici ce? – zgomot gaussian din imagine. Dimpotriva, estomparea gaussiană nu păstrează marginile din intrare.

img = cv2.GaussianBlur(img, (5, 5), 0)

3. Estompare mediană

Elementul central din zona nucleului este înlocuit cu mediana tuturor pixelilor de sub nucleu. În special, acest lucru depășește alte metode de estompare în eliminarea zgomotului de sare și piper din imagini.

Estomparea mediană este un filtru neliniar. Spre deosebire de filtrele liniare, estomparea mediană înlocuiește valorile pixelilor cu valoarea mediană disponibilă în valorile vecinătății. Deci, estomparea mediană păstrează muchiile, deoarece valoarea mediană trebuie să fie valoarea unuia dintre pixelii vecini.

img = cv2.medianBlur(img, 3)

4. Filtrarea bilaterală

Vorbind despre menținerea muchiilor ascuțite, filtrarea bilaterală este destul de utilă pentru a elimina zgomotul fără a netezi marginile. Similar cu estomparea gaussiană, filtrarea bilaterală folosește și un filtru gaussian pentru a găsi media ponderată gaussiană în vecinătate. Cu toate acestea, ia în considerare diferența de pixeli în timp ce estompează pixelii din apropiere.

Astfel, se asigură că numai acei pixeli cu intensitate similară cu pixelul central sunt neclare, în timp ce pixelii cu valori distincte ale pixelilor nu sunt neclare. Procedând astfel, marginile care au variații de intensitate mai mari, așa-numitele margini, sunt păstrate.

img = cv.bilateralFilter(img,9,75,75)

În general, dacă sunteți interesat să păstrați marginile, mergeți cu estompare mediană sau filtrare bilaterală. Dimpotrivă, estomparea gaussiană este probabil să fie mai rapidă decât estomparea mediană. Datorită complexității sale de calcul, filtrarea bilaterală este cea mai lentă dintre toate metodele.

Din nou, tu te faci.

Pragul imaginii

Nu există o singură metodă de pragare a imaginii care să se potrivească tuturor tipurilor de documente. În realitate, toate filtrele funcționează diferit pe diferite imagini. De exemplu, în timp ce unele filtre binarizează cu succes unele imagini, este posibil să nu reușească să binarizeze altele. La fel, unele filtre pot funcționa bine cu acele imagini pe care alte filtre nu le pot binariza bine.

Voi încerca să abordez elementele de bază aici, deși vă recomand să citiți documentația oficială a OpenCV pe Pragul de imagine pentru mai multe informații și teoria din spatele ei.

1. Prag simplu

S-ar putea să vă amintiți un prieten de-al vostru care vă dădea câteva sfaturi despre viața dvs. spunând „lucrurile nu sunt întotdeauna alb-negru”. Ei bine, pentru un prag simplu, lucrurile sunt destul de simple.

cv.threshold(img,127,255,cv.THRESH_BINARY)

Mai întâi, alegeți o valoare prag, să spunem 127. Dacă valoarea pixelilor este mai mare decât pragul, aceasta devine neagră. Dacă este mai puțin, devine alb. OpenCV ne oferă diferite tipuri de metode de prag care pot fi transmise ca al patrulea parametru. De multe ori folosesc pragul binar pentru majoritatea sarcinilor, dar pentru alte metode de prag pe care le puteți vizita documentația oficială.

2. Prag adaptativ

În loc să setăm o valoare de prag global, lăsăm algoritmul să calculeze pragul pentru regiunile mici ale imaginii. Astfel, ajungem să avem diferite valori prag pentru diferite regiuni ale imaginii, ceea ce este minunat!

cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)

Există două metode adaptive pentru calcularea valorii pragului. In timp ce Adaptive Thresh Mean returnează media zonei de cartier, Media Gaussiană Adaptivă calculează suma ponderată a valorilor vecinătății.

Mai avem alți doi parametri care determină mărimea zonei de vecinătate și valoarea constantă care se scade din rezultat: al cincilea și respectiv al șaselea parametru.

3. Pragul lui Otsu

Această metodă funcționează în special bine cu imagini bimodale, care este o imagine a cărei histogramă are două vârfuri. Dacă acesta este cazul, am putea fi dornici să alegem o valoare prag între aceste vârfuri. Aceasta este ceea ce face de fapt Binarizarea lui Otsu.

cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

Este destul de util pentru unele cazuri. Dar este posibil să nu reușească să binarizeze imagini care nu sunt bimodale. Deci, vă rugăm să luați acest filtru cu un bob de sare.

Tipuri de praguri

Este posibil să fi observat deja că există un parametru sau, în unele cazuri, o combinație de câțiva parametri, care sunt trecuți ca argumente pentru a determina tipul de prag, cum ar fi THRESH_BINARY. Nu intru aici în detaliu, așa cum este explicat clar în documentația oficială.

Ce urmează?

Până acum, am discutat câteva dintre tehnicile de pre-procesare a imaginii. S-ar putea să vă întrebați exact când vă veți murdări mâinile. Ei bine, a sosit timpul. Înainte de a reveni la IDE-ul tău preferat Python – al meu este PyCharm, btw – Vă voi arăta câteva linii de cod care vă vor economisi ceva timp în timp ce încercați să găsiți ce combinație de filtre și manipulări de imagini funcționează bine cu documentele dvs.

Să începem prin definirea unei funcții de comutare care conține câteva combinații de filtre de prag și metode de estompare. Odată ce ați luat ideea, ați putea adăuga și mai multe filtre, încorporând alte metode de pre-procesare a imaginii, cum ar fi redimensionarea în setul de filtre.

Aici am creat 20 de combinații diferite de metode de pragare a imaginilor, metode de estompare și dimensiuni ale nucleului. Funcția de comutare, aplica_prag, ia două argumente, și anume imaginea OpenCV și un număr întreg care denotă filtrul. La fel, deoarece această funcție returnează imaginea OpenCV ca urmare, ar putea fi ușor integrată în get_string funcție din postarea anterioară.

def apply_threshold(img, argument):    switcher = {        1: cv2.threshold(cv2.GaussianBlur(img, (9, 9), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],        2: cv2.threshold(cv2.GaussianBlur(img, (7, 7), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],        3: cv2.threshold(cv2.GaussianBlur(img, (5, 5), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],
                              ...              
        18: cv2.adaptiveThreshold(cv2.medianBlur(img, 7), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2),        19: cv2.adaptiveThreshold(cv2.medianBlur(img, 5), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2),        20: cv2.adaptiveThreshold(cv2.medianBlur(img, 3), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)    }    return switcher.get(argument, "Invalid method")

Și, iată că vine.

def get_string(img_path, method):    # Read image using opencv    img = cv2.imread(img_path)    # Extract the file name without the file extension    file_name = os.path.basename(img_path).split('.')[0]    file_name = file_name.split()[0]    # Create a directory for outputs    output_path = os.path.join(output_dir, file_name)    if not os.path.exists(output_path):        os.makedirs(output_path)
    # Rescale the image, if needed.    img = cv2.resize(img, None, fx=1.5, fy=1.5, interpolation=cv2.INTER_CUBIC)
    # Convert to gray    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)    # Apply dilation and erosion to remove some noise    kernel = np.ones((1, 1), np.uint8)    img = cv2.dilate(img, kernel, iterations=1)    img = cv2.erode(img, kernel, iterations=1)
    # Apply threshold to get image with only black and white    img = apply_threshold(img, method)
    # Save the filtered image in the output directory    save_path = os.path.join(output_path, file_name + "_filter_" + str(method) + ".jpg")    cv2.imwrite(save_path, img)    # Recognize text with tesseract for python    result = pytesseract.image_to_string(img, lang="eng")
    return result

Ultimele cuvinte

Acum, tot ce trebuie să facem este să scriem o buclă simplă care iterează peste directorul de intrare pentru a colecta imagini și aplică fiecare filtru pe imaginile adunate. Prefer să folosesc glob, sau os, pentru colectarea imaginilor din directoare și argparse pentru a trece argumente prin terminal, așa cum ar face orice altă persoană sănătoasă.

Aici am făcut cam același lucru ca și în al meu esențial, dacă doriți să aruncați o privire. Cu toate acestea, nu ezitați să utilizați instrumentele cu care vă simțiți confortabil.

Până acum, am încercat să acopăr câteva concepte și implementări utile de pre-procesare a imaginilor, deși este probabil doar vârful aisbergului. Nu știu cât „timp liber” voi avea în săptămânile următoare, așa că nu vă pot oferi un anumit interval de timp pentru publicarea următoarei mele postări. Cu toate acestea, iau în calcul adăugarea a cel puțin încă o parte la această serie care explică câteva lucruri pe care le-am lăsat deoparte, cum ar fi rotația și deranjarea imaginilor.

Până atunci, cel mai bun pariu este să vă păstrați inteligența și să continuați să căutați semne.*