前回の第1回では、信頼度のしきい値を変えると結果がどう変わるかを調べ、結果をJSONで出力できるようにしました。ただ、「位置 (317, 247, 512, 477)」のような数字の並びでは、認識が合っているのかを判断しにくいものです。
今回は、認識した結果を元の画像に枠とラベルで描き込み、目で見て確認できるようにします。画像認識のシステムでは、この「見て確認できる画面」が、現場の合意を得るうえでとても重要になります。
この回で作るもの
- 検出した物体の枠と、種類・信頼度のラベルを画像に描く関数
draw_detections - コマンドに
--outを付けると、確認用の画像を保存する機能
仕組み:元の画像には触れず、描き込んだ「コピー」を保存する
処理の流れは次のとおりです。
- 元の画像を読み込む(Pillow。画像を扱うPythonの定番ライブラリ)
- 検出結果ごとに、枠(四角)とラベル(種類と信頼度の文字)を描く
- 別のファイルとして保存する(元の画像は書き換えない)
元の画像を書き換えない点は、業務システムでは大切です。証拠となる写真を、確認用の加工で汚さないためです。
実装
種類ごとに色を分ける
種類が違えば枠の色も変えると、一目で区別できます。
# app/draw.py(抜粋)
PALETTE = [(230, 57, 70), (29, 120, 220), (42, 157, 143), (244, 162, 97), (131, 56, 236), (6, 214, 160)]
def color_for(label: str) -> tuple[int, int, int]:
"""同じ種類には、実行のたびに同じ色を返す(Python の hash() は実行ごとに変わるので使わない)。"""
return PALETTE[sum(label.encode("utf-8")) % len(PALETTE)]
Pythonの標準の hash() は、実行のたびに値が変わる仕組みです。色の決定に使うと、同じ「orange」が今日と明日で違う色になってしまうため、文字のバイト値の合計で決めています。
枠とラベルを描く
# app/draw.py(抜粋)
def draw_detections(image_path: str | Path, detections: list[Detection], out_path: str | Path) -> Path:
"""枠・種類・信頼度を描いた画像を out_path に保存して、そのパスを返す。元の画像は書き換えない。"""
image = Image.open(image_path).convert("RGB")
draw = ImageDraw.Draw(image)
font = ImageFont.load_default(size=max(12, image.width // 40))
line = max(2, image.width // 200)
for d in detections:
color = color_for(d.label)
draw.rectangle(d.box, outline=color, width=line)
text = f"{d.label} {d.confidence:.2f}"
left, top, right, bottom = draw.textbbox((d.box[0], d.box[1]), text, font=font)
# ラベルは枠の上に置く。画像の上端からはみ出す場合は枠の内側に置く。
shift = 0 if top - 4 >= 0 else (bottom - top) + 6
draw.rectangle((left - 2, top - 4 + shift, right + 2, bottom + shift), fill=color)
draw.text((left, top - 2 + shift), text, fill=(255, 255, 255), font=font)
out = Path(out_path)
out.parent.mkdir(parents=True, exist_ok=True)
image.save(out)
return out
- 枠の太さと文字の大きさは、画像の幅に合わせて変えます(大きな写真でも文字が読めるように)
- 枠が画像の上の端にあるとき、ラベルが画像の外にはみ出さないよう、枠の内側へ置きます
- 出力先のフォルダが無ければ自動で作ります
コマンドに --out を追加
# app/main.py(変更部分)
parser.add_argument("--out", help="枠とラベルを描いた画像の保存先(例: out/result.png)")
...
if args.out:
saved = draw_detections(args.image, detections, args.out)
print(f"確認用の画像を保存しました: {saved}")
動かして確かめる
python -m app.main samples/fruits.jpg --out out/result.png
出力された画像が次のとおりです(開発環境で生成。元の写真はOpenCVのサンプル画像で、Apache-2.0ライセンスです)。

数字だけのときには気づきにくかった問題が、画像にすると一目で分かります。
- 切ったオレンジ(中央)に「orange 0.72」が付いています。これは合っています
- 右下のレモンに「orange 0.87」が付いています。信頼度が最も高いのに、間違いです
- 左下のキウイに「orange 0.47」、右上のライムに「orange 0.40」が付いています。これも間違いです
この結果から分かるとおり、信頼度が高いことは、正しいことの保証にはなりません。学習済みモデルが知らない果物(レモン・キウイ・ライム)を、似たものとして自信を持って間違えています。この問題は、第6回で「精度を測る」、第7回で「追加学習」として扱います。
テスト
pytest -q # 11件成功
ruff check . # 問題なし
描画のテストでは、白い画像に枠を描き、枠の位置の色が期待どおりか、元のファイルが変わっていないか、画像の端でも落ちないかを確認しています。
つまずきやすい点
- 日本語のラベルは文字化けする:標準のフォントは英語向けです。日本語のラベルを出す場合は、日本語フォントのファイルを指定する必要があります(この連載では英語のラベルのままにしています)
- 枠が画像の外に出る:座標が画像の外を指す場合に備え、実運用では描画前に範囲を切り詰めておくと安全です
- 確認用の画像に個人情報が残る:顔や車のナンバーが写る写真を扱う場合、確認用の画像の保存先・保管期間も個人情報の管理対象になります
発注者向けメモ:「見える化」は合意形成の道具
画像認識の検討では、数字(精度)だけでなく、実際の認識結果を画像で見て判断する場が欠かせません。今回のようにAIが自信を持って間違える例を、導入前に見ておくことで、現場の期待値を合わせられます。
発注者がやること チェックリスト
- ☐ 自社の実際の写真で、枠とラベルを描いた画像を見て、間違いの傾向を確認した
- ☐ 「信頼度が高い=正しい」ではないことを、関係者に共有した
- ☐ 確認用の画像を誰が・どこで見るか(画面の要否)を決めた
- ☐ 確認用の画像の保存先と保管期間を決めた(個人情報が写る場合は特に)
開発会社への質問例
- 「認識結果を画面で確認できる仕組みは、見積もりに含まれていますか」
- 「間違いやすい対象は、事前のテストでどのように洗い出しますか」
- 「確認用に保存する画像は、どこに何日間保存されますか。削除の手順はありますか」
まとめと次回予告
第2回では、次のことを行いました。
- 認識結果を、枠と種類・信頼度のラベルとして画像に描く関数を作った
- 元の画像は書き換えず、確認用のコピーを保存するようにした
- 画像で見ることで、「信頼度が最も高いレモンが間違い」という問題が一目で分かった
次回の第3回では、写真に何個写っているかを種類ごとに数え、CSVに集計します。フォルダ内の複数の写真もまとめて処理します。
この連載の記事一覧
この記事は連載「YOLOで作る画像認識ツール」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。
- 【YOLOで作る画像認識ツール 第0回】全体像と環境づくり:YOLOで最初の1枚を認識する
- 【YOLOで作る画像認識ツール 第1回】信頼度のしきい値とJSON出力:認識結果を「使える形」にする
- 【YOLOで作る画像認識ツール 第2回】認識結果を画像に描いて確認する:枠とラベルで「間違い」を見つけやすくする(この記事)
- 【YOLOで作る画像認識ツール 第3回】種類ごとに何個あるか数えて、CSVに集計する
- 【YOLOで作る画像認識ツール 第4回】FastAPIで画像認識を「API」にして、ほかのシステムから使えるようにする
- 【YOLOで作る画像認識ツール 第5回】ブラウザから写真をアップロードして、認識結果をその場で見る
- 【YOLOで作る画像認識ツール 第6回】精度を数字で測る:正解データと、適合率・再現率
- 【YOLOで作る画像認識ツール 第7回】学習済みモデルにない対象を覚えさせる:追加学習の手順と、結果の正しい読み方
- 【YOLOで作る画像認識ツール 第8回】自信の低い結果だけ人が確認する:AIと人の役割分担を設計する
- 【YOLOで作る画像認識ツール 第9回】速く・軽くする:ONNXに変換してCPUで動かし、変換前後を比べる
- 【YOLOで作る画像認識ツール 第10回】Dockerで動かす:設定・ログ・ヘルスチェックを運用に近い形にする
- 【YOLOで作る画像認識ツール 第11回(最終回)】運用の注意点とライセンス(AGPL-3.0):精度の低下に気づく仕組みと、連載のまとめ

