画像フォーマット別のベンチマークをしてみた

PageSpeed Insightsがwebpかavifにして画像を軽くしろと言われるので、渋々ベンチをとってみた結果。JPEG, WebP, JPEG XL, AVIFで比較している。

比較では、画質を保ったままファイルサイズをどれだけ減らせるかに加えて、サーバーサイドでの変換にかかるCPU時間を重視した。本システムはリソースの限られた環境での運用を前提としており、変換が重いと画像のアップロードが終わるまでに時間がかかり、ストレスになるからだ。

以下に計測内容を残す。計測は3回行い、その中央値を出している。

変換に使った画像

形式 フォーマット 色数 画像サイズ ファイルサイズ(byte)
JPEG元画像 画質93・サブサンプリング4:2:0・ベースライン・EXIF・XMP有 24bit 4,000 x 3,000px 11,175,399
PNG元画像 埋め込みコメントあり 32bit 2,048 x 3,072px 7,636,570

計測に使った環境

Env Ver
CPU AMD Ryzen 5 8500G
メモリ DDR5-6000 16GB * 2
ストレージ Crucial P1 CT500P1SSD8JP
OS Ubuntu 26.04.1 LTS
ImageMagick 7.1.2-18 Q16 x86_64 23822
cjxl v0.11.1 [AVX3_DL,AVX2,SSE4,SSE2]
avifenc 1.3.0 (dav1d [dec]:1.5.3, libgav1 [dec]:0.20.0, aom [enc/dec]:v3.13.1)

サイズそのままで形式のみ変更

JPEGがサイズ削減率とCPU使用時間、画質の比率で最も優れており、他へ移る意義を見出すことができなかった。

JPEGの軽量化

スマホのカメラで撮影して作ったJPEGがどれだけ縮むか見たもの。JPEGからJPEGに再圧縮するだけでも劇的に減っている。EXIFは残してある。

画像全体を比較してもわかりづらいため、元画像の一部をクロップした比較用に切り抜いたJPEG元画像と、それに対応する変換結果の画像をすべてPNGで置いている。開いてスライドショーをめくっていくと差分が分かりやすいようにしている。

変換 設定 サイズ削減率 サイズ比率(JPEG基準) CPU時間(ms) CPU時間比率(JPEG基準) 画質
JPEG->JPEG 画質80・サブサンプリング4:2:0・プログレッシブ 88.8% 100% 217 100% ほぼ同一(AVIFに劣る)
JPEG->WebP 画質80・サブサンプリング4:2:0 92.4% 68.0% 707 325.8% のっぺりしている
JPEG->JPEG XL 画質80・サブサンプリングなし 92.5% 66.5% 6,041 2783.9% 若干色が薄い
JPEG->AVIF 画質80・サブサンプリング4:2:0・速度6/10 90.9% 81.4% 2,939 1354.4% 限りなく同一(JPEGに勝る)
JPEG→JPEGが劇的に減っている理由

JPEGからJPEGにしても普通に考えれば容量はさほど変わらないはずだが、これには理由がある。これはimagemagickの出力時にXMP-GDepthとXMP-GImageが欠落している関係だ。

試しに元のJPEGから純粋に画像を引き抜いたところ2,516,842バイトしかなかった。元が11,175,399バイトなのを差し引くと8,658,557バイトがXMP-GDepthとXMP-GImageに使われていることが分かる。

この二つのフィールドはAndroidのカメラによって作成されるもので、JPEGで保存した写真を劣化させずに再編集するためにボケ情報が保存されているらしく、それらのデータが容量を食っているらしい。これは後からぼかしの強さやピント位置を調整し直すためのもので、要は写真の中にもう1枚写真が丸ごと入っているから大きいわけだ。

つまりこのデータは単純に画像を表示する分には不要で、マスタデータとして保持するために必要な情報であるから、ブログなどの表示用途では外してしまって構わない。

ちなみにこのデータがある状態では、ぼかしがない状態の素の画像と、ぼかしをかけるためのマスクデータ、ぼかし加工のされた画像が内部にあり、以下のコマンドで切り出すことができる。

# XMP-GDepth(ぼかしデータ
exiftool -b -DepthImage src.jpeg > depth.jpeg
# XMP-GImage(真の写真
exiftool -b -ImageData src.jpeg > gimage.jpg
# ぼかしデータと真の写真を掛け合わせた写真データ(編集済の写真)
exiftool -xmp:all= -o no-xmp.jpg src.jpeg

しかし切り出した画像を見てもそこまで大きくないので不思議だなと思ったら、XMPフィールドには文字列しか入らないので、真の写真はBase64エンコードされた文字列として格納されているらしい。そらでかくなるわ!

恐らく元々JPEGの仕様として存在したものではなく、拡張フィールドにバイナリを無理やり押し込んだ結果こうなっているのだろう。

PNGの軽量化

メタデータは落ちている。

画像全体を比較してもわかりづらいため、元画像の一部をクロップした比較用に切り抜いたPNG元画像と、それに対応する変換結果の画像をすべてPNGで置いている。開いてスライドショーをめくっていくと差分が分かりやすいようにしている。

