Liczba danych przetwarzanych obecnie przez maszyny jest ogromna. Szacuję się ze każdego dnia jest tworzone 2,5 kwintyliona bitów danych w tym m.in filmów, muzyki, książek jak i dokumentów. I choć wiele z tych danych powstaje bezpośrednio na komputerze nadal nie rzadko musimy pozyskać informacje z nośników analogowych. Nawet zaś wtedy edycja tych danych nie zawsze jest łatwa jak w przypadku wszelkich zeskanowanych dokumentów. W takim przypadku jednak mogą nam pomóc programy typu OCR. Przy odrobinie zaś wysiłku możemy samemu taki program stworzyć.
Niestety w Javie właściwie nie istnieje darmowa i dobra bibliotek do rozpoznawania tekstu. Jednak to nie powód by rezygnować z naszego przedsięwzięcia. Istnieją bowiem ciekawe rozwiązania w innych językach. Jednym z najlepszych może być biblioteka Tesseract napisana w C++.
Tesseract jest darmową biblioteką do rozpoznawania tekstu nad którą prace rozpoczęto już w 1985 roku i prace nad nim trwają do dziś. Od 2005 zaś jest udostępniany na licencji open source i jest możliwy do pobrania z githuba pod tym adresem httpss://github.com/tesseract-ocr. Obecnie domyślna wersja Tesseracta jest wytrenowana w oparciu o ponad 400000 lin tekstu i rozpoznaje około 4500 czcionek dając dużą skuteczność przy rozpoznawaniu języków łacińskich. Dodatkowo jeśli będziemy pracować z nietypowym tekstem jesteśmy w stanie go samemu przeszkolić. Pomimo zaś tego że jest to biblioteka napisana w C++ istnieje darmowy wrapaer Javowy korzystający z JNI. Wraper ten wykorzystamy w naszym programie (https://tess4j.sourceforge.net).
Żeby stworzyć prosty program typu OCR ściągamy bibliotek tesseract i instalujemy httpss://github.com/UB-Mannheim/tesseract/wiki (Windows). Następnie tworzymy nowy projekt i dodajemy te dwie zależności do mavena:
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.im4java</groupId>
<artifactId>im4java</artifactId>
<version>1.4.0</version>
</dependency>
Kod źródłowy wygląda następująco:
public class App
{
public static void main( String[] args ) throws IOException, InterruptedException, IM4JavaException
{
File imageFile = new File("C:\\Users\\Sariel\\Desktop\\moby.png");
ITesseract instance = new Tesseract(); // JNA Interface Mapping
instance.setDatapath("C:\\Program Files (x86)\\Tesseract-OCR\\tessdata");
ConvertCmd cmd = new ConvertCmd();
cmd.run(op);
try {
String result = instance.doOCR(imageFile);
System.out.println(result);
} catch (TesseractException e) {
System.err.println(e.getMessage());
System.out.println("reading failed");
}
}
}
Kod jest stosunkowo prosty otwieramy interesujący nas obraz. Tworzymy nową instancję tesseracta. Wskazujemy miejsce gdzie zainstalowaliśmy bibliotekę po czym dokonujemy rozpoznawania tekstu poprzez metode doOCR().
Dla celów testowych korzystałem z niniejszego fragmentu książki „Moby Dick”
Powinniśmy otrzymać taki wynik:
men swung in the howlines; still wordless Ahab stood up to the blast.
Even when Wearied nature seemed demanding repose he would not
seek that repose in his hammock. Never could Starbuck forget the
old man's aspect, when one night going down into the cabin to mark
how the barometer stood, he saw him with closed eyes sitting straight
in his floor‘screwed chair; the rain and hnlflmelted sleet of the storm
from which he had some time before emerged, still slowly dripping
from the unremoved hat and coat. On the table beside him lay un'
rolled one of those charts of tides and currents which have previously
been spoken of. His lantern swung from his tightly clenched hand
Though the body was erect, the head was thrown back so that the
closed eyes were pointed towards the needle of the tell'tale that
swung from a beam in the ceiling.’
Terrible old man! thought Starbuckwith a. shudder, sleepingin this
gale, still thou steadfastly eyest thy purposes
CHAPTER 52 THE ALBATROSS
OUTH'EASTWARD from the Cape, ed" the distant Crozetts,
a goodcruising ground for Right Whalemen,asail loomedahead,
S the Goney (Albatross) by name. As she slowly drew nigh, from
my lofty perch at the fore'mast—head, I had a good view of that sight
so remarkable to a tyre in the far ocean fisheries—awhaler at sea, and
long absent from home.
As if the waves had been fullers, this craft was bleached like the
skeleton of a stranded walrus. All down her sides, this spectral ap»
pearance was traced with long channels of reddened rust, While all
her spars and her rigging were like the thick branches of trees furred
over with hoar'frost. Only her lower sails were set. A wild sight it
was to see her longfbearded lookouts at those three mast—heads. They
seemed clad in the skins of beasts,so torn and hepatched the raiment
that had survived nearly four years of cruising Standing in iron
‘The cabin—compass is called the tell'ule, because without going to the campus at the
helm,the Captain, while below, can inform himselfof the course ofthe ship.'
Jeśli chcemy możemy także skorzystać z linii komend umieszczamy w takim wypadku nasz dokument w folderze tesseracta, przechodzimy do tego folderu i korzystamy z niniejszej komendy:
tesseract moby.png resultText
gdzie moby.png to adres sczytywanego pliku a resultText to wynik w formacie txt
W zasadzie to już wszystko co potrzebujemy aby mieć nasze proste oprogramowanie typu OCR. Niemniej wielokrotnie może się zdarzy ze nasze skany czy obrazy źródłowe nie będą tak łatwe do rozpoznania przez bibliotekę tesseract zmniejszając jego skuteczność. Jest jednak wiele rzeczy które możemy uczynić aby poprawić jego skuteczność.
Żeby zwiększyć skuteczność rozpoznawania tekstu przez bibliotekę teseract możemy
– dokonać binaryzacji obrazu
– zwiększyć rozdzielczość obrazu, wielkość tekstu
– wyrównać/obrócić obraz
– pozbyć się obramowania
– pozbyć się szumu
Żeby dokonać zmian w dokumencie możemy skorzystać z dowolnej biblioteki graficznej czy też nawet photoshopa lub gimpa choć to wymagało by zapewne ręcznego edytowania każdego skanu. Osobiście polecam image magick i na jego podstawie pokaże jak można dokonać drobnych poprawek obrazu.
Image magick jest dostępny do ściągnięcia pod adresem:
httpss://www.imagemagick.org/script/download.php
Instalujemy oprogramowanie wraz z wszystkimi dodatkami jak install legacy utilities, install development headers and liberies for c and c++ itd. Dodajemy image magick do zmiennych środowiskowych:
Nazwa zmiennej: MAGICK_HOME
Wartość zmiennej: C:\Program Files\ImageMagick-7.0.5-Q16
w linii komend poleceniem
convert -version
możemy sprawdzić czy instalacja się udała, powinniśmy otrzymać informacje o wersji image magick.
Mając image magick zainstalowany możemy przygotować nasz obraz pod rozpoznawanie tekstu:
1) Binaryzacji obrazu czyli przekształcenie obrazu na czerń i biel. Możemy to uzyskać poprzez prostą komendę:
convert book.png -monochrome book2.png
book.png – plik źródłowy
-monochrome – komenda przekształcająca obraz na czerń oraz biel
book2.png – obraz wynikowy
Najlepszym wyjściem jest gdy otrzymamy czarny czytelny tekst na białym tle. Jednak nierzadko uzyskany obraz wymaga dalszej obróbki.
2) Zalecane jest aby obrazy w tesseracu miały przynajmniej rozdzielczość 300 dpi dlatego możemy skorzystać z następującej komendy:
convert -units PixelsPerInch dpi.png -density 300 dpiresult.png
Warto także pamiętać iż jeśli wielkość naszej czcionki jest niższa niż 10pt efektywność rozpoznawania obrazu poprzez tesseract może znacznie spaść.
3) Wyrównanie/obrócenie obrazu, wielokrotnie zeskanowany przez nasz obraz może być odrobinę odwrócony co utrudnia rozpoznanie poszczególnych liter. Obrócenie obrazu możemy uzyskać poprzez poniższa komendę:
convert rotate.png -rotate -3 rotate2.png
4)Pozbycie się obramowania. Częstokrotnie podczas skanowania możemy posiadać czarną obwódkę wokół obrazu która może utrudniać czytanie tekstu. Możemy się jej pozbyć na kilka sposobów:
Korzystając z metody trim która służy właśnie do usuwania czarnego obramowania
convert black_border.png -trim tes_out.png
Niestety częstokrotnie poziom czerni może być nie równy na całym arkuszu, domyślnie zaś program wyszukuje jedynie dokładnie jeden wybrany kolor z tego powodu możemy skorzystać z metody -fuzz która pozwala nam znaleźć pobliski kolor.
convert black_border2.png -fuzz 40% -trim tes_out.png
Jeśli obwódka jest stałego rozmiaru możemy ją po prostu przyciąc korzystając z metody crop lub shave
convert black_border2.png -crop 300x500+15+12 tes_out.png
convert black_border2.png -crop 300x500-15-12 tes_out.png
convert black_border2.png -shave 15x15 tes_out.png
Nierzadko obwódka znajduję się tylko po jednej stronie, jest nie jednolita i nie uda nam się jej usunąć metodą trim w takim wypadku możemy skorzystać z niniejszej komendy:
convert myborder.png -bordercolor black -border 1 -fuzz 95% -fill white -draw "color
0,0 floodfill" noborder.png
5) Ostatnia rzecz jaka pozostała to pozbywanie się szumu z obrazu. Niestety jest to najcięższy element i zależy w dużym stopniu od rodzaju szumu jaki mamy na obrazie. W celu jego nieusunięcia możemy wykorzystać np. rozmycie, podjaśnić obraz, dodać kontrast, czy skorzystać z innych filtrów. Przykładowo jeśli posiadamy szum w postaci białych i czarnych kropek dobrym wyjsciem może być skorzystanie z filtru medianowego:
convert ship.png -median 5 ship2.png
Inne przydatne komendy:
convert image.png -adaptive-blur 5 image2.png // dodanie rozmycia do obrazu
convert image.png -negate image2.png // zamiana/negacja kolorów
convert image.png -noise image2.png // dodanie szumu
convert image.png -white-threshold 20000 image2.png // zamienia wszystkie piksele powyżej podanej wartości na białe, reszta pikseli pozostaje bez zmian
Lista wszystkich komend wraz z ich opisami znajduję się pod adresem httpss://www.imagemagick.org/script/command-line-options.php
Te same funkcje możemy wykorzystać w naszym javovym programie musimy jedynie dodać kolejny wraper poprzez mavena tym razem dla Image Magick:
<dependency>
<groupId>org.im4java</groupId>
<artifactId>im4java</artifactId>
<version>1.4.0</version>
</dependency>
Przykładowy kod zaś mógłby wyglądać następująco, wszystkie metody działają tak samo jak zostało to opisane powyżej przy wykorzystaniu linii komend:
ProcessStarter.setGlobalSearchPath("C:\\Program Files\\ImageMagick-7.0.5-Q16");
ConvertCmd cmd = new ConvertCmd();
IMOperation op = new IMOperation();
op.addImage("C:\\Users\\Sariel\\Desktop\\image.png");
op.density(300);
op.whiteThreshold(90d*256);
op.monochrome();
op.rotate(-1d);
op.trim().fuzz(40d);
op.blur(6d);
op.addImage("C:\\Users\\Sariel\\Desktop\\testResult");
cmd.run(op);
Istnieje także dobry gotowy skrypt pod image magick do przygotowywania obrazów dla OCR o nazwie textcleaner, działający jednak jedynie pod Linuxem bądź Windowsem z zainstalowanym cygwinem:
https://www.fmwconcepts.com/imagemagick/textcleaner/
Dzięki tylko jednej komendy możemy zrobić wszystko to co zostało opisane powyżej jak i więcej dając nam bardzo dobre rezultaty.
Nic nie stoi też na przeszkodzie byśmy samemu zaimplementowali niektóre rozwiązania. Samemu napisałem prostą funkcje która szczytuję wszystkie piksele z obrazu i zamienia je według naszych potrzeb:
public static void readPixels(String in, String out) throws IOException {
File img = new File(in);
BufferedImage image = ImageIO.read(img);
int w = image.getWidth();
int h = image.getHeight();
int[] dataBuffInt = image.getRGB(0, 0, w, h, null, 0, w);
int[] pixels = new int[dataBuffInt.length];
for (int a = 0; a < dataBuffInt.length; a++) {
Color c = new Color(dataBuffInt[a]);
if (c.getRed() > 0 && c.getRed() <= 50 && c.getGreen() > 0 && c.getGreen() <= 50 && c.getBlue() > 0
&& c.getBlue() <= 100) {
pixels[a] = -1;
} else {
pixels[a] = 65536 * 1 + 256 * 1 + 1;
}
}
BufferedImage img2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
img2.getRaster().setDataElements(0, 0, w, h, pixels);
File outputfile = new File(out);
ImageIO.write(img2, "png", outputfile);
}
Na koniec jako ciekawostka jest alternatywne wykorzystanie oprogramowania Tesseract oraz Image Magick w celu łamania prostych captch:
op.density(300);
op.median(2d);
op.negate();
op.whiteThreshold(90d*256);
op.monochrome();
op.adaptiveBlur(6d);
op.noise(4d);
Wykorzystując nasz program bez problemu możemy rozszyfrować pierwszą captche nie dokonując jakichkolwiek modyfikacji graficznych. Kolejne trzy wymagają już pewnych prac graficznych lecz po tym tesseract i z nimi sobie dobrze radzi. Zautomatyzowanie tego procesu zaś powoduję iż takie captche nie spełnia swojego zadania. Dlatego gdy tworzymy serwis internetowy korzystajmy z lepszych generatorów captchy. Niestety mimo nawet najlepszych poprawek graficznych czasami nie będziemy w stanie odczytać zeskanowane dokumentu lub też będą w nim błędy i konieczne będzie zrobienie lub pozyskanie lepszego skanu.
Poniżej można ściągnąć kod źródłowy gotowego programu stworzonego w Eclipse.
Kod zródłowy