画像認識の記事の中身を表紙で伝える ― サムネイル13本を実写と数字に
この記事の素材(コード・ログ・データ 1ファイル・まとめてダウンロードあり)arrow_downwardAIで読む ChatGPT・Claude などで、この記事に質問できます expand_more
画像認識と物体検出のシリーズの記事サムネイル13枚を、その回で撮った実写を白黒にし、その回で測った数字と、色を1つだけ重ねる形に作り直した。 記事一覧のカードを見ただけで、各回が何を確かめて何が分かったかを、写真と数字で読み取れるようにするためだ。たとえば光の色を変える回なら、同じ青い面の色相(色合いを角度で表した数字)が 206° から 51° に動いたことが、2枚の写真と数字でカードに出る。
作り直したことで、次のことが変わった。
- シリーズのページ(pixelstomotors.com/series7 )を開いた人が、記事を開く前に、各回で使った写真と、その回で測った数字を見られる。前のサムネイルは多くが図形を描いた絵で、何を測ったかは記事を開くまで分からなかった
- カードの数字は、記事の実測値か、写真から計算し直した値だけにした。光の色を変えたときに赤の判定が画面の何割を拾うかは、記事と同じしきい値で計算し直して 96.3% になり、記事の値と一致した
- 13枚は、スクリプト1本(make.py)で作り直せる。2コアの CPU で、13枚を通しで作って約5秒。前のサムネイルは1枚ずつ HTML で手で組んでいて、作るのにかかった時間は測っていない
サムネイルは、記事一覧のカードの上に出る絵のこと。画像認識と物体検出のシリーズは、ロボットのカメラが物を見つける仕組みを下の層から学び直す記録で、どの回も、机の上のロボットアーム(mechArm 270)やブロックを撮った写真で1つずつ測っている。ところがカードの絵は、灰色の四角や丸を並べた記号の絵や、英語の見出しと図を組んだ白黒の図が多かった。写真で確かめているシリーズなのに、カードには写真がない。並べると、どれも同じ種類の絵に見えた。


やったことの全体は次のとおり。
| 順 | やったこと | なぜ | 結果 |
|---|---|---|---|
| 1 | トップのカードと同じ形(同じ写真を4列に並べ、左から処理が進む)で3枚を試作 | トップのシリーズのカードは、赤い箱の写真が処理されていく4列の図になっている | 3枚とも同じ並べ方で、赤が多すぎた |
| 2 | 並べ方を回ごとに変え、写真を白黒にして色を1か所だけにした3枚を作り直した | 「全部縦4つでなくていい」「赤は差し色程度」と指示を受けた | この方向で進めることになった |
| 3 | 残りの回にも広げ、差し色を回ごとに選んだ | 「差し色は赤に絞らなくていい」と指示を受けた | Log#1〜#12 の12枚を公開 |
| 4 | LOG236 のサムネイルも同じ形にした | LOG236 だけ、SAM 3 の画面の写真のままだった | LOG236 を13枚目として公開 |
このサムネイルの形には、5つの考えがある。
- 写真を主役にする。画像認識のシリーズはどの回も写真で測っているので、測った写真そのものをカードに出す
- 写真は白黒にし、色は1つだけにする。画像認識のシリーズでは、色そのものが調べる対象になる(赤いブロック、青い面)。写真を全部カラーにすると、どの色を見ればいいのかが分からなくなる
- 数字は1つだけにし、測った値だけを書く。カードに置く数字は、その回でいちばん大事な結果1つ
- 並べ方は回ごとに変える。一覧ではカードが横に並ぶので、同じ並べ方が続くと、どれも同じに見える
- 英語は小さなラベルだけにする。見出しの代わりに、写真と数字に語らせる
ここから下は、作業の記録になる。
作業ログ ― トップのカードと同じ4列の形で3枚を試作した
最初は、トップのシリーズのカードに形を合わせた。トップのカード(tools/series_cards/series7.py で作っている)は、赤い箱の写真を4列に並べ、左から 画素 → 白黒 → しきい値 → 輪郭 と処理が進む順に見せる図になっている。同じ形で、Log#2(画素の数字)・Log#5(モルフォロジー)・Log#6(円形度)の3枚を作った。