変換先 設定 サイズ削減率 サイズ比率(JPEG基準) CPU時間(ms) CPU時間比率(JPEG基準) 画質
PNG->JPEG 画質80・サブサンプリング4:2:0・プログレッシブ 91.4% 100% 207 100% ほぼ同一(AVIFに劣る)
PNG->WebP 画質80・サブサンプリング4:2:0 95.6% 51.7% 569 274.9% のっぺりしている
PNG->JPEG XL 画質80・サブサンプリングなし 93.8% 71.6% 3,897 1882.6% 若干色が暗い
PNG->AVIF 画質80・サブサンプリング4:2:0・速度6/10 95.8% 49.4% 4,317 2085.5% 限りなく同一(JPEGに勝る)

サムネイル生成

全て幅を320pxに落としている。

サムネイル生成ではCPU使用時間がすべて誤差であるため、処理の負荷の割に、JPEGと同じファイルサイズで、JPEGより高い画質の得られるAVIFを採用する意義があることが分かった。

JPEG

変換先のリンクを開いてスライドショーをめくっていくと差分が分かりやすいようにしている。EXIFは落としてある。

変換先 設定 サイズ削減率 サイズ比率(JPEG基準) CPU時間(ms) CPU時間比率(JPEG基準) 画質
JPEG->JPEG 画質80・サブサンプリング4:2:0・プログレッシブ 99.9% 100% 281 100% 悪い
JPEG->WEBP 画質80・サブサンプリング4:2:0 99.9% 72.4% 286 101.8% のっぺりしている
JPEG->JPEG XL 画質80・サブサンプリングなし 99.9% 78.0% 327 116.4% 若干悪い
JPEG->AVIF 画質80・サブサンプリング4:2:0・速度6/10 99.9% 100.0% 311 110.7% 非常に良い

PNG

変換先のリンクを開いてスライドショーをめくっていくと差分が分かりやすいようにしている。

変換先 設定 サイズ削減率 サイズ比率(JPEG基準) CPU時間(ms) CPU時間比率(JPEG基準) 画質
PNG->JPEG 画質80・サブサンプリング4:2:0・プログレッシブ 99.6% 100% 493 100% ノイズが目立つ
PNG->WEBP 画質80・サブサンプリング4:2:0 99.7% 78.2% 503 102.0% JPEGよりノイズが少ない
PNG->JXL 画質80・サブサンプリングなし 99.7% 81.4% 600 121.7% ややノイズがある
PNG->AVIF 画質80・サブサンプリング4:2:0・速度6/10 99.6% 92.0% 569 115.4% 非常に鮮明

実行時のコマンド

原寸用

変換方法 出力サイズ(KB) 合計CPU時間(ms)
magick src.jpeg -quality 80 -sampling-factor 2x2,1x1,1x1 -interlace Plane out_jpg_jpg_A.jpg 1,223.4 217
magick src.jpeg -quality 80 out_jpg_webp_A.webp 832.2 707
cjxl src.jpeg out_jpg_jxl_A.jxl -q 80 --lossless_jpeg=0 813.9 6,041
avifenc -q 80 -y 420 -s 6 --ignore-xmp src.jpeg out_jpg_avif_A.avif 995.4 2,939
magick src.png -quality 80 -sampling-factor 2x2,1x1,1x1 -interlace Plane out_png_jpg_A.jpg 640.7 207
magick src.png -quality 80 out_png_webp_A.webp 331.3 569
cjxl src.png out_png_jxl_A.jxl -q 80 458.9 3,897
avifenc -q 80 -y 420 -s 6 src.png out_png_avif_A.avif 316.6 4,317

サムネ用

サムネ用の画像は、事前に元画像を幅320pxへリサイズしてPNGに保存しているため、リサイズの処理時間は含んでいない。

変換方法 出力サイズ(KB) 合計CPU時間(ms)
magick mid_jpg_C.png -quality 80 -sampling-factor 2x2,1x1,1x1 -interlace Plane out_jpg_jpg_C.jpg 12.3 281
magick mid_jpg_C.png -quality 80 out_jpg_webp_C.webp 8.9 286
cjxl mid_jpg_C.png out_jpg_jxl_C.jxl -q 80 9.6 327
avifenc -q 80 -y 420 -s 6 mid_jpg_C.png out_jpg_avif_C.avif 12.3 311
magick mid_png_C.png -quality 80 -sampling-factor 2x2,1x1,1x1 -interlace Plane out_png_jpg_C.jpg 31.2 493
magick mid_png_C.png -quality 80 out_png_webp_C.webp 24.4 503
cjxl mid_png_C.png out_png_jxl_C.jxl -q 80 25.4 600
avifenc -q 80 -y 420 -s 6 mid_png_C.png out_png_avif_C.avif 28.7 569

確認用スクリプトと集計結果の全データ

詳細な全集計データには、本記事書いたデータの元ネタを格納している。

また以下に集計で使ったスクリプトも置いている。

