OpenVINO™ による光学式文字認識 (OCR)#

この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します:

Google ColabGitHub

このチュートリアルでは、OpenVINO モデルを使用して光学式文字認識 (OCR) を実行する方法を説明します。これは、テキスト検出のみを示す hello-detection チュートリアルの続きです。

horizontal-text-detection-0001text-recognition-resnet モデルは、テキスト検出とテキスト認識に使用されます。

このチュートリアルでは、モデル・ダウンローダー、モデル・コンバーター、情報ダンパーなどの Open Model Zoo ツールを使用して、Open Model Zoo からモデルをダウンロードして変換します。詳細については model-tools チュートリアルを参照してください。

目次:

import platform 

# openvino-dev パッケージをインストール 
%pip install -q "openvino-dev>=2024.0.0" onnx torch pillow opencv-python --extra-index-url https://download.pytorch.org/whl/cpu 

if platform.system() != "Windows":
     %pip install -q "matplotlib>=3.4" 
else:
     %pip install -q "matplotlib>=3.4,<3.7"
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed.This behaviour is the source of the following dependency conflicts. 
openvino-tokenizers 2024.4.0.0.dev20240712 requires openvino~=2024.4.0.0.dev, but you have openvino 2024.2.0 which is incompatible. 
Note: you may need to restart the kernel to use updated packages. 
Note: you may need to restart the kernel to use updated packages.

インポート#

from pathlib import Path 

import cv2 
import matplotlib.pyplot as plt 
import numpy as np 
import openvino as ov 
from IPython.display import Markdown, display 
from PIL import Image 

# `notebook_utils` モジュールを取得 
import requests 

r = requests.get( 

url="https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py", 
) 

open("notebook_utils.py", "w").write(r.text) 
from notebook_utils import load_image

設定#

core = ov.Core() 

model_dir = Path("model") 
precision = "FP16" 
detection_model = "horizontal-text-detection-0001" 
recognition_model = "text-recognition-resnet-fc" 

model_dir.mkdir(exist_ok=True)

モデルのダウンロード#

次のセルではモデル・ダウンローダーを実行して、検出モデルと認識モデルをダウンロードします。モデルが以前にダウンロードされている場合、再度ダウンロードすることはできません。

download_command = ( 
    f"omz_downloader --name {detection_model},{recognition_model} --output_dir {model_dir} --cache_dir {model_dir} --precision {precision} --num_attempts 5" 
) 
display(Markdown(f"Download command: `{download_command}`")) display(Markdown(f"Downloading {detection_model}, {recognition_model}...")) !$download_command 
display(Markdown(f"Finished downloading {detection_model}, {recognition_model}.")) 

detection_model_path = (model_dir / "intel/horizontal-text-detection-0001" / precision / detection_model).with_suffix(".xml") 
recognition_model_path = (model_dir / "public/text-recognition-resnet-fc" / precision / recognition_model).with_suffix(".xml")

ダウンロード・コマンド: omz_downloader --name horizontal-text-detection-0001,text-recognition-resnet-fc --output_dir model --cache_dir model --precision FP16 --num_attempts 5

Downloading horizontal-text-detection-0001, text-recognition-resnet-fc…

################|| Downloading horizontal-text-detection-0001 ||################ 
========== Downloading model/intel/horizontal-text-detection-0001/FP16/horizontal-text-detection-0001.xml 
========== Downloading model/intel/horizontal-text-detection-0001/FP16/horizontal-text-detection-0001.bin 
################|| Downloading text-recognition-resnet-fc ||################ 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/model.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/weight_init.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/heads/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/heads/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/heads/fc_head.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/heads/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/body.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/component.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/sequences/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/sequences/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/sequences/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/bricks/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/bricks/bricks.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/bricks/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/bricks/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/resnet.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/enhance_modules/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/enhance_modules/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/enhance_modules/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/utils/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/utils/builder.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/utils/conv_module.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/utils/fc_module.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/utils/norm.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/models/utils/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/utils/__init__.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/utils/common.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/utils/registry.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/utils/config.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/configs/resnet_fc.py 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/ckpt/resnet_fc.pth 
========== Downloading model/public/text-recognition-resnet-fc/vedastr/addict-2.4.0-py3-none-any.whl 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/heads/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/sequences/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/component.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/decoders/bricks/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/enhance_modules/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/utils/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/utils/__init__.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/utils/config.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/utils/config.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/utils/config.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/utils/config.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/utils/config.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/resnet.py 
========== Replacing text in model/public/text-recognition-resnet-fc/vedastr/models/bodies/feature_extractors/encoders/backbones/resnet.py 
========== Unpacking model/public/text-recognition-resnet-fc/vedastr/addict-2.4.0-py3-none-any.whl

