OCR:图像到文本?

Posted

技术标签:

【中文标题】OCR:图像到文本?【英文标题】:OCR: Image to text? 【发布时间】:2012-10-26 05:19:29 【问题描述】:

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

我现在可以做的如下:

    获取图像并裁剪 OCR 所需的部分。 使用tesseractleptonica 处理图像。 当应用的文档被裁剪成块(即每张图像 1 个字符)时,它可提供 96% 的准确度。 如果我不这样做并且文档背景为白色而文本为黑色,则它提供几乎相同的准确性。

例如,如果输入是这张照片:

照片开始

照片结束

我想要的是能够为这张照片获得相同的精度 不生成块。

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

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

但是我没有得到正确的车牌图像输出,要么它是空的,要么它为图像提供了一些垃圾数据。

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

请帮帮我。

任何建议将不胜感激。

更新

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

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

在正确二值化图像方面需要帮助。任何想法将不胜感激

【问题讨论】:

也许您可以在尝试识别文本之前尝试操作图像,例如将每个非黑色(或接近黑色)的像素颜色更改为白色。目前我没有用于执行此操作的 Objective-c 代码,但我确信它可以完成。 我有它,但在这里我无法实现它。 阅读已接受答案***.com/questions/9977905/change-a-color-in-a-uiimage上的链接 感谢您的回复。现在我有办法了。 TY :) @jcesar 感谢您的建议。我从您发布的链接中获取代码,目前正在尝试使我的代码正常工作:) 【参考方案1】:

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

    我需要获取唯一一个包含车牌的裁剪图像块。 需要使用我使用here提供的方法获得的数据,从该板块中找出数字部分的部分。 然后使用通过上述方法找到的 RGB 数据将图像数据转换为几乎是黑白的。 然后使用here提供的方法将数据转换为Image。

以上 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秒。

【讨论】:

这需要很长时间,但似乎正在做我想做的事。有什么方法可以将这样的东西与 GPUImage 或类似的东西一起使用? 是的,说 250 X 55 pix 图像几乎需要 1.5 分钟(几乎),但准确率达到 99%。你知道或有一些关于如何爱上所需时间的建议吗? :) 我对使用这种方法降低它没有任何建议,我正在使用图像预处理和 Tess 的组合来获得我正在处理的 100% 准确的结果。我会用你的图片试一试,看看我是否能得到类似的好结果,如果成功,我会在这里发布作为答案。 当然,我正在等待它,我会尽快测试它,如果它对我有用,我会接受答案。 @TheiosDev 通过使用此代码,它将 img 转换为黑白,但是您是如何从图像中获取文本的?【参考方案2】:

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

我使用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));

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

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

【讨论】:

感谢您的回复。我硬着头皮试一试。但目前我使用苹果默认图像处理框架的 CoreImage.framework 并使用它的默认过滤器,我很容易得到纯黑白的图像,只需要 0.1 到 0.3 秒。并为我尝试的几乎所有类型的图像提供完美的结果。 您应该更新您的答案以包含您正在使用的新方法,以便其他人可以从中受益。 在我的回答中查看我的更新我已经为它放了代码,伙计。我知道这是双向站点。【参考方案3】:

我敢说 tesseract 对你的目的来说太过分了。您不需要字典匹配来提高识别质量(您没有这本字典,但可能意味着计算许可证号的校验和),并且您已经为 OCR 优化了字体。 最重要的是,您可以使用标记(附近的橙色和蓝色区域很好)来查找图像中的区域。

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

至于识别本身,我建议使用不变矩(不确定是否在 tesseract 中实现,但您可以轻松地将其从 java 项目中移植:http://sourceforge.net/projects/javaocr/)

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

至于二值化(将黑色与白色分开),我会推荐 sauvola 方法,因为这对亮度变化具有最佳容差(也在我们的 OCR 项目中实现)

【讨论】:

是的,这是正确的,但我不知道如何获得完美的区域以及如何在不生成块的情况下获取文本,即需要将图像裁剪为每个图像块 1 个字符,然后执行 ocr产生好的结果,否则它只会给出垃圾值。 谢谢回复,我试试看:) 嗨@Konstantin,我已经更新了我的答案。我只是想办法用 0.3 到 0.5 秒的平均时间解决这个问题。再次感谢您的建议,因为它对我获得派生解决方案有很大帮助。

以上是关于OCR:图像到文本?的主要内容,如果未能解决你的问题,请参考以下文章

Python/OpenCV - 基于机器学习的 OCR(图像到文本)

用于 OCR 的场景文本图像超分辨率

将覆盖 HTML 传递到图像的 OCR?

Azure 计算机视觉 API - PDF 文件上的 OCR 到文本

适用于 .net 的免费 OCR SDK,可以将文本、带有格式和图像的表格提取到 Office word 文档中 [关闭]

从文本图像生成字体