確認用スクリプトのソースコード
#!/usr/bin/env bash
# 画像ダイエット ベンチマーク
# 使い方: src.jpeg / src.png と同じフォルダで  bash bench.sh
# 結果:   result.tsv(ファイル名・サイズKB・実時間ms・CPU時間ms・CPU使用率%)→ Excel の「測定結果」シート A1 に貼り付け
#         各コマンドのエラー出力は bench.log に残る
# Copyright Lycolia Rizzim All rights reserved.
# https://lycolia.info/

set -u

SRC_JPG=src.jpeg
SRC_PNG=src.png
RUNS=${RUNS:-3}        # 各コマンドを何回測るか(中央値を記録)。例: RUNS=5 bash bench.sh
OUT=result.tsv
LOG=bench.log

printf 'ファイル\tサイズ(KB)\t実時間(ms)\tCPU時間(ms)\tCPU使用率(%%)\n' > "$OUT"
: > "$LOG"

kb() { awk -v s="$(stat -c %s "$1")" 'BEGIN{printf "%.1f", s/1024}'; }

# measure 出力ファイル コマンド...
# bash の time で 実時間・user・sys を取り、CPU時間 = user + sys、CPU使用率 = CPU時間 / 実時間
measure() {
  local out=$1; shift
  local tf rows=() i line real user sys rc
  tf=$(mktemp)
  for ((i = 0; i < RUNS; i++)); do
    rm -f "$out"
    local TIMEFORMAT=$'%3R %3U %3S'
    { time "$@" >>"$LOG" 2>&1; rc=$?; } 2>"$tf"
    if (( rc != 0 )); then
      printf '%s\tERROR\t\t\t\n' "$out" >> "$OUT"
      echo "失敗: $out(bench.log を確認)" >&2
      rm -f "$tf"; return
    fi
    read -r real user sys < "$tf"
    rows+=( "$(awk -v r="$real" -v u="$user" -v s="$sys" 'BEGIN{printf "%d %d", r*1000, (u+s)*1000}')" )
  done
  rm -f "$tf"
  # 実時間の中央値の回を採用(CPU時間も同じ回の値)
  line=$(printf '%s\n' "${rows[@]}" | sort -n -k1,1 | sed -n "$(( (RUNS + 1) / 2 ))p")
  read -r real cpu <<< "$line"
  local pct
  pct=$(awk -v r="$real" -v c="$cpu" 'BEGIN{ if (r > 0) printf "%.0f", c / r * 100; else print "" }')
  printf '%s\t%s\t%s\t%s\t%s\n' "$out" "$(kb "$out")" "$real" "$cpu" "$pct" >> "$OUT"
  echo "完了: $out  実${real}ms  CPU${cpu}ms (${pct}%)" >&2
}

# 入力ファイルのサイズ(時間は空欄)
printf '%s\t%s\t\t\t\n' "$SRC_JPG" "$(kb "$SRC_JPG")" >> "$OUT"
printf '%s\t%s\t\t\t\n' "$SRC_PNG" "$(kb "$SRC_PNG")" >> "$OUT"

JPG_OPT=(-quality 80 -sampling-factor 2x2,1x1,1x1 -interlace Plane)
AVIF_OPT=(-q 80 -y 420 -s 6)

for tag in jpg png; do
  if [[ $tag == jpg ]]; then src=$SRC_JPG; else src=$SRC_PNG; fi

  # 前処理(B: メタデータ除去 / C: さらに幅320px)
  measure "mid_${tag}_B.png" magick "$src" -strip "mid_${tag}_B.png"
  measure "mid_${tag}_C.png" magick "$src" -strip -resize '320x>' "mid_${tag}_C.png"

  # JPEG
  measure "out_${tag}_jpg_A.jpg" magick "$src" "${JPG_OPT[@]}" "out_${tag}_jpg_A.jpg"
  measure "out_${tag}_jpg_B.jpg" magick "mid_${tag}_B.png" "${JPG_OPT[@]}" "out_${tag}_jpg_B.jpg"
  measure "out_${tag}_jpg_C.jpg" magick "mid_${tag}_C.png" "${JPG_OPT[@]}" "out_${tag}_jpg_C.jpg"

  # WebP
  measure "out_${tag}_webp_A.webp" magick "$src" -quality 80 "out_${tag}_webp_A.webp"
  measure "out_${tag}_webp_B.webp" magick "mid_${tag}_B.png" -quality 80 "out_${tag}_webp_B.webp"
  measure "out_${tag}_webp_C.webp" magick "mid_${tag}_C.png" -quality 80 "out_${tag}_webp_C.webp"

  # JXL
  if [[ $tag == jpg ]]; then
    measure "out_jpg_jxl_A.jxl" cjxl "$src" out_jpg_jxl_A.jxl -q 80 --lossless_jpeg=0
    measure "out_jpg_jxl_L.jxl" cjxl "$src" out_jpg_jxl_L.jxl          # ロスレス再圧縮
  else
    measure "out_png_jxl_A.jxl" cjxl "$src" out_png_jxl_A.jxl -q 80
  fi
  measure "out_${tag}_jxl_B.jxl" cjxl "mid_${tag}_B.png" "out_${tag}_jxl_B.jxl" -q 80
  measure "out_${tag}_jxl_C.jxl" cjxl "mid_${tag}_C.png" "out_${tag}_jxl_C.jxl" -q 80

  # AVIF
  measure "out_${tag}_avif_A.avif" avifenc "${AVIF_OPT[@]}" --ignore-xmp "$src" "out_${tag}_avif_A.avif"
  measure "out_${tag}_avif_B.avif" avifenc "${AVIF_OPT[@]}" "mid_${tag}_B.png" "out_${tag}_avif_B.avif"
  measure "out_${tag}_avif_C.avif" avifenc "${AVIF_OPT[@]}" "mid_${tag}_C.png" "out_${tag}_avif_C.avif"
