2012-11-06 6 views
13

Prima di contrassegnare come domanda di copia o ripetizione, leggere prima l'intera domanda.OCR: Immagine in testo?

sono in grado di fare al pressent è come qui sotto:

  1. per ottenere l'immagine e ritagliare la parte desiderata per l'OCR.
  2. Elaborare l'immagine utilizzando tesseract e leptonica.
  3. Quando il documento applicato viene ritagliato in blocchi, ovvero 1 carattere per immagine, fornisce il 96% di precisione.
  4. Se non lo faccio e lo sfondo del documento è di colore bianco e il testo è di colore nero, offre quasi la stessa accuratezza.

Per esempio, se l'ingresso è come questa foto:

Foto avviare

enter image description here

Foto fine

Quello che voglio è quello in grado di ottenere il stessa accuratezza per questa foto enter image description here
senza generare blocchi.

Il codice che ho usato per init tesseract ed estrarre il testo da un'immagine è come qui sotto:

Per init di Tesseract

nel file h

tesseract::TessBaseAPI *tesseract; 
uint32_t *pixels; 

nel file di .m

tesseract = new tesseract::TessBaseAPI(); 
tesseract->Init([dataPath cStringUsingEncoding:NSUTF8StringEncoding], "eng"); 
tesseract->SetPageSegMode(tesseract::PSM_SINGLE_LINE); 
tesseract->SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
tesseract->SetVariable("language_model_penalty_non_freq_dict_word", "1"); 
tesseract->SetVariable("language_model_penalty_non_dict_word ", "1"); 
tesseract->SetVariable("tessedit_flip_0O", "1"); 
tesseract->SetVariable("tessedit_single_match", "0"); 
tesseract->SetVariable("textord_noise_normratio", "5"); 
tesseract->SetVariable("matcher_avg_noise_size", "22"); 
tesseract->SetVariable("image_default_resolution", "450"); 
tesseract->SetVariable("editor_image_text_color", "40"); 
tesseract->SetVariable("textord_projection_scale", "0.25"); 
tesseract->SetVariable("tessedit_minimal_rejection", "1"); 
tesseract->SetVariable("tessedit_zero_kelvin_rejection", "1"); 

Per il testo ottenere da un'immagine

- (void)processOcrAt:(UIImage *)image 
{ 
    [self setTesseractImage:image]; 

    tesseract->Recognize(NULL); 
    char* utf8Text = tesseract->GetUTF8Text(); 
    int conf = tesseract->MeanTextConf(); 

    NSArray *arr = [[NSArray alloc]initWithObjects:[NSString stringWithUTF8String:utf8Text],[NSString stringWithFormat:@"%d%@",conf,@"%"], nil]; 

    [self performSelectorOnMainThread:@selector(ocrProcessingFinished:) 
          withObject:arr 
         waitUntilDone:YES]; 
    free(utf8Text); 
} 

- (void)ocrProcessingFinished0:(NSArray *)result 
{ 
    UIAlertView *alt = [[UIAlertView alloc]initWithTitle:@"Data" message:[result objectAtIndex:0] delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 
    [alt show]; 
} 

Ma non ottengo uscita corretta per l'immagine del numero di targa o è nullo o dà alcuni dati spazzatura per l'immagine.

E se utilizzo l'immagine che è la prima, ovvero lo sfondo bianco con testo nero, l'output è preciso dall'89 al 95%.

Please help me out.

Qualsiasi suggerimento sarà apprezzato.

Aggiornamento

Grazie a @jcesar per fornire il link e anche per @konstantin Pribluda per fornire informazioni e preziosa guida.

Sono in grado di convertire le immagini in una corretta forma in bianco e nero (quasi).e quindi il riconoscimento è migliore per tutte le immagini :)

Hai bisogno di aiuto con la corretta binarizzazione delle immagini. Qualsiasi Idea sarà apprezzata

+0

Forse puoi provare a manipolare l'immagine prima di provare a riconoscere il testo, ad esempio cambiare il colore dei pixel non nero (o vicino al nero) in bianco. In questo momento non ho il codice obiettivo-c per farlo, ma sono sicuro che può essere fatto. – jcesarmobile

+0

Ho pensato per questo ma lo stesso qui non sono in grado di implementarlo. –

+0

Leggere i collegamenti sulla risposta accettata http://stackoverflow.com/questions/9977905/change-a-color-in-a-uiimage – jcesarmobile

risposta

6

Ciao a tutti grazie per le vostre risposte, da parte di tutti che le risposte sono in grado di ottenere questa conclusione, come di seguito:

  1. ho bisogno di ottenere il solo un blocco immagine ritagliato con numero di targa in esso contenuto.
  2. Da quella piastra è necessario trovare la parte della porzione numero utilizzando i dati che ho ottenuto utilizzando il metodo fornito here.
  3. Quindi convertire i dati dell'immagine in quasi bianco e nero utilizzando i dati RGB trovati tramite il metodo precedente.
  4. Quindi i dati vengono convertiti nell'immagine utilizzando il metodo fornito here.

Sopra 4 passi sono combinati a un metodo come questo come di seguito:

