サムネ自動トリミング:顔検出・焦点領域・SmartCropで“映える”自動生成(Windows/Next.js)
OGP やカードのサムネは自動生成が肝です。手作業だと漏れ・バラつきが出やすく、公開速度も落ちます。 まずは SmartCrop + Sharp で“映える切り抜き”を自動化し、顔検出や手動の焦点(focal point)で上書きできる設計にすると安定します。
先に結論(運用ルール)
- テンプレは1.91:1(1200×630)とカード用の2系統を固定。
- 自動はSmartCrop、あれば顔検出>手動focal>SmartCropの優先順位。
- 表示は予約サイズ+AVIF/WebP(関連:CLSゼロ)。
要点(TL;DR)
- まずはSmartCrop + Sharpで自動化。顔検出/手動で上書きできる入口を残す。
- OGPは1200×630(安全域は別記事参照)。カードは 400×250 等で統一。
- Next.js表示はaspect-ratio予約 + pictureでコーデック切替。
1. なぜ“自動 + 上書き可”が最短か
サムネの印象は CTR と直結します。毎回手作業だと運用が重く、抜け漏れも発生。SmartCrop は顔・コントラスト・エッジなどをスコア化して ベストな領域を返してくれるため、まずは自動で8割を取り、残りを顔検出や手動focalで補正するのが現実解です。
2. 最短フロー(実装)
- Sharp + SmartCrop を導入(Windows対応)。
- OGP/カードの固定サイズを決め、バッチ生成スクリプトを用意。
- 必要に応じて顔検出の矩形や手動focalで上書き。
- AVIF/WebPで軽量化、予約サイズでCLSゼロ(関連:OGP安全域)。
3. 実装レシピ(コピペOK)
3.1 依存の導入
// Shell# 1) 依存の導入(Windows PowerShell)
npm i -D sharp smartcrop-sharp
# (任意)顔検出を入れる場合:軽量に試すなら @vladmandic/face-api + canvas
# npm i -D @vladmandic/face-api canvas
3.2 SmartCropで自動トリム(OGP)
// TypeScript// 2) SmartCrop + Sharp:1200x630(OGP用)の自動トリム例
// scripts/crop-ogp.ts
import fs from "node:fs/promises";
import sharp from "sharp";
import smartcrop from "smartcrop-sharp";
const INPUT = "assets/input.jpg";
const OUT = "public/ogp/auto-1200x630.jpg";
async function main() {
const image = sharp(INPUT);
const { width, height } = await image.metadata();
if (!width || !height) throw new Error("サイズ取得に失敗");
// SmartCropで最適領域を探索(1.91:1 = 1200x630)
const result = await smartcrop.crop(await fs.readFile(INPUT), { width: 1200, height: 630 });
const topCrop = result.topCrop; // { x, y, width, height }
await image
.extract({ left: topCrop.x, top: topCrop.y, width: topCrop.width, height: topCrop.height })
.resize(1200, 630) // 目的サイズ
.jpeg({ quality: 84, mozjpeg: true }) // まずはJPEGで確認(後述でWebP/AVIFも)
.toFile(OUT);
console.log("✔ OGP生成:", OUT);
}
main().catch((e) => { console.error(e); process.exit(1); });
3.3 顔検出のフォールバック(概念)
// TypeScript// 3) 顔検出がある場合はそれを優先(概念例:単一顔の境界箱をSmartCrop前に採用)
type Box = { x: number; y: number; width: number; height: number };
function preferFaceBox(face: Box | null, targetW: number, targetH: number, imgW: number, imgH: number): Box | null {
if (!face) return null;
const targetRatio = targetW / targetH;
// 顔の中心を維持しつつ比率1.91:1の矩形を作成(はみ出しは画面内にクランプ)
const cx = face.x + face.width / 2;
const cy = face.y + face.height / 2;
let w = Math.min(imgW, Math.max(face.width * 2.2, targetW));
let h = Math.round(w / targetRatio);
if (h > imgH) { h = imgH; w = Math.round(h * targetRatio); }
let x = Math.round(cx - w / 2);
let y = Math.round(cy - h / 2);
x = Math.max(0, Math.min(imgW - w, x));
y = Math.max(0, Math.min(imgH - h, y));
return { x, y, width: w, height: h };
}
// SmartCropの前に preferFaceBox() が返す矩形があればそれで extract() する
3.4 バッチ生成(2サイズ)
// Shell# 4) 一括書き出し(PowerShell例:OGPとカードの2サイズ)
# OGP 1200x630 / Card 400x250
node scripts/crop-ogp.ts
# ※ 同様に 400x250 版のスクリプトを作って node scripts/crop-card.ts
3.5 WebP/AVIFで軽量化
// TypeScript// 5) 仕上げの軽量化(SharpでWebP/AVIFにも出力)
await image
.extract({ left: topCrop.x, top: topCrop.y, width: topCrop.width, height: topCrop.height })
.resize(1200, 630)
.toFile("public/ogp/auto-1200x630.webp"); // 既定画質(q=80程度)
await image
.extract({ left: topCrop.x, top: topCrop.y, width: topCrop.width, height: topCrop.height })
.resize(1200, 630)
.avif({ quality: 45, effort: 4 })
.toFile("public/ogp/auto-1200x630.avif");
3.6 Next.jsでの表示(CLSゼロ)
// HTML + CSS<!-- 7) Next.jsでの表示(比率予約 + picture + 代替テキスト) -->
<figure class="thumb">
<picture>
<source srcset="/ogp/auto-1200x630.avif" type="image/avif" />
<source srcset="/ogp/auto-1200x630.webp" type="image/webp" />
<img
src="/ogp/auto-1200x630.jpg"
width="1200" height="630"
alt="記事の主題を示すサムネイル"
loading="eager" decoding="async"
style="object-fit: cover"
/>
</picture>
<figcaption class="sr-only">記事の主題を示すサムネイル</figcaption>
</figure>
<style>
.thumb { aspect-ratio: 1200 / 630; background:#f3f4f6; }
.thumb > picture, .thumb img { width: 100%; height: 100%; display:block; }
</style>
<!-- 予約サイズでCLSゼロ(関連:/articles/cls-zero-images-ads) -->
4. 応用と使いどころ
- 人物が複数なら顔矩形の結合(外接)」→ 中心に寄せた比率矩形を作る。
- UIスクショはテキスト/アイコン領域のコントラストを重視(関連:スクショ最適化)。
- <picture>のアートディレクションと組み合わせれば、モバイル/PCで別トリムを安全に配信できる。
5. 公開前チェック
// Checklist# 公開前チェック(サムネ自動生成)
- 目的サイズ:OGP 1200x630(1.91:1)、カード 400x250 等を決めてから実装
- SmartCropで自動化し、顔検出や手動focalを“上書き”できる設計に
- alt/キャプションは“画像の内容”を簡潔に。装飾は alt=""
- 予約サイズ(aspect-ratio or width/height)でCLSゼロ
- WebP/AVIFで軽量化。色はまず sRGB 正規化(/articles/srgb-color-management)
- /compressor と /compare で劣化と容量を確認、OGPは安全域もチェック(/articles/ogp-thumbnail-safe-area)
6. まとめ
サムネは自動で量産し、必要に応じて顔検出/手動focalで仕上げるのが最短です。 予約サイズと軽量コーデックで CWV を守りつつ、OGP安全域 と背景のレスポンシブ設計も合わせれば、 どの端末でも“映える”サムネを安定運用できます。
FAQ(よくある質問)
画像形式の基本方針は?(写真/スクショ/透過)
写真は AVIF / WebP(画質80–85%目安)、UIやスクショはPNG / WebP Lossless、単色ロゴはSVGが基本です。 実装の詳細は srcset/sizes設計ガイド と スクショ最適化 を参照してください。
圧縮しても画質を落とさないコツは?
実表示幅に合わせたリサイズ → 過大ダウンロードを防ぎ、
srcset/sizes
を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。