done

echo "結果を $OUT に書き出しました(Excel の「測定結果」シート A1 に貼り付け)" >&2

あとがき

JPEG XLはEdgeが対応していないので、どのみち対応しないのだが、これまでの画像フォーマットのすべてを過去にするみたいな大層な謳い文句を見かけたので計測することにした。

WebPは昔計測したことがあり、画質のわりに容量が小さくならないことは知っていたが、計測したデータを残していなかったので、今回データを残す意味合いで測ることにした。

本命はJPEGのメタデータを削ってどこまで減らせるかと、AVIFを活用できるかが焦点だった。結果としてよいデータが取れたので良かったと思う。

またJPEGのファイル構造が知れたことはいい勉強になったし、今回の記事でテキストリンクに紐づいた画像をポップアップ表示させる必要が出たので、その改修ができたのも収穫だった。詳しくはまたリリースノートの記事を作って書くが、実はテキストリンクをポップアップさせるのは前からやりたかったのだが、脳裏から消えていた。

あとどうでもいい話として、過去にStableDiffusionのベンチマークで作ってたうちの子が劇的に麗しくなったのが見れたのもいい収穫だった。何せベンチマークする時くらいしか見てなかったので…。

ひとまずは次期バージョンでJPEGの不要なXMP落としとavifサムネイルに対応していきたい。レンタルサーバー向け対応はどうするかなぁ…。恐らくさくらのレンサバでもやろうと思えば対応自体はできそうな気はするが…。例えば利用者がavifencを自前ビルドするなど…w(そもそも今このLyndbaumのユーザーは私しかいないはずなのでどうでもいい話ではあるが…w)

映画館での映画鑑賞録にミスがあったので直した

Claude Opus 5.5を使って鑑賞作品の整理をしていたところ、鑑賞履歴に漏れがある作品があることが分かった。エクセルのVLOOKUPで機械的に確認したところ『北極百貨店のコンシェルジュさん』と『大雪海のカイナ ほしのけんじゃ』が鑑賞履歴から欠落していることが判明した。

過去の予約メールと、保存している紙チケットから突き合せた結果、この画像のような差分があることが判明した。

恐らくどこかで操作を誤ったか何かで『アリスとテレスのまぼろし工場』がフィルされた結果、『北極百貨店のコンシェルジュさん』と『大雪海のカイナ ほしのけんじゃ』が消えてしまったのだと思う。

またそれとは別に『アリスとテレスのまぼろし工場』の鑑賞が一回分欠落していることが分かったので、そこも埋めた。

これによって『アリスとテレスのまぼろし工場』の鑑賞回数が前回集計時の8回から6回に減少した。

聲の形を観てきた

先日、『聲の形』の10周年記念再上映が始まった。私は2019年に映画を観始めた関係で、それ以前の作品については触れる機会がなく、聲の形は興味のある作品の一つだったので観に行くことにした。これはそのログである。他にも『心が叫びたがってるんだ。』『サカサマのパテマ』にも興味があるので再上映が叶った暁には観てみたいと考えている。

鑑賞は兵庫県下で唯一上映を担当したアースシネマズ姫路で行うことにした。こちらではDolby Atmos上映となっており、ただでさえ音がいい館なのに、更によく、大変素晴らしい没入感を味わうことができた。

さて、前置きはほどほどにして感想に入っていきたい。

今観るべき作品だった

正直、本作を観たのが今でよかったと思う。今だからこそ、カタルシスという言葉の意味が解る。

『トラペジウム』で出会った脳を灼かれるという感情は、すなわちカタルシスなのである。

私は『トラペジウム』や『メイクアガール』といった「脳を灼かれる」作品を好んでおり、そしてこれらを観てきたからこそ、本作をより灼かれる作品として鑑賞することができたと思う。

思えば他にも過去に好んできた作品にこのような要素は多かったように思う。例えばSAOはアスナが親による圧力を受けていたり、シノンがリアルで襲われたりとか、そういった暴力的なシーンはあったし、それを観ていたからこそ映画のSAOPも、そういう背景込みで楽しめていた部分はあったと思う。

『アリスとテレスのまぼろし工場』もヒロインが捨てられるような描写があったり、偉そうな人が凋落してリンチにあったり、主人公が親から圧力を受けたりするなど、そういう描写があったように思う。そして決まって切り返しの場面がある。

