画像生成ツールは「16:9」と返したのに1376×768だった。アイキャッチを測って切った

画像生成ツールは「16:9」と返したのに1376×768だった。アイキャッチを測って切った

今朝、画像生成ツールが返した結果には "exact_aspect_ratio": "16:9" と書いてありました。ところが、できあがったPNGを測ると1376×768です。割ると1.7917。16:9の1.7778とは、ぴったり一致しません。

アイキャッチを毎日1枚作っていると、この程度のズレが何枚も積もります。直さないと困るズレなのか、読者には見えないズレなのか。測ってから決めることにしました。

戻り値と画素が食い違った

使っているのは、OpenRouter経由のNano Banana 2(google/gemini-3.1-flash-image)です。横長を指定して1回だけ生成しました。プロンプトには文字を入れていません。戻り値のうち、今回の話に関わる項目は次の3つです。

"model": "google/gemini-3.1-flash-image"
"exact_aspect_ratio": "16:9"
"cost_usd": 0.0672345

1枚0.0672345ドル。毎日1枚作ると、30日で約2.02ドルです。アイキャッチ代としては、思っていたより軽い数字でした。

次に、画素を identify で確かめます。作業用に eyecatch-probe というディレクトリを作り、受け取ったPNGを eyecatch.png の名前で置いてあります。

identify -format "%wx%h %b\n" eyecatch-probe/eyecatch.png
1376x768 1.53046MB

OpenRouterのモデルページに載っている2K・16:9の作例も、2752×1536でした。1376×768のちょうど2倍で、縦横比は同じ1.7917です。今回だけの揺れではなさそうです。理由までは分かりません。32の倍数に丸めているのではと考えていますが、確認はしていません。

公開中の75枚はどうなっていたか

ここで気になったのが、すでにサイトに置いてある75枚です。リポジトリで追跡している public/images/posts/*.webp を全部開いて、寸法を数えました。

数えたスクリプト(audit.mjs)
import { execSync } from 'node:child_process';
import sharp from 'sharp';

const files = execSync("git ls-files 'public/images/posts/*.webp'").toString().trim().split('\n');
const sizes = new Map();
let near169 = 0;
for (const f of files) {
  const { width, height } = await sharp(f).metadata();
  const key = `${width}x${height}`;
  sizes.set(key, (sizes.get(key) ?? 0) + 1);
  if (Math.abs(width / height - 16 / 9) < 0.001) near169++;
}
console.log(`files=${files.length} within0.1%=${near169} other=${files.length - near169}`);
for (const [k, n] of [...sizes].sort((a, b) => b[1] - a[1])) console.log(k, n);
node eyecatch-probe/audit.mjs
files=75 within0.1%=70 other=5
1600x900 26
1200x675 20
1360x765 8
1536x864 7
1280x720 4
1376x768 4
1920x1080 2
1365x768 2
912x513 1
2752x1536 1

16:9との差が0.1%以内の画像は70枚でした。残り5枚の中身は、1376×768が4枚、2752×1536が1枚。どれも縦横比は今回受け取った画像と同じ1.7917です。残る70枚が、どのような経路で整ったのかは、今回は追っていません。公開済みの画像を直す作業もしていません。

サイトの表示では、ほとんど差が出なかった

正直に書くと、私は最初「ズレたまま置くと表示が崩れる」と思っていました。外れました。

記事ページの先頭画像は、CSSで aspect-ratio: 16 / 9 の枠に入り、画像は object-fit: cover で収まります。一覧のカードも同じ16:9の枠です。高さ768に合わせた枠の幅は1365.33。1376との差は10.67で、画像全体の0.78%にあたります。左右から5画素ほどが枠の外へ出るだけです。

つまり、読者の画面だけを見るなら、1376×768のまま置いても困りません。それでも切ることにしたのは、毎朝の手順に「16:9であること」を確かめる検査があるからです。検査を通すために、画像側を合わせます。

切るか、伸ばすか

整え方は2通り考えました。

  • 切る。左から8画素、上から1画素の位置を起点に、1360×765を取り出します。これは16:9ちょうどです
  • 伸ばす。1280×720へ fit: 'fill' で変形します。横の縮尺が0.9302、縦が0.9375になり、0.78%だけ縦横比がゆがみます

伸ばした版は、見比べていません。毎日の自動手順に、縦横比をわざと変える処理を入れたくなかったからです。切ったほうは、元の絵を保ったまま、端が数画素なくなるだけで済みます。

容量は、品質値でかなり変わりました。次のスクリプトで並べます。

容量を測ったスクリプト(probe.mjs)
import { statSync } from 'node:fs';
import sharp from 'sharp';

const src = 'eyecatch-probe/eyecatch.png';
const kb = (f) => (statSync(f).size / 1024).toFixed(1);
console.log(`png ${kb(src)} KB`);

for (const q of [85, 75]) {
  const out = `eyecatch-probe/full-q${q}.webp`;
  await sharp(src).webp({ quality: q }).toFile(out);
  console.log(`1376x768 q${q} ${kb(out)} KB`);
}

const cropped = 'eyecatch-probe/crop-1360x765-q75.webp';
await sharp(src).extract({ left: 8, top: 1, width: 1360, height: 765 }).webp({ quality: 75 }).toFile(cropped);
console.log(`crop 1360x765 q75 ${kb(cropped)} KB`);

const stretched = 'eyecatch-probe/fill-1280x720-q75.webp';
await sharp(src).resize(1280, 720, { fit: 'fill' }).webp({ quality: 75 }).toFile(stretched);
console.log(`fill 1280x720 q75 ${kb(stretched)} KB`);
node eyecatch-probe/probe.mjs
png 1494.6 KB
1376x768 q85 109.7 KB
1376x768 q75 69.6 KB
crop 1360x765 q75 68.7 KB
fill 1280x720 q75 62.6 KB

PNGのままなら1494.6KBです。品質85のWebPにしても109.7KB。公開中の75枚をファイルサイズで並べた中央値は62.1KBなので、品質85だとその1.8倍近くあります。手元の変換スクリプトは品質85がデフォルトでしたが、アイキャッチには重いと感じて、品質75に下げました。

切った1360×765の品質75は68.7KBです。生成した画像を開いて見たところ、文字や数字は混ざっておらず、面の色むらや縁のにじみも目立ちませんでした。この画像をそのままアイキャッチにしています。

まだ分からないこと

測ったのは1枚です。ほかの絵柄や、より細かい描き込みの画像で、品質75が同じように持つかは確かめていません。1枚の結果から言えるのは、ここまでです。

SNSのカード表示も見ていません。記事URLを貼ったとき、各サービスが画像のどこを切るのか。サイト内の枠は object-fit: cover で説明がつきますが、外部サービスの切り方は別の話です。上の質問に答えが届いたら、その回で扱います。

もう1つ、ツールが「16:9」と返す理由は分かっていません。戻り値の文字列を信じて検査を省くと、ズレた画像がそのまま公開に届きます。当面は、戻り値ではなく、できた画像の画素を測る手順を残します。

出典

この記事をシェア