Finished downloading horizontal-text-detection-0001, text-recognition-resnet-fc.

### text-recognition-resnet-fc モデルは多数のファイルで構成されています。すべてのファイル名は、 
### モデル・ダウンローダーの出力に印刷されます。この出力を表示するには、次の 2 行のコメントを解除します 
# for line in download_result: 
#     print(line)

モデルの変換#

ダウンロードされた検出モデルはインテルのモデルであり、すでに OpenVINO 中間表現 (OpenVINO IR) 形式になっています。テキスト認識モデルはパブリックモデルであり、OpenVINO IR に変換する必要があります。このモデルは Open Model Zoo からダウンロードされたものであるため、モデル・コンバーターを使用してモデルを OpenVINO IR 形式に変換します。

モデル・コンバーターの出力が表示されます。変換が成功すると、出力の最後の行には [ SUCCESS ] Generated IR version 11 model. が含まれます

convert_command = f"omz_converter --name {recognition_model} --precisions {precision} --download_dir {model_dir} --output_dir {model_dir}" 
display(Markdown(f"Convert command: `{convert_command}`")) 
display(Markdown(f"Converting {recognition_model}...")) 
    ! $convert_command

変換コマンド: omz_converter --name text-recognition-resnet-fc --precisions FP16 --download_dir model --output_dir model

Converting text-recognition-resnet-fc…

========== Converting text-recognition-resnet-fc to ONNX
Conversion to ONNX command: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/bin/python -- /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages/omz_tools/internal_scripts/pytorch_to_onnx.py --model-path=/opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages/omz_tools/models/public/text-recognition-resnet-fc --model-path=model/public/text-recognition-resnet-fc --model-name=get_model --import-module=model '--model-param=file_config=r"model/public/text-recognition-resnet-fc/vedastr/configs/resnet_fc.py"' '--model-param=weights=r"model/public/text-recognition-resnet-fc/vedastr/ckpt/resnet_fc.pth"' --input-shape=1,1,32,100 --input-names=input --output-names=output --output-file=model/public/text-recognition-resnet-fc/resnet_fc.onnx 

ONNX check passed successfully.
========== Converting text-recognition-resnet-fc to IR (FP16) 
Conversion command: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/bin/python -- /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/.venv/bin/mo --framework=onnx --output_dir=model/public/text-recognition-resnet-fc/FP16 --model_name=text-recognition-resnet-fc --input=input '--mean_values=input[127.5]' '--scale_values=input[127.5]' --output=output --input_model=model/public/text-recognition-resnet-fc/resnet_fc.onnx '--layout=input(NCHW)' '--input_shape=[1, 1, 32, 100]' --compress_to_fp16=True 

[ INFO ] MO command line tool is considered as the legacy conversion API as of OpenVINO 2023.2 release. In 2025.0 MO command line tool and openvino.tools.mo.convert_model() will be removed. Please use OpenVINO Model Converter (OVC) or openvino.convert_model(). OVC represents a lightweight alternative of MO and provides simplified model conversion API.Find more information about transition from MO to OVC at https://docs.openvino.ai/2023.2/openvino_docs_OV_Converter_UG_prepare_model_convert_model_MO_OVC_transition.html 
[ INFO ] Generated IR will be compressed to FP16. If you get lower accuracy, please consider disabling compression explicitly by adding argument --compress_to_fp16=False. Find more information about compression to FP16 at https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html 
[ SUCCESS ] Generated IR version 11 model. 
[ SUCCESS ] XML file: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/notebooks/optical-character-recognition/model/public/text-recognition-resnet-fc/FP16/text-recognition-resnet-fc.xml 
[ SUCCESS ] BIN file: /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-727/.workspace/scm/ov-notebook/notebooks/optical-character-recognition/model/public/text-recognition-resnet-fc/FP16/text-recognition-resnet-fc.bin