こういった作品を多く観てきたからこそ、私は本作を今観て、その意味を嚙み締め、感情に刻むことができたように思う。もうこれは「脳を灼かれる」というより「カタルシス」である。カタルシスは日本語的には「精神の浄化」や「悲劇が観客の心に怖れと憐れみの感情を呼び起こすことで精神を浄化する効果」らしいが、そういった意味ではまさに精神が浄化されるような気持ちなのかもしれない。

ボーイミーツガール的作品

私はボーイミーツガールが好きだ。例えば『あした世界が終わるとしても』『ジョゼと虎と魚たち』『夏へのトンネル、さよならの出口』、そして何よりも『ソードアート・オンライン』といったボーイミーツガールはそれに該当する。

そして本作はボーイミーツガールでもあった。つまりカタルシス x ボーイミーツガールという組み合わせだ。

余談だが私のボーイミーツガール好きは『交響詩篇エウレカセブン』から来ている。

物語について

全てを壊しその負い目から自殺を試みた少年と、逆に全てを壊したと思い込み自らを疫病神のように思い自殺を試みた少女、そしてすべてを壊されたと思ったクラスメイト達の物語だと思った。

少女をいじめ、その結果自分がいじめられる側に転落したガキ大将だった主人公、ヒロインをいじめる側から主人公をいじめる側に転換したクラスメイト、そしてどれだけボロボロにされようと全員と友達になりたかったヒロイン。ああ、こんなひどい物語があってたまるものか、ヒロイン以外は全員が全員を下に見ていて、全ては快楽の対象なのだ。

そしてこのことは子供であるこれらの人物以外にも主人公とヒロインの親にまで波及して、血を流す暴力沙汰にさえ発展する。主人公の親はヒロインに対する賠償金を負担することになる。このことは主人公に高校生になるまでの間、長らく負い目を持たせ、主人公は希死念慮を抱くようになる。しかし主人公は親の必死の説得によって自死をやめる。

反対にヒロインにもクラスを無茶苦茶にしてしまった、自分が悪いという希死念慮が芽生える。これはクラスメイトの一人がそう発言していたことの影響も大きい。つまり部分的に事実ではあったのだ。そしてヒロインは主人公とは異なり、本当に飛び降りてしまう。しかし、その死は主人公の犠牲によって食い止められる。

主人公をいじめていたクラスメイトの一人は、主人公を決死に介抱し、ヒロインを排除しようとする。しかし紆余曲折あり、最終的には母親同士さえ完全に和解する。小学校から高校までの長い時があり、完全に断絶しており、破壊されていた人間関係が修復される。言ってしまえばご都合ストーリーと言われても仕方がない内容だとは思う。しかし、だからこそ、これが創作だからこそ心が揺さぶられるのかもしれない。

覆水は盆に返らないが、盆を置きなおして水を注ぎ直すことはできる

ここまでの流れからするに、一度完全に破壊しつくされ、もう二度と戻らなくなったものでさえ、それは努力によって修復することが可能だということを示唆しているのではないかと私は感じた。少なくとも私は本作からそれを感じ取った。

覆水が盆に返ることは決してない。零れた水は流れ、地面に染み込んだり、表面積が増えたことで蒸発する。その事実は決して変わらない。

ただ盆を元に戻し、そこに水を注ぐことはできる。過去は変えられないが、未来は変えることができる。

だから、何か過ちを犯して悔いているなら、それは改め、前を向いてもいいかもしれない。そして自分がかつて被害者だったとしても、赦していいのかもしれない。一生相手を憎み続けるのではなく、時には赦すことも必要なのではないかということだ。或いはそうでなくても過去に立ち向かうこともそうかもしれない。

これは『超かぐや姫!』における、いろはと母親の関係や、『どこよりも遠い場所にいる君へ』の月ヶ瀬が逃げていた過去、七尾が血縁者からの虐待に向き合うことにしたことも似たようなことかもしれない。

過去最高のボーイミーツガール

本作は障害者と健常者のラブストーリーや突き放したヒロインを再び追う部分では『ジョゼと虎と魚たち』に近いし、集団の破壊や同調という意味では『トラペジウム』も近いと思う。

陰湿さでは『かがみの孤城』のキャラクター背景も一つ近い部分があるかもしれない。離れてまたくっつく取り戻したい想いでは『アリスとテレスのまぼろし工場』の要素も少しありそうだ。

そして過去に立ち向かう姿勢では『どこよりも遠い場所にいる君へ』の要素もあるように思える。

人生は何があろうとやり直せる、死に値するものなどどのようなことだってない。そして過去は取り消せないが、やり直すことはできる。私は本作からそれを感じることができた。過去にもそういうメッセージを受け取ってきた作品はあったと思うが、本作はそれを非常に強く感じられたように思う。

また本作は陰湿でひどいいじめが小学校で始まり、ヒロインが転校し、そこから高校に入り結構なブランクがあったのに、クラスメイトとヒロインの再会によっていじめが再発するし、親同士の確執が再燃するというなかなかグロテスクな設定があり、最終的にはそれを乗り越え、全てを終結させるが、しかしそれでも、歪さは残ったままで、ただそれがいい。そう、問題は解決するが、全てがきれいに収まるわけではない。

