【Objective-C】Vision.Frameworkを使って画像からテキスト検出する方法(VNDetectText)

2020年8月26日

iOS11から追加されたVision.Frameworkでは画像から以下の検出ができるようになりました。

  • Face Detection and Recognition:顔とパーツの認識
  • Machine Learning Image Analysis:機械学習による画像分析
  • Barcode Detection:バーコードの検出
  • Image Alignment Analysis:画像の並び解析
  • Text Detection:テキストの検出
  • Horizon Detection:水平角の検出
  • Object Detection and Tracking:物体検出と追跡

今回は上記の中でも画像からテキストの検出(Text Detection)する方法をご紹介します。
テキストの検出では文字列文字の両方の位置を検出することができます。
これにより画像のどの部分が文字なのかを判定できるようになるわけですね!
早速実践してみましょう。

Vision.Frameworkを使って画像からテキスト検出する方法

#import <Vision/Vision.h>

はじめに、Vision.Frameworkをインポートします。

- (void)detectWithImage:(UIImage *)image {
    
    // 元画像の表示
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    imageView.frame = CGRectMake((self.view.frame.size.width - imageView.frame.size.width)/2,
                                 (self.view.frame.size.height - imageView.frame.size.height)/2,
                                 imageView.frame.size.width,
                                 imageView.frame.size.height);
    [self.view addSubview:imageView];
    
    // 画像からテキストを検出
    VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithCIImage:[[CIImage alloc] initWithImage:image] options:@{}];
    VNDetectTextRectanglesRequest *request = [[VNDetectTextRectanglesRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
        if (!error) {
            for (VNTextObservation *textObservation in request.results) {
                NSLog(@"%@",NSStringFromCGRect(textObservation.boundingBox));
                for (VNRectangleObservation *rectangleObservation in textObservation.characterBoxes) {
                    NSLog(@" |-%@", NSStringFromCGRect(rectangleObservation.boundingBox));
                }
            }
            
            // テキストを検出した範囲を表示(赤い四角と青い四角)
            UIImageView *imageView = [[UIImageView alloc] initWithImage:[self overlayImageWithTextObservations:request.results size:image.size]];
            imageView.frame = CGRectMake((self.view.frame.size.width - imageView.frame.size.width)/2,
                                         (self.view.frame.size.height - imageView.frame.size.height)/2,
                                         imageView.frame.size.width,
                                         imageView.frame.size.height);
            [self.view addSubview:imageView];
        }
        else {
            NSLog(@"%@",error);
        }
    }];
    request.reportCharacterBoxes = YES;
    NSError *error;
    [handler performRequests:@[request] error:&error];
    if (error) {
        NSLog(@"%@",error);
    }
}

次に画像から文字を検出する実装します。
これだけではどの範囲が文字として検出されたかわかりづらいので、文字として検出された部分を赤い四角と青い四角で囲った画像を返すようにするメソッドを追加します。

- (UIImage *)overlayImageWithTextObservations:(NSArray<VNTextObservation *> *)results size:(CGSize)size {
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
    UIImage *overlayImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        CGAffineTransform transform = CGAffineTransformIdentity;
        transform = CGAffineTransformScale(transform, size.width, -size.height);
        transform = CGAffineTransformTranslate(transform, 0, -1);
        for (VNTextObservation *textObservation in results) {
            [[UIColor redColor] setStroke];
            [[UIBezierPath bezierPathWithRect:CGRectApplyAffineTransform(textObservation.boundingBox, transform)] stroke];
            for (VNRectangleObservation *rectangleObservation in textObservation.characterBoxes) {
                [[UIColor blueColor] setStroke];
                [[UIBezierPath bezierPathWithRect:CGRectApplyAffineTransform(rectangleObservation.boundingBox, transform)] stroke];
            }
        }
    }];
    return overlayImage;
}

たったこれだけです!
使用する方法も簡単です。

[self detectWithImage:[UIImage imageNamed:@"2.png"]];

このようにUIImageの画像を渡すだけです。
早速サンプル画像でどんな具合に文字検出がされるか試してみましょう。

小文字のアルファベットも大文字のアルファベットも正常に認識されていますね!
位置も大きさも完璧です。
フォントによっては多少の違いが出ると思いますがまずまずの動きだと思います。

続いては、ひらがな、カタカナ、漢字です。
ひらがなに関しては点など少し離れた位置にあるものを認識してくれなかったり、逆に点単体を認識してしまったりと精度は微妙そうです。漢字に至っては認識すらしてくれなさそうです、、、。
唯一使えそうなのはカタカナでしょうか、位置もサイズも大丈夫そうですが、こちらもガギグゲゴなどの濁点には対応できなそうな感じがするのでオススメはできません。

まとめ

画像からのテキスト検出はとても簡単だが、日本語の精度は悪い。
英語だけの精度ならかなり良さそうなので、英語に限定した使い方なら良好そう。