2014-05-13 16 views
10

Po prostu zacząłem brudzić sobie ręce biblioteką Tesseract, ale wyniki są naprawdę bardzo złe.iOS Tesseract: złe wyniki

Postępowałem zgodnie z instrukcjami w repozytorium Git (https://github.com/gali8/Tesseract-OCR-iOS). Moja ViewController wykorzystuje następujące metody, aby rozpocząć rozpoznawanie:

Tesseract *t = [[Tesseract alloc] initWithLanguage:@"deu"]; 
t.delegate = self; 

[t setVariableValue:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" forKey:@"tessedit_char_whitelist"]; 
[t setImage:img]; 

[t recognize]; 

NSLog(@"Recognized text: %@", [t recognizedText]); 

labelRecognizedText.text = [t recognizedText]; 

t = nil; 

próbki obrazu z tempalte projektu the sample image

działa dobrze (który mówi mi, że sam projekt jest ustawiony prawidłowo), ale gdy próbuję użyć inne obrazy, rozpoznany tekst to kompletny bałagan. Na przykład, próbowałem zrobić zdjęcie mojego wyszukiwarki wyświetlania obrazu próbki:

https://dl.dropboxusercontent.com/u/607872/tesseract.jpg (1,5 MB)

Ale Tesseract rozpoznaje:

Recognized text: s f l TO if v Ysssifss f 

ssqxizg ss sfzzlj z 

s N T IYIOGY Z I l EY s s 

k Es ETL ZHE s UEY 

z xhks Fsjs Es z VIII c 

s I XFTZT c s h V Ijzs 

L s sk sisijk J 

s f s ssj Jss sssHss H VI 

s s H 

i s H st xzs 
s s k 4 is x2 IV 
Illlsiqss sssnsiisfjlisszxiij s 
K 

Nawet gdy biała lista zawiera jedynie charakter numery, nie uzyskać wynik nawet blisko tego, co obraz wygląda następująco:

Recognized text:  3   74 211 

    1    

     1 1 1  

    3 53 379  1 

3 1 33 5 3 2 
     3   9 73 
    1 61 2 2 
    3 1 6 5 212 7 
     1 
4  9 4 
      1 17 
111 11 1 1 11 1 1 1 1 

Zakładam, że jest coś złego w drodze fotos są zaczerpnięte z aparatu iPada mini, z którego obecnie korzystam, ale nie wiem, co i dlaczego.

Jakieś wskazówki?


Aktualizacja # 1

W odpowiedzi Tomas:

śledziłem tutorial w swoim poście, ale kilka błędów napotkanych po drodze ...

  • UIImage+OpenCV kategoria nie może być używana w moim projekcie ARC
  • Nie mogę zaimportować <opencv2/...> w moich kontrolerów, autouzupełnianie nie oferuje go (a więc [UIImage CVMat] nie jest zdefiniowana)

myślę, że jest coś nie tak z moim integracji OpenCV, choć następuje Hello-tutorial i dodał ramy. Czy muszę też zbudować OpenCV na moim Macu, czy wystarczy tylko dołączyć framework do mojego projektu Xcode?

Ponieważ ja naprawdę nie wiem, co można uznać za „ważne” w tym punkcie (ja już przeczytać kilka postów i samouczków i próbowałem różnych czynności), nie krępuj się zapytać :)


Aktualizacja # 2

@ Tom: dzięki, ARC-część była niezbędna. Mój ViewController został już przemianowany na .mm. Zapomnij o części dotyczącej "nie można zaimportować programu opencv2 /", ponieważ już go zawarłem w moim TestApp-Prefix.pch (jak podano w samouczku "Cześć").

do następnego wyzwania;)

Zauważyłem, że kiedy używać zdjęć wykonanych za pomocą aparatu, granice dla obiektu roi nie oblicza się pomyślnie. Bawiłem się z orientacją urządzenia i umieszczałem w moim widoku UIImage, aby zobaczyć kroki przetwarzania obrazu, ale czasami (nawet gdy obraz jest prawidłowo wyrównany) wartości są ujemne, ponieważ if -warunek w bounds.size() - for -oop isn ' t spełnione. Najgorszy przypadek jaki miałem: minX/Y i maxX/Y nigdy nie zostały dotknięte. Krótka historia: linia zaczynająca się od Mat roi = inranged(cv::Rect( zgłasza wyjątek (asercja nie powiodła się, ponieważ wartości były < 0). Nie wiem, czy liczba konturów ma znaczenie, ale zakładam, że im większy obraz, tym bardziej prawdopodobny jest wyjątek zapewniania.