私はキノの旅にある「世界は美しくなんかない。そしてそれ故に、美しい。」というワードが好きなのだが、本作はこれに非常に合致する。これ以上ないほどにだ。だからこそ、これ以上ないまでにカタルシスを感じたのかもしれない。正直、醜さを醜さのまま据え置いているという点では『メイクアガール』にも重なる部分があると感じるし、『メイクアガール』は間違いなく過去の鑑賞作の中でも群を抜いた傑作だった。そして本作も、そうだ。

そして本作の聲の形とは聾唖者でヒロインの聲の形そのものであり、拙くもどかしくとも、気持ちのこもったThe shape of voiceということもよく理解できた。タイトルがここまで響いた作品も他にない。決して綺麗じゃない、醜さがあるかもしれない。だからこそ、それ故に美しいのだ。

すなわち、本作は私が観てきた中で間違いなく最高のボーイミーツガールだった。ここまでの積み重ねがあったからこそ、今初めて観てよかった作品だったと、そう感じたのだ。

あとがき

実は私は京都アニメーションの作品が苦手で、理由として『けいおん!』や『響け!ユーフォニアム』があまり好みでなく『涼宮ハルヒ』シリーズも得意ではない。

しかし本作は非常によく、『ヴァイオレット・エヴァーガーデン 外伝 -永遠と自動手記人形-』も好みの作品であるから、要因を少し探ってみたところ本作と脚本家が同じであることが分かったので、このあたりが影響しているのかもしれないと思った。

他にもシャフトやスタジオコロリドなど苦手としているスタジオはあるのだが、いずれも何かしらの化学反応が起きると劇的な輝きというか、カウンターというか、非常に素晴らしい作品が出てきたりするので、固定観念をあまり持っても仕方がないのも確かな気はしている。ちなみにシャフトについては単に物語シリーズが苦手なだけな可能性は多分にある。

Lyndbaum v4.22アップデート

adiary置き換え計画始動から連なる、Lyndbaum v4.20アップデートの続き。

バージョンが0.2上がった。今回はバグ修正と機能削除がメインで3,434行追加、15,841行削除と、追加の5倍くらい削除している。

バグ修正

「設定」→「基本設定」で画面幅を最低にしたときに要素が突き抜ける不具合の修正

v4.20でやった管理画面のごみ箱で画面幅を狭めると要素がボックスを貫通する不具合の修正の仲間。

投稿画面にあるフォントの色やサイズを変える記法を挿入する機能がMarkdownで機能していなかった問題を修正

MarkdownモードではSatsuki記法のガワである[::]のようなものを出すだけで、機能していなかったため、機能するようにした。

なお、この機能はこのサイトではこれまでに使っていないが、便利な気がしたので対応した。

<details><summary>の中でMarkdownが展開されない問題の対応

この中ではMarkdownが展開されず、単なる文字列として出てくる状態だったため、中にあるMarkdownを解釈できるようにした。

機能変更

管理画面にあったSatsuki2テーマの依存を解除

管理画面にはadiaryの表示基盤ともいえるSatsuki2テーマと深く結びついており、引きはがせない場所があった。

どうしようもないので管理画面用に切り離したSatsuki2テーマを残し、公開画面用のSatsuki2テーマを別に切り出した。

既存テーマのレスポンシブ対応

既存テーマのレスポンシブ対応を行い、SPモードの削除ができるようにした。

なお横長の画像によってレイアウトが完全に固定されているなど、レスポンシブ対応が単純にできないテーマはすべて削除した。

また3カラムテーマと2カラムテーマを切り替える機能は色々無理があったので個別にテーマを作って対処した。

既定のテーマをlyndbaum/lyndbaumに変更

一番便利に作ってあるため。ほかのテーマはレスポンシブ対応こそしたもののadiary自体の思想を引きずっているため、レスポンシブ対応したというだけで終わっている。

これもSatsuki2テーマをコア機能から引きはがしたからこそ、できた対応である。

System infoプラグインにシステム名とバージョンを出すようにした

今までずっとここにバージョン出てないのはおかしいと思っていたので…。

機能削除

SPモードの全廃

関連機能を消し去り、SPモードを全廃した。これだけでコードが13,000行ほど消えた。

3.50未満のアップデータの削除

3.52devからフォークしているため、それより前は過去として忘れることにした。

ユースケースがなさそうな記法とユースケースが思いつかない記法をバッサリ消した

v4.15時点でやった記法削除の続き対応として、Google検索やWikipediaリンクの記法と言った、それ事前に調べてリンク取ってくるからURLリンク貼ったら終わる米みたいな記法や、はてなダイアリー時代の記法、その他諸々を消した。TeXも私が動作確認できないので消した。

さつき記法の削除

Pukiwiki記法すら忘れられかけているのに、さつき記法は無理があろう…と言うことで、ほとんどの記法を削除した。目次や画像、動画などと言った一部の記法や、記法拡張機能は便利なので残してあるがMarkdownの拡張記法という位置づけになっており、最早さつき記法とは呼べない。

