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

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
1280x72042ぴったり
1600x90027ぴったり
1200x67521ぴったり
1360x76515ぴったり
1376x76811横に10.7px長い
1536x8647ぴったり
1920x10802ぴったり
1365x76820.3px短い(通す)
960x5401ぴったり
912x5131ぴったり
2752x15361横に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 foundImageMagick 7が入っていないImageMagick 7を入れる
-resize 1280x720 だけでは1280x720にならない^ がないと、縦横比を保ったまま枠に収まる大きさになる-resize 1280x720^ と -extent 1280x720 を組み合わせる

出典

資料確認した内容
The Open Graph protocolog:image と、og:image:width、og:image:height の位置づけ
Facebook for Developers: Images in Link Shares1.91:1に近い画像と1200x630以上の推奨
ImageMagick: Command-line Options-resize の ^ と -extent、-gravity の指定
Pillow: Image moduleImage.size で幅と高さを読む方法
この記事をシェア