推論デバイスの選択#

OpenVINO を使用して推論を実行するためにドロップダウン・リストからデバイスを選択します

import ipywidgets as widgets 

device = widgets.Dropdown( 
    options=core.available_devices + ["AUTO"], 
    value="AUTO", 
    description="Device:", 
    disabled=False, 
) 

device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')

オブジェクト検出#

検出モデルをロードし、画像をロードし、推論を実行して、検出推論結果を取得します。

検出モデルをロード#

detection_model = core.read_model(model=detection_model_path, weights=detection_model_path.with_suffix(".bin")) 
detection_compiled_model = core.compile_model(model=detection_model, device_name=device.value) 

detection_input_layer = detection_compiled_model.input(0)

画像のロード#

# `image_file` 変数は URL またはローカル画像を指します 
image_file = 
"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/intel_rnb.jpg" 

image = load_image(image_file) 

# N、C、H、W = バッチサイズ、チャネル数、高さ、幅
N, C, H, W = detection_input_layer.shape 

# ネットワークの予想される入力サイズに合わせて画像のサイズを変更 
resized_image = cv2.resize(image, (W, H)) 

# ネットワーク入力の形状に合わせて形状を変更 
input_image = np.expand_dims(resized_image.transpose(2, 0, 1), 0) 

plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB));
../_images/optical-character-recognition-with-output_16_0.png

推論の実行#

画像内でテキストボックスが検出され、[100, 5] 形状のデータのブロブとして返されます。検出の各説明は [x_min, y_min, x_max, y_max, conf] の形式になります。

output_key = detection_compiled_model.output("boxes") 
boxes = detection_compiled_model([input_image])[output_key] 

# ゼロのみのボックスを削除 
boxes = boxes[~np.all(boxes == 0, axis=1)]

検出結果を取得#

def multiply_by_ratio(ratio_x, ratio_y, box): 
    return [max(shape * ratio_y, 10) if idx % 2 else shape * ratio_x for idx, shape in enumerate(box[:-1])] 

def run_preprocesing_on_crop(crop, net_shape): 
    temp_img = cv2.resize(crop, net_shape) 
    temp_img = temp_img.reshape((1,) * 2 + temp_img.shape) 
    return temp_img 

def convert_result_to_image(bgr_image, resized_image, boxes, threshold=0.3, conf_labels=True): 
    # ボックスと説明の色を定義 
    colors = {"red": (255, 0, 0), "green": (0, 255, 0), "white": (255, 255, 255)} 
    # 画像の形状を取得して比率を計算
    (real_y, real_x), (resized_y, resized_x) = image.shape[:2], resized_image.shape[:2] 
    ratio_x, ratio_y = real_x / resized_x, real_y / resized_y 

    # ベースイメージを BGR 形式から RGB 形式に変換 
    rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB) 

    # ゼロ以外のボックスを反復処理 
    for box, annotation in boxes: 
        # 配列の最後の場所から信頼係数を選択 
        conf = box[-1] 
        if conf > threshold: 
        # float を int に変換し、各ボックスのコーナーの位置に x と y の比率を掛ける
        (x_min, y_min, x_max, y_max) = map(int, multiply_by_ratio(ratio_x, ratio_y, box)) 
        # 位置に基づいてボックスを描画。`rectangle` 関数のパラメーターは、image、start_point、end_point、color、thickness です 
        cv2.rectangle(rgb_image, (x_min, y_min), (x_max, y_max), colors["green"], 3) 

        # 位置と信頼度に基づいて画像にテキストを追加します。`putText` 関数のパラメーターは、image、text、bottomleft_corner_textfield、font、font_scale、color、thickness、line_type です 
        if conf_labels: 
            # アノテーションの長さに基づいて背景ボックスを作成
            (text_w, text_h), _ = cv2.getTextSize(f"{annotation}", cv2.FONT_HERSHEY_TRIPLEX, 0.8, 1) 
            image_copy = rgb_image.copy() 
            cv2.rectangle( 
                image_copy, 
                (x_min, y_min - text_h - 10), 
                (x_min + text_w, y_min - 10), 
                colors["white"], 
                -1, 
            ) 
            # テキストの下に白いボックスが付いた重み付けされた画像コピーを追加 
            cv2.addWeighted(image_copy, 0.4, rgb_image, 0.6, 0, rgb_image) 
            cv2.putText( 
                rgb_image, 
                f"{annotation}", 
                (x_min, y_min - 10), 
                cv2.FONT_HERSHEY_SIMPLEX, 
                0.8, 
                colors["red"], 
                1, 
                cv2.LINE_AA, 
            ) 
    return rgb_image