Szczerze mówiąc: Nie miałem czasu, aby przeczytać dokumentację OpenCV i zrozumieć, co robi twój kod, ale od tej chwili nie sądzę, żeby istniała jakakolwiek droga. Wydaje się, niestety, dla mnie, moje początkowe zadanie (potwierdzenie skanowania, uruchomienie OCR, wyświetlanie pozycji w tabeli) wymaga więcej zasobów (= czasu) niż myślałem.

Odpowiedz

6

Nie ma nic złego w sposobie robienia zdjęć z iPada per se. Ale nie można po prostu rzucić tak złożonego obrazu i oczekiwać, że Tesseract w magiczny sposób wybierze tekst do wyodrębnienia. Przyjrzyj się obrazowi, a zauważysz, że nie ma jednolitej błyskawicy, jest bardzo głośny, więc może nie być najlepszą próbką do rozpoczęcia gry.

W takich sytuacjach konieczne jest wstępne przetworzenie obrazu w celu udostępnienia biblioteki tesseract czegoś łatwiejszego do rozpoznania.

Poniżej znajduje się bardzo naiwny przykład przetwarzania wstępnego, który wykorzystuje OpenCV (http://www.opencv.org), popularny framework przetwarzania obrazu. To powinno dać ci i pomysł, aby zacząć.

#import <TesseractOCR/TesseractOCR.h> 
#import <opencv2/opencv.hpp> 
#import "UIImage+OpenCV.h" 

using namespace cv; 

... 

// load source image 
UIImage *img = [UIImage imageNamed:@"tesseract.jpg"]; 

Mat mat = [img CVMat]; 
Mat hsv; 

// convert to HSV (better than RGB for this task) 
cvtColor(mat, hsv, CV_RGB2HSV_FULL); 

// blur is slightly to reduce noise impact 
const int blurRadius = img.size.width/250; 
blur(hsv, hsv, cv::Size(blurRadius, blurRadius)); 

// in range = extract pixels within a specified range 
// here we work only on the V channel extracting pixels with 0 < V < 120 
Mat inranged; 
inRange(hsv, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 120), inranged); 

enter image description here

Mat inrangedforcontours; 
inranged.copyTo(inrangedforcontours); // findContours alters src mat 

// now find contours to find where characters are approximately located 
vector<vector<cv::Point> > contours; 
vector<Vec4i> hierarchy; 

findContours(inrangedforcontours, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0)); 

int minX = INT_MAX; 
int minY = INT_MAX; 
int maxX = 0; 
int maxY = 0; 

// find all contours that match expected character size 
for (size_t i = 0; i < contours.size(); i++) 
{ 
    cv::Rect brect = cv::boundingRect(contours[i]); 
    float ratio = (float)brect.height/brect.width; 

    if (brect.height > 250 && ratio > 1.2 && ratio < 2.0) 
    { 
     minX = MIN(minX, brect.x); 
     minY = MIN(minY, brect.y); 
     maxX = MAX(maxX, brect.x + brect.width); 
     maxY = MAX(maxY, brect.y + brect.height); 
    } 
} 

enter image description here

// Now we know where our characters are located 
// extract relevant part of the image adding a margin that enlarges area 
const int margin = img.size.width/50; 
Mat roi = inranged(cv::Rect(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin)); 
cvtColor(roi, roi, CV_GRAY2BGRA); 
img = [UIImage imageWithCVMat:roi]; 

enter image description here

Tesseract *t = [[Tesseract alloc] initWithLanguage:@"eng"]; 

