Odczytywanie polskich znaków diakrytycznych za pomocą OCR

0

Czy wiecie może w jaki sposób w C# mogę odczytać polskie znaki diakrytyczne (ą, ę, itd.) za pomocą techniki OCR? Znalazłem biblioteczkę tesseract-ocr i wszystko działa pięknie, ale dla alfabetu łacińskiego. Z tego co się zorientowałem to były plany, aby ta biblioteczka odczytywała również polskie znaki, ale projekt już od nawa stoi w miejscu, a wersja która jest do ściągnięcia ma jeszcze spore problemy z prawidłowym odczytem. Czy macie jakieś doświadczenie w tej sprawie, by polecić inne rozwiązanie tego problemu, albo chociaż możecie coś zasugerować?

0

Zbudować wielowarstową sieć neuronową, nauczyć ją próbkami (sygnały) na ich wejścia.

5

Zbudować wielowarstową sieć neuronową, nauczyć ją próbkami (sygnały) na ich wejścia.

  1. Poważnie, ktoś kto buduje jakąś aplikacje użytkową ma sobie wszystkie takie problemy rozwiązywać sam? Takie rzeczy robią w instytutach naukowych, a nie programiści w normalnych firmach. Gdyby wszyscy tak robili to siedzielibyśmy dalej w jaskiniach.
  2. To nic nie da, sieć neuronowa jest za słabym modelem do robienia OCR (OCR tekstu, nie pojedynczego znaku).

A odnośnie tematu:

Czy wiecie może w jaki sposób w C# mogę odczytać polskie znaki diakrytyczne (ą, ę, itd.) za pomocą techniki OCR?

Znajdź jakąś bibliotekę (chyba że jesteś zdeterminowany bardzo do napisania własnej, ale wątpię). W przypadku C# w zasadzie z darmowych i działających masz do wyboru tylko tesseract.

Znalazłem biblioteczkę tesseract-ocr i wszystko działa pięknie, ale dla alfabetu łacińskiego.

Bo potrzebujesz polskich datasetów (tessdata) żeby działać na polskich tekstach. Możesz sobie je wygenerować sam, albo poszukać gotowych zrobionych przez jakichś miłych ludzi. Patrz np - https://code.google.com/p/tesseract-polish/

Z tego co się zorientowałem to były plany, aby ta biblioteczka odczytywała również polskie znaki

Biblioteka jest dość generyczna, wystarczy ją nauczyć (są narzędzia od tego) nowego języka/zbioru znaków i może działać.

ale projekt już od nawa stoi w miejscu, a wersja która jest do ściągnięcia ma jeszcze spore problemy z prawidłowym odczytem.

A nieprawda, od kiedy go google podchwyciło to miał drugą młodość chwilowo. Za to wrappery tesseracta do C# mogą być martwe, ale powinno się ich dać użyć.
Ale zakładam że używasz tego - https://github.com/charlesw/tesseract - czyli w sumie najpopularniejszej paczki na NuGet-cie.
Ma bardzo (moim zdaniem) bezsensowne API, ale jak już działa to działa. Innej dobrej i aktualnej biblioteki do OCRa dla C# nie ma, a przynajmniej ostatnio kiedy szukałem nie było

Może o API troche. Api tego jest bardzo słabe, bo to bezpośredni, niskopoziomowy wrapper na C++owe API tesseracta, w dodatku z pominięciem pomocniczych funkcji/helperów stamtąd.
Więc zrobienie niektórych prostych rzeczy wymaga bardzo dużego kombinowania. W dodatku są bugi (albo były) na granicy .net <-> tesseract, i z tworzeniem obiektów Pix z Bitmap miałem problemy (robione jest to już za pomocą kodu natywnego który woła wrapper). Nie wiadomo jakie inne problemy się tam mogą kryć, tylko ostrzegam w razie czego.

Wystarczy wspomnieć że sam autor się pomylił za pierwszym razem w odpowiadaniu na swoim forum na dość podstawowe pytanie (iteracja po znakach w parsowanym tekście)). (wiem bo sam szukałem czegoś podobnego kiedyś).

Dokumentacja biblioteki jest słaba, więc wrzucę coś działającego. Jakiś kod z dysku który kiedyś napisałem do czytania słów z bitmapy - może pomoże w czymś/oszczędzi Ci godzin zgrzytania zębami (tak jak ja zgrzytałem, bo leciały wyjątki tam gdzie NIE powinny, albo nic nie było rozpoznawane chociaż powinno):

/// <summary>
/// Klasa pomocnicza reprezentująca słowo (do iterowania po słowach)
/// </summary>
class Word
{
    public string Text { get; set; }
    public Rectangle Bounds { get; set; }
}