テキスト認識#

テキスト認識モデルをロードし、検出モデルから検出されたボックスに対して推論を実行します。

テキスト認識モデルをロード#

recognition_model = core.read_model(model=recognition_model_path, weights=recognition_model_path.with_suffix(".bin")) 

recognition_compiled_model = core.compile_model(model=recognition_model, device_name=device.value) 

recognition_output_layer = recognition_compiled_model.output(0) 
recognition_input_layer = recognition_compiled_model.input(0) 

# 入力レイヤーの高さと幅を取得 
_, _, H, W = recognition_input_layer.shape

推論の実行#

# 画像のサイズ変更のスケールを計算(real_y, real_x), (resized_y, resized_x) = image.shape[:2], resized_image.shape[:2] 
ratio_x, ratio_y = real_x / resized_x, real_y / resized_y 

# テキスト認識モデル用に画像をグレースケールに変換 
grayscale_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 

# モデルのドキュメントに基づいて、出力をエンコードするための辞書を取得 
letters = "~0123456789abcdefghijklmnopqrstuvwxyz" 

# アノテーション用の空のリストを準備 
annotations = list() 
cropped_images = list() 
# fig, ax = plt.subplots(len(boxes), 1, figsize=(5,15), sharex=True, sharey=True) 
# 検出モデルによって指定されたボックスに基づいて、各作物のアノテーションを取得 
for i, crop in enumerate(boxes): 
    # 作物の角の座標を取得
    (x_min, y_min, x_max, y_max) = map(int, multiply_by_ratio(ratio_x, ratio_y, crop)) 
    image_crop = run_preprocesing_on_crop(grayscale_image[y_min:y_max, x_min:x_max], (W, H)) 

    # 認識モデルを使用して推論を実行 
    result = recognition_compiled_model([image_crop])[recognition_output_layer] 

    # 出力を圧縮して不要な次元を削除 
    recognition_results_test = np.squeeze(result) 

    # 出力レイヤーからの確率に基づいて注釈を読み取り 
    annotation = list() 
    for letter in recognition_results_test: 
        parsed_letter = letters[letter.argmax()] 

        # `argmax` から 0 インデックスを返すと、文字列の終了が示されます 
        if parsed_letter == letters[0]: 
            break 
        annotation.append(parsed_letter) 
    annotations.append("".join(annotation)) 
    cropped_image = Image.fromarray(image[y_min:y_max, x_min:x_max]) 
    cropped_images.append(cropped_image) 

boxes_with_annotations = list(zip(boxes, annotations))

結果の表示#

画像の検出されたテキストボックスと OCR 結果を表示#

認識されたテキストの周囲にボックスを描画し、テキスト認識モデルからの OCR 結果を表示することで、結果を視覚化します。

plt.figure(figsize=(12, 12)) 
plt.imshow(convert_result_to_image(image, resized_image, boxes_with_annotations, conf_labels=True));
../_images/optical-character-recognition-with-output_26_0.png

境界ボックスごとに OCR 結果を表示#

画像によっては、セル上にボックスが表示されている画像では OCR 結果が読み取れない場合があります。以下のコードを使用して、抽出されたボックスとボックスごとの OCR 結果を表示します。

for cropped_image, annotation in zip(cropped_images, annotations): 
    display(cropped_image, Markdown("".join(annotation)))
../_images/optical-character-recognition-with-output_28_0.png

building

../_images/optical-character-recognition-with-output_28_2.png

noyce

../_images/optical-character-recognition-with-output_28_4.png

2200

../_images/optical-character-recognition-with-output_28_6.png

n

../_images/optical-character-recognition-with-output_28_8.png

center

../_images/optical-character-recognition-with-output_28_10.png

robert