関連PR

V420 bugfix1 / V421 / V422

あとがき

なんか前にやることがなくなってきたとか書いた気がするが、インポート機能やエクスポート機能が機能削除の連続で破綻してきているなどで、整合が取れなくなってきている部分がある他、地味な対応が結構浮かんできているので、やりたいこと自体はだいぶ出てきている。

ただやっていくペースは落としていき、後はのんびりやっていきたい構えだ。実際今回も変更行数こそ多かったものの、v4.20同様に変更点そのものは減っている。

そういえば、本家からどの程度変わっているのか差分を確認すると、現時点で1,073ファイルの変更、25,550行の追加、43,212行の削除があるようだ。

開発や公開の続いている和製CMSの一覧

掲載基準は私が見つけることができたもので、配布サイトが生きていたものとしている。

adiary

最終更新:2026年6月24日

高速で使いやすいCMS - ブログ/Wiki/Markdown - adiary official website

Perl製のCMSで日記機能とコンテンツページ機能を備える、現在うちのブログで使っているやつ。はてなダイアリーのクローンのような存在で、リッチな機能が薄く、古い人間には使いやすいと思う。モダンよりレガシーみたいな感じだが、Markdownが使えるのが最大の特徴だ。

実行環境はCGI/FastCGI/組み込みサーバーの三つが選べるため、レンタルサーバーでも、自宅サーバーでもVPSでも動く。動作は軽快でWordPressより遥かに軽い。

対応記法は、はてな記法の亜種である、さつき記法、Markdown、HTML、reStructuredTextがある。Markdown記法は概ね問題なく使えるが癖がある。CommonMarkとGFMが中途半端に実装されており、脚注記法などに対応していないほか、エスケープ周りで細かいバグがある。

DBはテキストファイルDBとRDBが選べるため、格安レンタルサーバーでも使いやすい。RDBはMySQLとPostgreSQLに対応している。但し個人ブログ程度の記事量ではテキストDBがDBドライバが起動してる間に全てを完了させられるため、RDBを選ぶ意味はほとんどないと思う。SQLiteに対応するパッチを書いたことがあるが、800記事程度ではFastCGI環境下ですらテキストDBの方が早かった。

個人開発であるため、仕様に思想が強く好き嫌いが解れる部分も多々あると思うが、その辺りはソースをいじってなんとかできる。

adiary-extends

私はかつてadiary-extendsとして以下のように幾らか小手先を改造したものを運用していたが、後述するように今はadiaryをベースにした魔改造CMSを使っている。

  • クリップボードから画像をアップロード可能にした
    • 本家は今でもたぶんできないと思う
  • Lightbox2をFancybox v3.5.7に置換
    • これは後日、本家にも似たような対応が入った
  • 任意の画像をOGPに設定可能にした
    • 凄い雑な実装をしているが一応可能になっている
    • 本家では記事先頭の画像がOGPになる

他にもいくつか追加している。

Lyndbaum

私がadiary-extendsのアップデートを続けて改修しているもので、adiary置き換え計画始動から連なるものとして膨大な手入れをしている。このへんに変更履歴があるが最新版であるv4.21については本記事執筆時点ではまとめていない。

大まかに次の改修を施しているが、adiary-extends時代にやったことも混ざっている。

  • 不必要な機能の削除
    • RDB機能の削除
      • 前述のとおり存在意義がないため
    • マルチサイト機能の削除
      • WordPressなどにあるような複数サイト運営の機能を丸ごと削除した
      • WordPressでもそうだが、この機能には多くの問題があり、バグも内包していたため切り捨てた
  • レスポンシブデザイン対応
    • かつてはUAでPCとSPとサーバーサイドで切り替えて応答HTMLを変える懐かしの実装だったが、これをやめてレスポンシブデザインにした
  • SEOなど、クローラー向けの改善
    • LCP, FCP, CLSなどの初回ロードにかかわる数値の改善
    • meta descriptionの追加
    • OGPを無条件で出せるようにした
      • 元々UAでOGPが出るようになっていたが、無条件で出すようにした
  • Markdownパーサーの改良
    • 不足していた構文の実装のためCommon MarkやGFMの機能を取り込み、バグってる構文もこれに基づいて修正した
    • Common MarkやGFMはそのままだと扱いづらいため、日本人向け[1]に改良してある
  • 管理画面と閲覧画面の分離
    • 何を言っているか分からないと思うがadiaryは管理画面と閲覧画面が融合しており管理画面に余計な要素が多かった
    • このため一般的なCMS同様、これを完全に分離した
  • コメントスパム対策の強化
    • IPアドレスによるコメント禁止機能(CIDR対応)、NGワード機能、リンクを含む投稿の制限、短すぎるコメント、日本語を含まないコメントの制限、連投制限など、スパムコメントを弾く機能を強化した
  • NSFWコンテンツ対応
    • 年齢制限のあるコンテンツを公開するための年齢確認、ブラー、検索エンジンへの通知機能を追加している

tDiary

最終更新:2026年10月9日

tDiary.org