作って並べると、3枚とも縦4列で同じに見えた。Log#2 の3列目は、1画素の色をそのまま列全体に塗ったので、カードの4分の1が赤になった。これを見て、次の指示を受けた。
全部縦4つの分け方でなくていいよ。バリエーションを作ってください。赤は差し色程度の扱いにしてください。
差し色は、白黒やモノクロの画面の中で、1か所だけに使う色のこと。目がそこへ行く。
作業ログ ― 並べ方を回ごとに変え、写真を白黒にして色を1か所にした
3枚を、並べ方の違う形で作り直した。写真はどれも白黒にし、色はその回で見てほしい所だけに残した。
| 回 | 並べ方 | 色を残した所 |
|---|---|---|
| Log#2 画素の数字 | 左に写真、右に写真の一部を6×6のマスに拡大し、マスごとに赤の値を書く | 拡大したマスのうち、本当の色で塗った真ん中の1マス |
| Log#5 モルフォロジー | 上に写真の横長の帯、下にしきい値・オープニング・クロージングの3枚 | 写真の中の赤いブロックだけ |
| Log#6 円形度 | 左半分が写真、右半分が輪郭の図と数字 | 写真の中の測った円と、図の重心の十字 |

- ねらい:一覧で、どのカードも同じ種類の絵に見えないようにする。色で、その回の主役を指す
- 変わったこと:3枚とも並べ方が違う。赤はカードの中の1か所だけになった(試作1の Log#2 は、カードの4分の1が赤だった)
- 見込める効果:一覧に並べたとき、隣のカードと区別しやすくなる見込み。カードから記事を開く数が変わるかは、サイトの計測で確かめる
この方向で残りの回にも広げることになり、続けて次の指示を受けた。
差し色は赤に絞らないで他の色でもOK
赤いブロックが主役の回は赤のままにし、青い面の回は青、オレンジの光の回はオレンジのように、その回の中身に合った色を1つ選ぶことにした。
作業ログ ― 残りの回のサムネイルを、回の中身に合わせて作った
12枚それぞれで、その回の中身に合う並べ方と色を決めた。素材は、その回の記事で撮った写真か、実機の録画から取ったコマ。Log#10 だけは、記事で使った生成画像(AI に作らせた資源ごみのトレイの画像)をそのまま使った。
| 回 | 並べ方 | 差し色(どこに) | 入れた数字(どこから) | 素材 |
|---|---|---|---|---|
| #1 9つの層 | 写真+9つの層の積み上げ | 黄(前倒しして3番目に学ぶ評価指標の層) | なし | 真上のカメラの写真(Log05) |
| #2 画素の数字 | 写真の一部を拡大していく | 赤(本当の色の1マス) | R 115 / G 17 / B 16(写真のそのマスから計算) | 赤い箱の写真(Log02 IMG_7901) |
| #3 光の色と色相 | 2枚を左右に並べる | 青(白い光の下の色相の数字) | 206° → 51°(記事の実測 205.9° → 50.9°) | 同じ配置の2枚(Log03 IMG_7913・7914) |
| #4 赤の判定の失敗 | 大きな判定結果+4条件の写真の列 | オレンジ(オレンジの光の写真と割合) | 96.3%(記事と同じしきい値で計算し直し、一致) | 光だけ変えた4枚(Log03 IMG_7911〜7914) |
| #5 モルフォロジー | 写真の帯+3枚 | 赤(写真の中のブロック) | 36 → 1(この絵のために計算) | 真上のカメラの写真(Log05) |
| #6 円形度 | 左右2分割 | 赤(測った円と重心) | 0.898(写真の輪郭から計算。記事の値と同じ) | 30°傾けた図形の紙(Log06) |
| #7 特徴点マッチング | 2枚を対応の線で結ぶ | 緑(対応の線と数) | 867 / 934(記事の実測) | 本の写真と、本のないマットの写真(Log07) |
| #8 射影変換 | 斜めの写真 → 真上から見た形 | 水色(紙の4つの角) | なし | 斜めに置いた紙(Log08 tilt_30) |
| #9 用語の5段 | 用語の5段+写真の帯 | 黄(土台の第0段) | なし | 真上のカメラの写真(Log05) |
| #10 SAM 3 | 生成画像に4つの枠 | 青緑(同じ物とされた4本) | same 4(記事の結果) | 記事で使った生成画像(Log10) |
| #11 持ち上がったか | 2コマを並べる | 赤(写真の中のブロック) | なし | 実機の録画の440・560コマ目 |
| #12 答えの6つの形 | 写真+6つの答えの格子 | 黄(見出しの2行目) | なし | 真上のカメラの写真と、公開中の6枚 |

数字を入れた回は、その数字がどこから来たかを決めてから入れた。Log#4 の 96.3% は、記事と同じしきい値(白い光の写真1枚から決めた thresholds_white.json)で、4枚の写真の全画素を数え直した。白い光の3枚が 1.9%・2.1%・2.3%、オレンジの光の1枚が 96.3% で、記事の値と一致した。Log#5 の「36 → 1」は記事の数字ではない。この絵のために、赤のしきい値をゆるめてマットの目盛りも拾うようにし、塊を数えた(36個)。5×5のオープニング(小さな粒を消す処理)をかけると、ブロックの1個だけが残った。
Log#7 の線は、ORB(画像から目印になる点を拾い、2枚の間で対応を探す OpenCV の手法)で2枚の間を実際に結び、上位400本から70本を選んで描いた。カードの「867 / 934」は記事の実測で、線の本数とは別のもの。
LOG236 のサムネイルを、映り込みにかかったマスクを主役にした
LOG236(SAM 3 で器具を切り出したら、トレイの映り込みまでマスクされた記録)だけ、サムネイルが SAM 3 の操作画面を撮った写真のままだった。同じシリーズの一覧に並ぶので、これも同じ形にそろえるよう指示を受けた。
SAM 3 は Meta が公開している、言葉やクリックで指定した物の形を切り出すモデル。LOG236 の記事に載せた動画は、SAM 3 が器具に色の輪郭(マスク)を付けた画面を録ったもの。その110コマ目で、ステンレスのトレイに映った像にまで青いマスクが付いている。このコマを白黒にし、映り込みのマスクだけ青を残した。右には「MASKED A MIRROR IMAGE.」と置いた。

- ねらい:記事で分かったこと(映り込みも「ある」と見る)を、カードの絵の中で見せる
- 変わったこと:操作画面の全体ではなく、映り込みにマスクが付いた1か所が主役になった
- 見込める効果:一覧で、操作画面の写真よりも、何が起きた記事なのかが伝わる見込み
作り直した後のシリーズのページ


画像認識のシリーズのサムネイルは、写真に色を1つ足す形になった。記事の決まり(article_rules.md)のサムネイルの節はモノクロのままで、シリーズごとにサムネイルを変える見直しを別に進めている。それが終わったら書き直す。この記事はサイトの作業の記録なので、ひとり研究所の裏側に置き、サムネイルは今の決まりどおりモノクロの HTML で作った(前と後の Log#3 を白黒で並べた)。
作業ログ ― サムネイルをサイトに入れる仕組み
この記事のコード・ログ・データは、1ファイルずつ下に置いています。全文はリンク先にあります。
ファイルの一覧(1本)
01_make.py.txt ― make.py(全文)― 13枚のサムネイルを作る・21,405字
画像認識のシリーズのサムネイルは、HTML の中に図を組む形だった(public/thumbs/ の HTML を、一覧のカードが iframe で読み込む)。作り直した絵は画像なので、HTML の中身を「画像を1枚見せるだけ」に書き換え、画像は public/thumbs/img/LOG{N}.webp に置いた。webp は、jpg より軽い画像の形式。カードの仕組み(RecordCard.astro)は変えていない。
LOG236 は Studio から移した記事で、サムネイルが HTML ではなく画像(cover)になっている。src/data/cards.json と src/data/records/sam3-mirror-reflection-grasp-problem.json の cover を、/media/s-1920x1080_f24658f9-fd06-4c0b-9c47-a870ad20dea0.webp から /thumbs/img/LOG236.webp に書き換えた。前の絵は R2 に残っている。
SNS にリンクを貼ったときに出る画像(public/og/)は、サムネイルの HTML から Playwright(ブラウザを動かすための道具)で書き出している。12本とも tools/render_og.py で書き出し直した。LOG236 は cover の画像がそのまま SNS の画像になる。
動かしたコマンドと出力
$ cd tools/vision_thumbs && time python3 make.py
LOG 236
LOG 298
LOG 299
LOG 300
301 rates {'BASE': np.float64(0.019272982148106157), 'DIM': np.float64(0.020775430156630553), 'BRIGHT': np.float64(0.02252646814898799), 'ORANGE': np.float64(0.9633497496220711)}
LOG 301
LOG 302
302 36 1
LOG 304
304 0.8978910238079594
LOG 309
309 orb matches 512
LOG 310
LOG 313
LOG 317
LOG 325
LOG 459
real 0m4.943s
user 0m4.788s
sys 0m0.578s
CPU は2コア(nproc で 2)。Python 3.13.18、OpenCV 5.0.0、NumPy 2.5.3、Pillow 12.3.0。
$ python3 tools/vision_thumbs/publish.py
298 r-image-recognition-object-detection-overview
299 r-where-pixel-values-change
300 r-hue-holds-color-breaks
301 r-rgb-hsv-threshold-lighting-failure
302 r-morphology-kernel-fits-or-vanishes
304 r-what-circularity-actually-measures
309 r-same-place-different-object
310 r-tilted-plane-to-flat
313 r-vocabulary-of-modern-vision
317 r-sam3-first-look-sorting-line
325 r-decisions-api-grasp-check
459 LOG459
$ for n in r-image-recognition-object-detection-overview r-where-pixel-values-change r-hue-holds-color-breaks r-rgb-hsv-threshold-lighting-failure r-morphology-kernel-fits-or-vanishes r-what-circularity-actually-measures r-same-place-different-object r-tilted-plane-to-flat r-vocabulary-of-modern-vision r-sam3-first-look-sorting-line r-decisions-api-grasp-check LOG459; do python3 tools/render_og.py $n 2>&1 | tail -1; done
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
書き出し 1 件 → public/og/
$ npm run build 2>&1 | tail -1
16:28:22 [build] Complete!
公開した commit は、5013a43(12本)、b781c44(途中の PNG を git の対象から外す)、9fe6449(LOG236)。前の HTML のサムネイルは git の履歴に残っている。
make.py(全文)― 13枚のサムネイルを作る
"""画像認識と物体検出(/series7)の記事サムネイル12本を作る(2026-11-08)。
トップのカードと同じく、実写の写真と、その回で計算した結果を組み合わせる。並べ方は回ごとに変え、差し色は1枚に1色だけ。
数字はすべて記事の実測値か、この中で写真から計算した値。
素材(KIOXIA の r2r2r-data と、記事の作業フォルダ)は Mac 連携で /mnt/user-data/uploads/ に写してから動かす。
python3 make.py # 12本を out/ に PNG で書き出す
出力は out/LOG{N}.png(1920×1080)。webp にして public/thumbs/img/ へ置く(publish.py)。
"""publish.py(全文)― webp にしてサムネイルの HTML を書き換える
"""LOG236 は Studio から移した記事でサムネイルが画像(cover)のため、src/data/cards.json と records/ の cover を /thumbs/img/LOG236.webp に向けてある(前の絵は R2 の s-1920x1080_f24658f9-….webp)。
make.py の out/LOG{N}.png を webp にして public/thumbs/img/ に置き、記事のサムネイルの HTML を、その画像を見せるだけの形に書き換える(2026-11-08)。
前の HTML のサムネイルは git の履歴に残っている。書き換えたあと tools/render_og.py で SNS 用の画像も作り直す。"""
import os
from PIL import Image
SITE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
HERE = os.path.dirname(os.path.abspath(__file__))
THUMB = {298: 'r-image-recognition-object-detection-overview', 299: 'r-where-pixel-values-change', 300: 'r-hue-holds-color-breaks',
301: 'r-rgb-hsv-threshold-lighting-failure', 302: 'r-morphology-kernel-fits-or-vanishes', 304: 'r-what-circularity-actually-measures',
309: 'r-same-place-different-object', 310: 'r-tilted-plane-to-flat', 313: 'r-vocabulary-of-modern-vision',
317: 'r-sam3-first-look-sorting-line', 325: 'r-decisions-api-grasp-check', 459: 'LOG459'}
HTML = '''<!DOCTYPE html>
<!-- 画像認識シリーズのサムネイル。画像は tools/vision_thumbs/make.py で作る(2026-11-08) -->
<html lang="ja"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>*{{margin:0;padding:0}}html,body{{background:#111;height:100%;overflow:hidden}}img{{display:block;width:100%;height:100%;object-fit:cover}}</style>
</head><body><img src="img/LOG{n}.webp" alt=""></body></html>
'''
os.makedirs(f'{SITE}/public/thumbs/img', exist_ok=True)
for n, name in THUMB.items():
Image.open(f'{HERE}/out/LOG{n}.png').convert('RGB').save(f'{SITE}/public/thumbs/img/LOG{n}.webp', quality=86)
open(f'{SITE}/public/thumbs/{name}.html', 'w').write(HTML.format(n=n))
print(n, name)
使った素材の場所
| 回 | 素材のパス |
|---|---|
| #1・#5・#9 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log05/mat_white_ref_20260729_211605_f0.png |
| #12 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log05/mat_orange_20260729_212044_f0.png と p2m-site の public/site/vision/{cls,det,seg,pose,trk,dep}.webp |
| #2 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log02/IMG_7901.JPG |
| #3 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log03/IMG_7913.JPG・IMG_7914.JPG と measure_regions.json |
| #4 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log03/IMG_7911.JPG〜IMG_7914.JPG と /Volumes/KIOXIA/r2r2r-data/vision/measurements/Log04/thresholds_white.json |
| #6 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log06/shapes_red_rot30_20260731_190825_f0.png |
| #7 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log07/flat_0_20260802_072613/f0.png・mat_0_20260802_080524/f0.png |
| #8 | /Volumes/KIOXIA/r2r2r-data/vision/captured/Log08/tilt_30_20260807_082518/f0.png |
| #10 | /Volumes/KIOXIA/r2r2r-data/vision/generated/Log10/B_tray_similar.png |
| #11 | /Volumes/KIOXIA/r2r2r-data/runs/run_pick_place/run_0001_20261019_195120/recording/top.mp4(440・560コマ目) |
| LOG236 | R2 の media/s-1280x720_0e7779e9-0f3a-45fd-aa67-b76c819faabb.mp4(110コマ目) |