OG画像130枚を「横幅×9=高さ×16」で照合したら14枚落ちた。1365x768は直しようがなかった

このブログの記事157本のうち、image: を指定している記事は130本あります。その130枚のOG画像を、横幅×9と高さ×16が等しいかで照合しました。落ちたのは14枚です。
14枚のうち2枚は、1365x768でした。高さ768のまま、横幅を整数で16:9ぴったりにする方法は、そもそもありません。768×16÷9は1365.33…になるからです。画像のほうが悪いのではなく、私の検査の式が間違っていました。
前回からの差分
公開済み140ページの共有カードを巡回した回では、og:image のURLが開けるかまでを見ました。画像は全件開けて、問題は題名の末尾の改行でした。
今回は、その画像ファイルの中身です。URLが開けても、画像が16:9かどうかは分かりません。運用の決まりには「OGは16:9」と書いてあります。ところが、scripts、docs、.github を 16:9 で検索しても、ガイドライン以外に出てきません。検査するスクリプトは見当たりませんでした。決まりだけがあり、機械の目がない状態です。
なぜ「ちょうど」では足りないのか
先に理由を書きます。画像の寸法は整数です。16:9を厳密に満たせる寸法は、高さが9の倍数のときだけです。720なら1280、900なら1600、1080なら1920。768のような高さは、最初から満たせません。
AIの画像生成が返す寸法は、モデルごとの決まりがあって、こちらで選べないことが多いです。その上に「ちょうど16:9」の検査を置くと、直せないものまで落とします。かといって緩めすぎると、10ピクセル以上ずれたものを見逃します。そこで、横幅が理想値から1ピクセル未満しかずれていなければ通す、という基準にしました。
用語
| 用語 | この回での意味 |
|---|---|
| OG画像 | og:image で指定する、共有カードに出る画像。このサイトでは記事ごとに1枚 |
| 縦横比 | 横幅÷高さ。16:9なら約1.7778 |
| 許容幅 | 理想の横幅と実際の横幅の差として許す、ピクセル数。今回は1未満 |
| 横幅のずれ | 横幅 − 高さ×16÷9。プラスなら横に長い |
| 偽陽性 | 直しようがないのに、検査が不合格にすること |
検査の構成
public/images/posts/*.webp
│ (読むだけ。書き換えない)
▼
check_og_ratio.py ── Pillowで幅と高さを読む
│
├─ 横幅のずれが1px未満 → 通す
├─ 1px以上 → NG行を出す
└─ 画像が0枚 → NGで終了コード1
▼
終了コード 0 or 1
手順1: 寸法の違う見本を3枚作る
実際の画像を触らずに済むよう、まず合成の見本で試します。すでに手元にOG画像のディレクトリがある人は、この手順を飛ばして手順2へ進んでください。
必要なものは、ImageMagickとPythonのPillowです。私の環境はImageMagick 7.1.1-43、Python 3.13.5、Pillow 11.1.0でした。
mkdir -p og-ratio-check/images
cd og-ratio-check
magick -size 1280x720 gradient:navy-teal images/a-1280x720.webp
magick -size 1365x768 gradient:navy-teal images/b-1365x768.webp
magick -size 1376x768 gradient:navy-teal images/c-1376x768.webp
1280x720は16:9ぴったりです。1365x768は、高さ768で出せる最も近い整数の寸法です。1376x768は、このサイトで実際に見つかった寸法を真似ました。
手順2: 検査スクリプトを置く
check_og_ratio.py という名前で、次を保存します。
import sys
from pathlib import Path
from PIL import Image
target = Path(sys.argv[1])
files = sorted(target.glob("*.webp"))
if not files:
print(f"NG: {target} に .webp がありません")
sys.exit(1)
bad = 0
for f in files:
with Image.open(f) as im:
w, h = im.size
# 16:9 に必要な横幅と実際の横幅の差(ピクセル)
gap = w - h * 16 / 9
ok = abs(gap) < 1
if not ok:
bad += 1
print(f"NG {f.name} {w}x{h} 横幅が{gap:+.1f}px ずれています")
print(f"{len(files)}枚中 {bad}枚が16:9から1px以上ずれています")
sys.exit(1 if bad else 0)
見本3枚に実行します。
python3 check_og_ratio.py images; echo "exit=$?"
NG c-1376x768.webp 1376x768 横幅が+10.7px ずれています
3枚中 1枚が16:9から1px以上ずれています
exit=1
3枚中、落ちたのは1376x768の1枚です。1365x768は、横幅が0.3pxしかずれないので通りました。
最初に私が書いた式は、これではありませんでした。同じ3枚を w*9 == h*16 で照合すると、次のようになります。
python3 - <<'PY'
from pathlib import Path
from PIL import Image
for f in sorted(Path("images").glob("*.webp")):
w, h = Image.open(f).size
print(f.name, w * 9 == h * 16)
PY
a-1280x720.webp True
b-1365x768.webp False
c-1376x768.webp False
1365x768がFalseになっています。これが、冒頭の14枚のうち2枚が偽陽性だった理由です。
手順3: 悪い入力でも落ちるか確かめる
画像が0枚でも「0枚中0枚がずれ」で終了コード0になる検査は、使えません。次の3つを試しました。
mkdir empty pngonly four3
magick -size 1280x720 xc:red pngonly/x.png
magick -size 1200x900 xc:red four3/y.webp
for d in empty pngonly four3; do python3 check_og_ratio.py $d; echo "exit=$?"; done
NG: empty に .webp がありません
exit=1
NG: pngonly に .webp がありません
exit=1
NG y.webp 1200x900 横幅が-400.0px ずれています
1枚中 1枚が16:9から1px以上ずれています
exit=1
空のディレクトリも、拡張子違いのPNGだけのディレクトリも、4:3の画像も、すべて終了コード1で止まりました。
手順4: 公開中の130枚に実行する
サイトのリポジトリのルートへ戻り、本物のOG画像のディレクトリに向けます。画像は読むだけで、1枚も書き換えていません。
cd ..
python3 og-ratio-check/check_og_ratio.py public/images/posts
出力は、12行のNGと、末尾の集計1行でした。NGのうち9行は同じ形なので省いて、3行だけ載せます。
NG ai-agent-product-reply-context-boundary.webp 1376x768 横幅が+10.7px ずれています
NG hermes-agent-prompt-size-budget.webp 1376x768 横幅が+10.7px ずれています
NG opencode-github-actions-pr-review.webp 2752x1536 横幅が+21.3px ずれています
130枚中 12枚が16:9から1px以上ずれています
終了コードは1でした。内訳は、1376x768が11枚と2752x1536が1枚です。2752x1536は1376x768のちょうど2倍で、同じ比率です。
寸法の全体は次のとおりでした。
| 寸法 | 枚数 | 16:9 |
|---|---|---|
1280x720 | 42 | ぴったり |
1600x900 | 27 | ぴったり |
1200x675 | 21 | ぴったり |
1360x765 | 15 | ぴったり |
1376x768 | 11 | 横に10.7px長い |
1536x864 | 7 | ぴったり |
1920x1080 | 2 | ぴったり |
1365x768 | 2 | 0.3px短い(通す) |
960x540 | 1 | ぴったり |
912x513 | 1 | ぴったり |
2752x1536 | 1 | 横に21.3px長い |
記事の日付で見ると、2026-10-19から11-03までの30枚のうち7枚がずれていました。11-04以降の10枚は、すべて通っています。なぜ止まったかは、このデータからは分かりません。
1376x768を直すとどうなるか
公開済みの画像は書き換えません。コピーを作って、手順だけ確かめました。ここで使ったのは、このサイトの生成手順に書いてあるImageMagickのコマンドです。
cp ../public/images/posts/ai-sales-csv-column-contract-sample.webp images/real-1376.webp
magick identify -format "%f %wx%h %b\n" images/real-1376.webp
magick images/real-1376.webp -resize 1280x720^ -gravity center -extent 1280x720 fixed.webp
magick identify -format "%f %wx%h %b\n" fixed.webp
real-1376.webp 1376x768 93298B
fixed.webp 1280x720 45888B
1376x768を1280x720の枠に合わせると、まず1290x720まで縮みます。左右から合計10px、全体の0.78%が切れます。ファイルサイズは93,298バイトから45,888バイトに下がりましたが、ただし、書き出し時の画質設定が違う可能性もあり、縮小だけの効果とは言えません。
直した画像を検査に通すと、次のようになりました。
mkdir fixed-dir && cp fixed.webp fixed-dir/
python3 check_og_ratio.py fixed-dir; echo "exit=$?"
1枚中 0枚が16:9から1px以上ずれています
exit=0
実害は小さかった
正直に言うと、12枚のズレは見た目にほとんど効きません。0.78%、約10pxです。
Facebookの資料によると、リンク共有の画像は1.91:1に近いほど、切り取りなしで出ます。1280x720は高さの約6.9%、1376x768は約6.2%が、1.91:1では切れます。差は0.7ポイントです。つまり、16:9で作った画像は、Facebookでは切り取りが入る前提です。
だから私は、12枚の公開済み画像を直すつもりはありません。やることは2つです。今後の朝稿で生成直後に check_og_ratio.py を通すこと。そして、検査の式を「ちょうど」にしないことです。11枚が1376x768に集まっていることと、2752x1536がその2倍であることから、生成直後のサイズがそのまま残ったと私は見ています。過去の生成ログは今回見ていないので、過去分については推測です。ただ、この記事のOG画像を同じモデルで作ったときも、返ってきたのは1376x768でした。1280x720へ収めてから、このスクリプトに通しています。
よくあるエラー
| 症状 | 原因 | 対処 |
|---|---|---|
ModuleNotFoundError: No module named 'PIL' | Pillowが入っていない | python3 -m pip install pillow |
1365x768や1366x768が落ちる | 完全一致の式を使っている | 横幅の差が1px未満なら通す式にする |
.webp がありません で止まる | ディレクトリの指定違い、または拡張子が .png | パスと拡張子を確かめる |
magick: command not found | ImageMagick 7が入っていない | ImageMagick 7を入れる |
-resize 1280x720 だけでは1280x720にならない | ^ がないと、縦横比を保ったまま枠に収まる大きさになる | -resize 1280x720^ と -extent 1280x720 を組み合わせる |
出典
| 資料 | 確認した内容 |
|---|---|
| The Open Graph protocol | og:image と、og:image:width、og:image:height の位置づけ |
| Facebook for Developers: Images in Link Shares | 1.91:1に近い画像と1200x630以上の推奨 |
| ImageMagick: Command-line Options | -resize の ^ と -extent、-gravity の指定 |
| Pillow: Image module | Image.size で幅と高さを読む方法 |


