OpenVINO™ による光学式文字認識 (OCR)#
この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します:
このチュートリアルでは、OpenVINO モデルを使用して光学式文字認識 (OCR) を実行する方法を説明します。これは、テキスト検出のみを示す hello-detection チュートリアルの続きです。
horizontal-text-detection-0001 と text-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));

推論の実行#
画像内でテキストボックスが検出され、[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));

境界ボックスごとに OCR 結果を表示#
画像によっては、セル上にボックスが表示されている画像では OCR 結果が読み取れない場合があります。以下のコードを使用して、抽出されたボックスとボックスごとの OCR 結果を表示します。
for cropped_image, annotation in zip(cropped_images, annotations):
display(cropped_image, Markdown("".join(annotation)))

building

noyce

2200

n

center

robert
アノテーションをプレーンテキスト形式で印刷#
入力画像内の位置に基づいて、検出されたテキストのアノテーションを左上隅から印刷します。
[annotation for _, annotation in sorted(zip(boxes, annotations), key=lambda x: x[0][0] ** 2 + x[0][1] ** 2)]
['robert', 'n', 'noyce', 'building', '2200', 'center']