-(void)getRGBAsFromImage:(UIImage*)image 
{ 
    NSInteger count = (image.size.width * image.size.height); 
    // First get the image into your data buffer 
    CGImageRef imageRef = [image CGImage]; 
    NSUInteger width = CGImageGetWidth(imageRef); 
    NSUInteger height = CGImageGetHeight(imageRef); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char)); 
    NSUInteger bytesPerPixel = 4; 
    NSUInteger bytesPerRow = bytesPerPixel * width; 
    NSUInteger bitsPerComponent = 8; 
    CGContextRef context = CGBitmapContextCreate(rawData, width, height, 
               bitsPerComponent, bytesPerRow, colorSpace, 
               kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); 
    CGColorSpaceRelease(colorSpace); 

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); 
    CGContextRelease(context); 

    // Now your rawData contains the image data in the RGBA8888 pixel format. 
    int byteIndex = 0; 
    for (int ii = 0 ; ii < count ; ++ii) 
    { 
     CGFloat red = (rawData[byteIndex]  * 1.0) ; 
     CGFloat green = (rawData[byteIndex + 1] * 1.0) ; 
     CGFloat blue = (rawData[byteIndex + 2] * 1.0) ; 
     CGFloat alpha = (rawData[byteIndex + 3] * 1.0) ; 

     NSLog(@"red %f \t green %f \t blue %f \t alpha %f rawData [%d] %d",red,green,blue,alpha,ii,rawData[ii]); 
     if(red > Required_Value_of_red || green > Required_Value_of_green || blue > Required_Value_of_blue)//all values are between 0 to 255 
     { 
      red = 255.0; 
      green = 255.0; 
      blue = 255.0; 
      alpha = 255.0; 
      // all value set to 255 to get white background. 
     } 
     rawData[byteIndex] = red; 
     rawData[byteIndex + 1] = green; 
     rawData[byteIndex + 2] = blue; 
     rawData[byteIndex + 3] = alpha; 

     byteIndex += 4; 
    } 

    colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef bitmapContext = CGBitmapContextCreate(
                 rawData, 
                 width, 
                 height, 
                 8, // bitsPerComponent 
                 4*width, // bytesPerRow 
                 colorSpace, 
                 kCGImageAlphaNoneSkipLast); 

    CFRelease(colorSpace); 

    CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext); 

    UIImage *img = [UIImage imageWithCGImage:cgImage]; 

    //use the img for further use of ocr 

    free(rawData); 
} 

Nota:

L'unico inconveniente di questo metodo è il tempo impiegato e il RGB valore da convertire in bianco e altro in nero.

UPDATE:

CGImageRef imageRef = [plate CGImage]; 
    CIContext *context = [CIContext contextWithOptions:nil]; // 1 
    CIImage *ciImage = [CIImage imageWithCGImage:imageRef]; // 2 
    CIFilter *filter = [CIFilter filterWithName:@"CIColorMonochrome" keysAndValues:@"inputImage", ciImage, @"inputColor", [CIColor colorWithRed:1.f green:1.f blue:1.f alpha:1.0f], @"inputIntensity", [NSNumber numberWithFloat:1.f], nil]; // 3 
    CIImage *ciResult = [filter valueForKey:kCIOutputImageKey]; // 4 
    CGImageRef cgImage = [context createCGImage:ciResult fromRect:[ciResult extent]]; 
    UIImage *img = [UIImage imageWithCGImage:cgImage]; 

basta sostituire (getRGBAsFromImage:) codice del metodo di cui sopra con questo e il risultato è lo stesso ma il tempo impiegato è appena 0,1 a 0,3 secondo.

+0

Questo richiede molto tempo, ma sembra che stia facendo quello che voglio. Qualche modo di usare qualcosa di simile con GPUImage o qualcosa di simile? – mwright

+0

sì, è giusto per dire che l'immagine pix 250 X 55 impiega quasi 1.5 minuti (quasi), ma fornisce un'accuratezza del 99%. Sai o hai qualche suggerimento su come amare il tempo richiesto? :) –

+0

Non ho suggerimenti per ridurlo usando questo metodo, sto usando una combinazione di pre-elaborazione dell'immagine e Tess per ottenere risultati accurati al 100% su ciò su cui sto lavorando. Farò un tentativo con la tua immagine e vedere se riesco a ottenere risultati simili, se funziona, inserirò qui una risposta. – mwright

1

Immagino che il tesseract sarà eccessivo per il tuo scopo. Non hai bisogno della corrispondenza del dizionario per migliorare la qualità del riconoscimento (non hai questo dizionario, ma forse significa calcolare il checksum sul numero di licenza) e hai carattere ottimizzato per OCR. E, soprattutto, hai dei marcatori (le aree di colore arancione e blu nelle vicinanze sono buone) per trovare la regione nell'immagine.

Le mie app OCR Uso l'area di recupero di interesse assistito dall'uomo (mirando semplicemente all'overlay dell'help in anteprima della fotocamera). Di solito si usa qualcosa come haar cascade per individuare caratteristiche interessanti come facce. Puoi anche calcolare il centroide dell'area arancione, o solo il rettangolo di pixel arancioni, semplicemente attraversando tutta l'immagine e posizionando i pixel più a sinistra/a destra/in alto/in basso di colore adatto