/// <summary>
/// Przeskaluj bitmapę do wymaganego DPI. Tesseract wymaga obrazów z odpowiednią rozdzielczością,
/// inaczej tekst z małym fontem będzie nierozpoznawalny
/// </summary>
static Bitmap RescaleToDpi(Image image, int dpiX, int dpiY)
{
    Bitmap bm = new Bitmap(
        (int)(image.Width * dpiX / image.HorizontalResolution),
        (int)(image.Height * dpiY / image.VerticalResolution),
        image.PixelFormat);
    bm.SetResolution(dpiX, dpiY);
    using (Graphics g = Graphics.FromImage(bm))
    {
        g.InterpolationMode = InterpolationMode.Bicubic;
        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
        g.DrawImage(image, 0, 0, bm.Width, bm.Height);
    }
    return bm;
}

/// <summary>
/// Iteruj po słowach w bitmapie (jak nazwa wskazuje zresztą).
/// Nie takie proste do napisania jak by się wydawało.
/// Dodatkowa komplikacja wynika z tego, że trzeba przeskalować
/// bitmapę do dobrego DPI żeby mieć dobre wyniki
/// </summary>
IEnumerable<Word> OcrAndIterateWords(Bitmap image)
{
    const int DesiredDpi = 500;
    var result = RescaleToDpi(image, DesiredDpi, DesiredDpi);
    var scaleX = DesiredDpi / image.HorizontalResolution;
    var scaleY = DesiredDpi / image.VerticalResolution;
    var imageBytes = GetTiffBytes(result);
    var pixels = Pix.LoadTiffFromMemory(imageBytes);
    using (var page = engine.Process(pixels))
    {
        foreach (var word in IteratePage(page))
        {
            word.Bounds = new Rectangle(
                (int)(word.Bounds.X / scaleX),
                (int)(word.Bounds.Y / scaleY),
                (int)(word.Bounds.Width / scaleX),
                (int)(word.Bounds.Height / scaleY));
            yield return word;
        }
    }
}

/// <summary>
/// Iteruj po słowach na przeanalizowanej przez tesseracta stronie (Page) 
/// Ta metoda to WTF, bo C#owe Api tesseracta jest zrobione jak jest, i trzeba ręcznie
/// iterować po wszystkich strukturach. Nie można też pomijać żadnych fragmentów (np. brać
/// od razu następne słowo, bez iterowania po blokach i paragrafach) bo nie (bo nie zadziała).
/// </summary>
IEnumerable<Word> IteratePage(Page page)
{
    using (var iter = page.GetIterator())
    {
        iter.Begin();
        do
        {
            do
            {
                do
                {
                    do
                    {
                        Rect bounds;
                        iter.TryGetBoundingBox(PageIteratorLevel.Word, out bounds);
                        string text = iter.GetText(PageIteratorLevel.Word) ?? "";
                        yield return new Word
                        {
                            Text = text,
                            Bounds = new Rectangle(bounds.X1, bounds.Y1, bounds.Width, bounds.Height)
                        };
                    } while (iter.Next(PageIteratorLevel.TextLine, PageIteratorLevel.Word));
                } while (iter.Next(PageIteratorLevel.Para, PageIteratorLevel.TextLine));
            } while (iter.Next(PageIteratorLevel.Block, PageIteratorLevel.Para));
        } while (iter.Next(PageIteratorLevel.Block));
    }
}

/// <summary>
/// Mały helper, zapisuje bitmapę jako TIFF do tablicy bajtów. 
/// Bo API wspiera tylko czytanie tiffów z bajtów, a Pix z bitmapy nie działał afaik (bug jakiś)
/// </summary>
/// <param name="img"></param>
/// <returns></returns>
byte[] GetTiffBytes(Bitmap img)
{
    using (var buffer = new MemoryStream())
    {
        img.Save(buffer, System.Drawing.Imaging.ImageFormat.Tiff);
        return buffer.ToArray();
    }
}
0

Wielkie dzięki MSM za wyczerpującą odpowiedź i za kod :) Jeśli chodzi o tesseract-polish to już przed napisaniem tego wątku na forum ściągnąłem plik tessdata, ale nie wiem jak go użyć, widocznie czegoś nie doczytałem, bo to mój pierwszy kontakt z tą biblioteką. Czy mógłbym jeszcze prosić o wskazanie jak prawidłowo posługiwać się tą biblioteką, jak ją uczyć, jak dodawać nowe próbki, itd.?

0

@msm
Chciałbym skorzystać z tego co napisałeś. Do tej pory używałem prostej metody z użyciem Tesseracta:


    public static string GetText(Bitmap imgsource)
        {
            var ocrtext = string.Empty;
            using (var engine = new TesseractEngine(@"./tessdata", 
             "eng",EngineMode.Default))
            {
                using (var img = PixConverter.ToPix(imgsource))
                {
                    using (var page = engine.Process(img))
                    {
                        ocrtext = page.GetText();
                    }
                }
            }

            return ocrtext;
        }

Jednak teraz muszę wyciągnąć treść z tifa z bardzo słabej jakości i małym fontem. Widzę, że zrobiłeś kawał dobrej roboty i napisałeś metody, które przystosowują obraz do użycia OCR tesseract.

Mógłbyś powiedzieć jak mam z tego skorzystać?
W mainie wczytam tifa jako bitmapę

  Bitmap bmp = Bitmap.FromFile(path) as Bitmap;

i teraz jaki kolejny krok wykonać?

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0