[iOS9]CITextFeatureで文字領域検出+TesseractOCRiOSで文字認識してみた


概要

  • 画像内のテキスト位置を検出してみる
  • フリーのライブラリでOCRにかけてみる

結論から言うと、まだ手放しには使えなそうかなーと。

CITextFeatureについて

iOSにはCoreImageという画像をあれこれするためのFrameworkがあって、
その中にCIDetectorという、画像内の顔検出などができる機能がありました。
iOS9において、これに画像内のテキスト位置を検出する機能が付加されています。

実装

文字領域を取得する場合、CIDetectorを以下のように初期化します。

CIDetector *ciDetector = [CIDetector detectorOfType:CIDetectorTypeText
                                            context:nil
                                            options:nil];

各領域(feature)は、以下のようにArrayに取得します。

// optionsにCIDetectorReturnSubFeaturesを設定することでsubfeatureが取得できる
NSArray *features = [ciDetector featuresInImage:ciImage
                                        options:@{CIDetectorReturnSubFeatures: @YES}];

CITextFeatureは認識した文字の矩形情報を含んでいますが、
さらにその矩形内の子となる文字の矩形情報を含んでいます。
要は、1文字単位の情報もまとめて取得してくれるということです。
この子要素の情報は、subFeatureというpropertyからアクセスすることができます。

これらを使って、文字領域の矩形を囲むよう線を引くコードです。

テキストを検出して線で囲む
- (UIImage *) detectText:(UIImage *)inputImage {
    CIImage *ciImage = [CIImage imageWithCGImage:inputImage.CGImage];

    CIDetector *ciDetector = [CIDetector detectorOfType:CIDetectorTypeText
                                                context:nil
                                                options:nil];

    NSArray *features = [ciDetector featuresInImage:ciImage
                                            options:@{CIDetectorReturnSubFeatures: @YES}];

    UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, 0.0);
    [inputImage drawInRect:CGRectMake(0, 0, inputImage.size.width, inputImage.size.height)];

    for (CITextFeature *feature in features) {
        CGContextRef drawContext = UIGraphicsGetCurrentContext();
        CGContextSetLineWidth(drawContext, 3.f);

        // Y座標が逆になるので変換
        CGRect textRect = feature.bounds;
        textRect.origin.y = inputImage.size.height - (textRect.origin.y + textRect.size.height);

        // 線でテキストを囲む
        CGContextSetStrokeColorWithColor(drawContext, [UIColor blueColor].CGColor);
        CGContextStrokeRect(drawContext,textRect);

        // subfeatureのチェック
        CGContextSetStrokeColorWithColor(drawContext, [UIColor greenColor].CGColor);
        for (CITextFeature *subfeature in feature.subFeatures) {
            CGRect subTextRect = subfeature.bounds;
            subTextRect.origin.y = inputImage.size.height - (subTextRect.origin.y + subTextRect.size.height);
            CGContextStrokeRect(drawContext,subTextRect);            
        }
    }

    UIImage *drawedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return drawedImage;
}

さっそくやってみる

画像はKeynoteで適当に作りました。横幅640pxです。

結果

青枠が、UITextFeatureに検出された文字領域。
赤枠が、その検出された文字領域が含む子要素です。
ちょっと微妙な結果になりました。

  • あかさたなが取れていない
  • 文章によって縦幅が変わっている
  • subfeatureの誤認識が多い

( ˘ω˘)うーん…
取れている部分は割と綺麗に矩形が取れていますね。
ひらがなは文字中に隙間が多いので向いていないのかも…?

TesseractOCRiOSで文字を読み取ってみる

折角なので文字認識まで。
目標として、CITextFeatureで取得した領域に絞ってOCRスキャンをかけると
どうなるのかな?と思ったので試してみました。(下記参考にさせて頂きました)
【Swift】文字認識ライブラリ、TesseractOCR for iOSを試してみた

PodsでTesseractOCRiOSを追加してください。
Objective-Cだとこんな感じ。(スキャンに地味に数秒かかります)

- (void) analyze:(UIImage *)image {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"jpn"];
        tesseract.delegate = self;
        tesseract.image = image;

        if ([tesseract recognize]){
            NSLog(@"text : %@", tesseract.recognizedText);
        }
    });
}

認識させてみた

まずは画像全体をスキャンした結果。

あかさたな はまやらわ
える しっているか
しにがみは りんごしか たベない

綺麗に認識できています。

次に、CIDetectorから取得できた文字領域を対象にスキャンを行います。
今回は、上の方で取得できていた青い矩形の領域3つに対し処理を行いました。

textRectに対しOCRをかける
    ...

    CGImageRef srcImageRef = inputImage.CGImage;
    CGImageRef trimmedImageRef = CGImageCreateWithImageInRect(srcImageRef, textRect);
    UIImage *trimmedImage = [UIImage imageWithCGImage:trimmedImageRef];
    [self analyze:trimmedImage];

    ...

以下、その結果です。

はまやらわ

える しつているか

し にがみは り ん ご しか たベない
  • 小文字が大文字になってしまっている
  • 3行目の区切りが多い

(ヽ´ω`)うーーん……
領域をせばめたせいで、文字内の隙間がよくない感じに認識されているような。
あかさたなはCIDetectorの時点で省かれてしまっていますし微妙ですね。

まとめ

なんだか結果としては微妙でした。サンプルの文字列が悪かったのかも知れません。
コントラストを上げて文字をはっきりさせたり、
画像の解像度を上げるなどで改善の可能性はあるかと思います。

OCRの速度によっては、指定の文字の領域は塗りつぶしてしまって伏せ字に…とかできないかなあと思いました。