みんな大好きtDiary!と言いたいところだが、このCMSの存在を知っている人は今ではインターネット老人か、ニッチな趣味を持つ若者くらいだろう。

はてなダイアリーの元祖クローンであり、RubyでCGIとして実装されている。

はてなダイアリークローンとしてみると、adiaryの親戚的なCMSでもある。公開日はtDiaryの方が古く2002年2月20日で、adiaryは2006年7月10日だ。

てがろぐ

最終更新:2026年6月29日

お手軽マイクロブログCGI「てがろぐ」:スキン式で複数ユーザ対応 - にししふぁくとりー

今日において創作界隈で一定の支持を持っているCMSで、Perl製のCGIとして実装されている。初出は2017年12月07日と、Perl CGIとしてはかなり新しい部類だ。

Twitterのようなタイムラインを作ることができるため、マイクロブログに近い使い方ができるのが特徴で、SNSに疲れたけど何かを発信したいとか、自分のサイトにたぶんを載せたい用途にマッチすると思われる。

Serene Bach

最終更新:2026年10月25日

sb開発研究所

Go製でCGIと組み込みサーバーに対応しているCMS。かつてsbとして一世を風靡していたCMSの一つで、初版であるsbは2004年9月14日公開と、tDiary並の古参である。現在ではWordPressとHugoの中間にある軽量な選択肢を名乗っている。

本記事掲載のCMSの中で唯一スクリプトでなく、バイナリで動作するものだ。動作してはmacOS、FreeBSD、Linux、Windows向けのARM64, AMD64(x64)バイナリがサポートされているようで、クロスプラットフォームで動作するのが嬉しい。

個人的にはBlogn→BlognPlus→の流れでCMSを色々触っている中で採用候補としてsbを試したことがあるので、少しだけ縁がある。

英語の説明がメインで日本語の説明が少ないのが日本人向けとしてはやや難しそうだ。ちょっと興味が湧いたので実際にレンタルサーバーに置いて動くか見たので、[さくらのレンタルサーバーにSerene Bach v4を設置してみた
](0824)で記事にしたりもした。

shiromuku DIARY

最終更新:2024年4月3日

Perl CGI's By Mrs. Shiromuku

Perl製のCGIで、KENT-WEBの掲示板CGIにありそうな素朴なデザインのやつ。最近は更新されていないようだが、2年前に更新があったので載せることにした。

確認できた範囲では2003年6月12日が最も古いタイムスタンプだったので、かなりの古参のようだ。全く聞いたことがなかったのでCMS界隈の広さを感じる。

Pukiwiki

最終更新:2022年3月30日

FrontPage - PukiWiki-official

現在でも日本Wiki界隈のデファクトスタンダードと思われるWiki向けCMS。現在でも結構活発にフォークされておりMarkdown対応を謳うものも存在する。

基本的にCGIであり、PHPで実装されている。Markdownが普及した現在となっては中々厳しい状況だと思うが、チケット管理システムであるBacklogのWikiでPukiwiki構文が使えるなど、割と日本に根差した存在だと思う。

初出を調べてみたもののWikipediaにある2001年以上の情報を得ることは出来ず不明だった。作者のサイトのWebArchiveは2004年以降しかなく、見つけられた範囲で最も古い利用者のサイトでさえ、2002年時点だった。

PukiwikiはYukiwikiのPHP移植であり、元ネタがあるのだがそちらは2018年で更新が止まっているようだ。しかしこれだけ古いと、もうYukiwikiの作者は鬼籍に入っているだろうな…と思ったらめちゃくちゃインターネットで見たことあって椅子から転げ落ちてしまった。というかFediverse界でも超有名人だったわ…。

Pukiwikiは私が最も長く運用しているCMSで、現在でもFrontPage - ECO-Wiki (lycolia)で利用しているし、過去にROの職Wikiを運営したこともあるほどだ。ROの職Wikiはアクセスが凄まじく、一日のPCが一万ほどあったと思うし、当時のさくらのレンサバは負荷に耐えられず、よく落ちていた。

まとめ

基本的に最新版のことを書いている。

ソフトウェア 動作方式 実装言語 初出 最終更新
adiary CGI/FastCGI/組み込みサーバー Perl 2006年7月10日 2026年6月24日
tDiary CGI Ruby 2002年2月20日 2026年10月25日
てがろぐ CGI Perl 2017年12月07日 2026年6月29日
Serene Bach CGI/組み込みサーバー Go 2004年9月14日 2026年10月25日
shiromuku DIARY CGI Perl 2003年6月12日 2024年4月3日
Pukiwiki CGI PHP 2001年のどこか 2022年3月30日

あとがき

本記事は2026/08/25 12:59に下書きを書き長らく放置していたが、エイヤの気合で何とか出すことができた。下書きを書いた時点ではLyndbaumなどなく、adiary-extendsもしょっぱい修正しかなかったため、この部分を随分書き直す羽目になった…w


  1. 改行を素直に改行として扱う方式。Qiitaやはてなダイアリー、Typoraでは改行を開業として扱えるが、本来の仕様では行末に半角スペースが不達必要で日本語を書くにあたり煩わしいのだ。 ↩