OCR:图像到文字?

时间:2012-11-06 09:19:00

标签: ios ocr xcode4.5 tesseract leptonica

  

在标记为复制或重复问题之前,请先阅读整个问题。

我能够在现有的情况下做到如下:

  1. 获取图像并裁剪OCR所需的部分。
  2. 使用tesseractleptonica处理图片。
  3. 当应用文档以块的形式裁剪时,即每个图像1个字符,它提供96%的准确度。
  4. 如果我不这样做,文档背景为白色,文本为黑色,则几乎相同。
  5. 例如,如果输入是这张照片:

    照片开始

    enter image description here

    照片结束

    我想要的是能够为这张照片enter image description here获得相同的准确度 没有生成块。

    我用来初始化图像并从图像中提取文本的代码如下:

    初学tesseract

    .h文件中的

    tesseract::TessBaseAPI *tesseract;
    uint32_t *pixels;
    
    .m文件中的

    tesseract = new tesseract::TessBaseAPI();
    tesseract->Init([dataPath cStringUsingEncoding:NSUTF8StringEncoding], "eng");
    tesseract->SetPageSegMode(tesseract::PSM_SINGLE_LINE);
    tesseract->SetVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    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");
    

    从图像中获取文字

    - (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];
    }
    

    但是我没有为号牌图像得到正确的输出,无论是null还是为图像提供了一些垃圾数据。

    如果我使用的是第一个图像,即文本为黑色的白色背景,那么输出的准确度为89%到95%。

    请帮帮我。

    任何建议都将受到赞赏。

    更新

    感谢@jcesar提供链接,也感谢@konstantin pribluda提供有价值的信息和指南。

    我能够将图像转换为适当的黑白形式(几乎)。所以对所有图像的识别都更好:)。

      

    需要有关图像正确二值化的帮助。任何想法将不胜感激

3 个答案:

答案 0 :(得分:6)

大家好,感谢您的回复,在所有回复中,我能得出以下结论:

  1. 我需要获得唯一一个包含铭牌的裁剪图像块。
  2. 需要使用我提供的方法here找到数字部分的部分。
  3. 然后使用通过上述方法找到的RGB数据将图像数据转换为几乎黑白。
  4. 然后使用提供的方法here将数据转换为图像。
  5. 将以上4个步骤合并为如下所示的一种方法:

    -(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);
    }
    

    注意:

      

    此方法的唯一缺点是消耗的时间和转换为白色和其他黑色的RGB值。

    更新:

        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]; 
    

    只需将上述方法(getRGBAsFromImage:)代码替换为此方法,结果相同,但所用时间仅为0.1至0.3秒。

答案 1 :(得分:4)

我能够使用提供的演示照片获得近乎即时的结果,并生成正确的字母。

我使用GPUImage

预处理了图片
// Pre-processing for OCR
GPUImageLuminanceThresholdFilter * adaptiveThreshold = [[GPUImageLuminanceThresholdFilter alloc] init];
[adaptiveThreshold setThreshold:0.3f];
[self setProcessedImage:[adaptiveThreshold imageByFilteringImage:_image]];

然后将处理后的图像发送到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));
}

这留下'标记 - 但这些也很容易删除。根据您拥有的图像集,您可能需要对其进行微调,但它应该让您朝着正确的方向前进。

如果您在使用它时遇到问题,请告诉我,它来自我正在使用的项目,我不想将所有内容删除或从头开始创建项目。

答案 2 :(得分:1)

我敢说,为了你的目的,tesseract将是矫枉过正的。您不需要字典匹配来提高识别质量(您没有此字典,但可能意味着计算许可证编号的校验和),并且您已针对OCR进行了字体优化。 最重要的是,你有标记(附近的橙色和蓝色区域很好),可以在图像中找到区域。

我的OCR应用程序我使用人工辅助的感兴趣区域检索(只是帮助覆盖相机预览)。通常使用类似haar级联的东西来定位像面部这样的有趣特征。您还可以通过遍历所有图像并存储合适颜色的最左/最右/最顶/最底像素来计算橙色区域的质心,或仅计算橙色像素的边界框

至于识别itselff,我建议使用不变的时刻(不确定是否在tesseract中实现,但您可以轻松地从java项目中移植它:http://sourceforge.net/projects/javaocr/

我在显示器图像上尝试了我的演示应用程序,它识别了这项运动的数字(未经过训练 对于角色)

至于二值化(将黑色与白色分开)我建议使用Sauvola方法,因为这样可以提供最佳的亮度变化容差(也在我们的OCR项目中实现)