[t setVariableValue:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" forKey:@"tessedit_char_whitelist"]; 
[t setImage:img]; 

[t recognize]; 

NSString *recognizedText = [[t recognizedText] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 

if ([recognizedText isEqualToString:@"1234567890"]) 
    NSLog(@"Yeah!"); 
else 
    NSLog(@"Epic fail..."); 

Uwagi

  • kategoria UIImage+OpenCV można znaleźć here. Jeśli jesteś pod kontrolą ARC, sprawdź: this.
  • Proszę spojrzeć na this, aby rozpocząć pracę z OpenCV w Xcode. Zauważ, że OpenCV to framework C++, którego nie można zaimportować w zwykłych plikach źródłowych C (lub Objective-C). Najłatwiejszym rozwiązaniem jest zmiana nazwy kontrolera widoku z .m na .mm (Objective-C++) i ponowne zaimportowanie go do projektu.
+0

Dzięki za porady, zaktualizowałem moje pytanie. – Dennis

+0

Zaktualizowałem sekcję notatek z kilkoma komentarzami: –

+0

Dziękujemy! Poprawiłem Twój post i ponownie edytowałem moje pytanie. – Dennis

2

Istnieje różne zachowanie wyniku tesseract.

  • Wymaga dobrej jakości obrazu, co oznacza dobrą widoczność tekstury.
  • Duży obraz zajmuje dużo czasu, aby go przetworzyć, aby zmienić rozmiar na mały przed przetworzeniem.
  • Przed wysłaniem do tesseract dobrze będzie zrobić efekt koloru na obrazie. Użyj efektów, które mogą poprawić widoczność obrazu.
  • Istnieje czasami inne zachowanie przetwarzania zdjęcia przy użyciu aparatu lub albumu z aparatem.

W przypadku robienia zdjęcia bezpośrednio z aparatu spróbuj poniżej funkcji.

- (UIImage *) getImageForTexture:(UIImage *)src_img{ 
CGColorSpaceRef d_colorSpace = CGColorSpaceCreateDeviceRGB(); 
/* 
* Note we specify 4 bytes per pixel here even though we ignore the 
* alpha value; you can't specify 3 bytes per-pixel. 
*/ 
size_t d_bytesPerRow = src_img.size.width * 4; 
unsigned char * imgData = (unsigned char*)malloc(src_img.size.height*d_bytesPerRow); 
CGContextRef context = CGBitmapContextCreate(imgData, src_img.size.width, 
               src_img.size.height, 
               8, d_bytesPerRow, 
               d_colorSpace, 
               kCGImageAlphaNoneSkipFirst); 

UIGraphicsPushContext(context); 
// These next two lines 'flip' the drawing so it doesn't appear upside-down. 
CGContextTranslateCTM(context, 0.0, src_img.size.height); 
CGContextScaleCTM(context, 1.0, -1.0); 
// Use UIImage's drawInRect: instead of the CGContextDrawImage function, otherwise you'll have issues when the source image is in portrait orientation. 
[src_img drawInRect:CGRectMake(0.0, 0.0, src_img.size.width, src_img.size.height)]; 
UIGraphicsPopContext(); 

/* 
* At this point, we have the raw ARGB pixel data in the imgData buffer, so 
* we can perform whatever image processing here. 
*/ 

// After we've processed the raw data, turn it back into a UIImage instance. 
CGImageRef new_img = CGBitmapContextCreateImage(context); 
UIImage * convertedImage = [[UIImage alloc] initWithCGImage: 
          new_img]; 

CGImageRelease(new_img); 
CGContextRelease(context); 
CGColorSpaceRelease(d_colorSpace); 
free(imgData); 
return convertedImage; 
} 
1

Konwersja UIImage z sRGB rgb formatu.
jeśli używasz IOS 5.0 i wyżej użytku

korzystanie #import <Accelerate/Accelerate.h>

inny znak komentarza // IOS 3.0-5.0

-(UIImage *) createARGBImageFromRGBAImage: (UIImage*)image 
{ //CGSize size = CGSizeMake(320, 480); 
    CGSize dimensions = CGSizeMake(320, 480); 
    NSUInteger bytesPerPixel = 4; 
    NSUInteger bytesPerRow = bytesPerPixel * dimensions.width; 
    NSUInteger bitsPerComponent = 8; 

    unsigned char *rgba = malloc(bytesPerPixel * dimensions.width * dimensions.height); 
    unsigned char *argb = malloc(bytesPerPixel * dimensions.width * dimensions.height); 

    CGColorSpaceRef colorSpace = NULL; 
    CGContextRef context = NULL; 

    colorSpace = CGColorSpaceCreateDeviceRGB(); 
    context = CGBitmapContextCreate(rgba, dimensions.width, dimensions.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault); // kCGBitmapByteOrder32Big 
    CGContextDrawImage(context, CGRectMake(0, 0, dimensions.width, dimensions.height), [image CGImage]); 
    CGContextRelease(context); 
    CGColorSpaceRelease(colorSpace); 

    const vImage_Buffer src = { rgba, dimensions.height, dimensions.width, bytesPerRow }; 
    const vImage_Buffer dis = { rgba, dimensions.height, dimensions.width, bytesPerRow }; 
    const uint8_t map[4] = {3,0,1,2}; 
    vImagePermuteChannels_ARGB8888(&src, &dis, map, kvImageNoFlags); 

    //IOS 3.0-5.0 
    /*for (int x = 0; x < dimensions.width; x++) { 
     for (int y = 0; y < dimensions.height; y++) { 
      NSUInteger offset = ((dimensions.width * y) + x) * bytesPerPixel; 
      argb[offset + 0] = rgba[offset + 3]; 
      argb[offset + 1] = rgba[offset + 0]; 
      argb[offset + 2] = rgba[offset + 1]; 
      argb[offset + 3] = rgba[offset + 2]; 
     } 
    }*/ 




    colorSpace = CGColorSpaceCreateDeviceRGB(); 
    context = CGBitmapContextCreate(dis.data, dimensions.width, dimensions.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrderDefault); // kCGBitmapByteOrder32Big 
    CGImageRef imageRef = CGBitmapContextCreateImage(context); 
    image = [UIImage imageWithCGImage: imageRef]; 
    CGImageRelease(imageRef); 
    CGContextRelease(context); 
    CGColorSpaceRelease(colorSpace); 

    free(rgba); 
    free(argb); 

    return image; 
} 

Tesseract *t = [[Tesseract alloc] initWithLanguage:@"eng"]; 

[t setVariableValue:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" forKey:@"tessedit_char_whitelist"]; 
[t setImage:[self createARGBImageFromRGBAImage:img]]; 

[t recognize]; 
0

Szybkie odpowiednik @ odpowiedź Faraz za

func getImageForTexture(srcImage: UIImage) -> UIImage{ 
    let d_colorSpace = CGColorSpaceCreateDeviceRGB() 
    let d_bytesPerRow: size_t = Int(srcImage.size.width) * 4 
    /* 
    * Note we specify 4 bytes per pixel here even though we ignore the 
    * alpha value; you can't specify 3 bytes per-pixel. 
    */ 
    let imgData = malloc(Int(srcImage.size.height) * Int(d_bytesPerRow)) 

    let context = CGBitmapContextCreate(imgData, Int(srcImage.size.width), Int(srcImage.size.height), 8, Int(d_bytesPerRow), d_colorSpace,CGImageAlphaInfo.NoneSkipFirst.rawValue) 
    UIGraphicsPushContext(context!) 
    // These next two lines 'flip' the drawing so it doesn't appear upside-down. 
    CGContextTranslateCTM(context, 0.0, srcImage.size.height) 
    CGContextScaleCTM(context, 1.0, -1.0) 
    // Use UIImage's drawInRect: instead of the CGContextDrawImage function, otherwise you'll 
    srcImage.drawInRect(CGRectMake(0.0, 0.0, srcImage.size.width, srcImage.size.height)) 
    UIGraphicsPopContext() 
    /* 
    * At this point, we have the raw ARGB pixel data in the imgData buffer, so 
    * we can perform whatever image processing here. 
    */ 

    // After we've processed the raw data, turn it back into a UIImage instance. 

    let new_img = CGBitmapContextCreateImage(context) 
    let convertedImage = UIImage(CGImage: new_img!) 
    return convertedImage 

} 
1

Od tygodni borykałem się z rozpoznawaniem postaci Tesseract. Oto dwie rzeczy nauczyłem się zmusić go do pracy lepiej ...

  1. Jeśli wiesz jaka czcionka będzie czytanie, jasny szkolenia i przekwalifikowania go tylko dla tej czcionki. Wiele czcionek spowalnia przetwarzanie OCR, a także zwiększa niejednoznaczność procesu decyzyjnego Tesseract. Doprowadzi to do większej dokładności i szybkości.

  2. Po przetworzeniu OCR jest naprawdę potrzebne. Otrzymasz matrycę znaków rozpoznawalną przez Tesseract. Będziesz musiał dalej przetwarzać znaki, aby zawęzić to, co próbujesz przeczytać. Na przykład, jeśli twoja aplikacja czyta etykiety żywności, znajomość reguł dla słów i zdań, które składają się na etykietę żywności, pomoże rozpoznać serię znaków, które składają się na tę etykietę.

+0

Jak mogę trenować nowy język ze znakami specjalnymi? Czy możesz podać przykład? – Ramakrishna