Come per il riconoscimento suoelff, consiglierei di utilizzare i momenti invarianti (non so se implementato in Tesseract, ma si può facilmente porta da fuori progetto Java: http://sourceforge.net/projects/javaocr/)

ho fatto del mio demo app sull'immagine di monitoraggio e ha riconosciuto le cifre sullo sport (non è addestrato per i caratteri)

Per quanto riguarda la binarisazione (separando il nero dal bianco), raccomanderei il metodo sauvola in quanto ciò dà a bes t la tolleranza ai cambiamenti di luminanza (implementate anche nel nostro progetto OCR)

+0

Sì, è giusto, ma non so come ottenere l'area perfetta e come ottenere il testo senza fare alcuna generazione di blocchi, cioè è necessario ritagliare le immagini in 1 char per blocchi immagine e quindi fare ocr genererà un buon risultato altrimenti darà solo valori di spazzatura. –

+0

Grazie per la risposta lo proverò :) –

+0

ciao @ Konstantin, ho aggiornato la mia risposta. Ho appena trovato un modo per risolvere il problema con un tempo medio compreso tra .3 e 0,5 secondi. E ancora grazie per il tuo suggerimento, in quanto mi aiuta molto per arrivare alla soluzione derivata. –

4

Sono stato in grado di ottenere risultati quasi immediati utilizzando la foto dimostrativa fornita oltre a generare le lettere corrette.

I pretrattati l'immagine utilizzando GPUImage

// Pre-processing for OCR 
GPUImageLuminanceThresholdFilter * adaptiveThreshold = [[GPUImageLuminanceThresholdFilter alloc] init]; 
[adaptiveThreshold setThreshold:0.3f]; 
[self setProcessedImage:[adaptiveThreshold imageByFilteringImage:_image]]; 

E poi l'invio di che immagine elaborata a TESS

- (NSArray *)processOcrAt:(UIImage *)image { 
    [self setTesseractImage:image]; 

    _tesseract->Recognize(NULL); 
    char* utf8Text = _tesseract->GetUTF8Text(); 

    return [self ocrProcessingFinished:[NSString stringWithUTF8String:utf8Text]]; 
} 

- (NSArray *)ocrProcessingFinished:(NSString *)result { 
    // Strip extra characters, whitespace/newlines 
    NSString * results_noNewLine = [result stringByReplacingOccurrencesOfString:@"\n" withString:@""]; 
    NSArray * results_noWhitespace = [results_noNewLine componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 
    NSString * results_final = [results_noWhitespace componentsJoinedByString:@""]; 
    results_final = [results_final lowercaseString]; 

    // Separate out individual letters 
    NSMutableArray * letters = [[NSMutableArray alloc] initWithCapacity:results_final.length]; 
    for (int i = 0; i < [results_final length]; i++) { 
     NSString * newTile = [results_final substringWithRange:NSMakeRange(i, 1)]; 
     [letters addObject:newTile]; 
    } 

    return [NSArray arrayWithArray:letters]; 
} 

- (void)setTesseractImage:(UIImage *)image { 
    free(_pixels); 

    CGSize size = [image size]; 
    int width = size.width; 
    int height = size.height; 

    if (width <= 0 || height <= 0) 
     return; 

    // the pixels will be painted to this array 
    _pixels = (uint32_t *) malloc(width * height * sizeof(uint32_t)); 
    // clear the pixels so any transparency is preserved 
    memset(_pixels, 0, width * height * sizeof(uint32_t)); 

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    // create a context with RGBA pixels 
    CGContextRef context = CGBitmapContextCreate(_pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, 
               kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast); 

    // paint the bitmap to our context which will fill in the pixels array 
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), [image CGImage]); 

    _tesseract->SetImage((const unsigned char *) _pixels, width, height, sizeof(uint32_t), width * sizeof(uint32_t)); 
} 

Questo lasciato segni 'per il - ma queste sono anche facili da rimuovere. A seconda del set di immagini che hai, potrebbe essere necessario regolarlo un po ', ma dovrebbe farti muovere nella giusta direzione.

Fatemi sapere se avete problemi ad usarlo, è da un progetto che sto usando e non volevo dover spogliare tutto o creare un progetto da zero per questo.

+0

grazie per la risposta. Ci provo sgarbatamente. Ma al momento ho capito che funzionava con CoreImage.framework del framework di elaborazione delle immagini di default di Apple e utilizzando i suoi filtri predefiniti ho ottenuto la mia immagine in bianco e nero puro molto facilmente e richiede solo 0,1 a 0,3 secondi. E fornisce risultati perfetti per quasi tutti i tipi di immagini su cui ci provo. –

+0

È necessario aggiornare la risposta per includere il nuovo metodo che si sta utilizzando in modo che altri possano trarne vantaggio. – mwright

+0

Vedere il mio aggiornamento nella mia risposta Ho messo il codice per questo tizio. So che questo è il sito a due vie. –