JPEGの軽量化では、メタデータの削除が定石として語られます。ところがこの「メタデータを全部消す」という発想には、地味ながら無視できない副作用があります。
アイデアマンズでは画像最適化を手がけるなかで、この副作用を避けつつ確実に効く削り方を探してきました。その答えとして、EXIFのサムネイル画像だけを削除するGo言語のパッケージを公開しています。
この記事ではメタデータの全消しが危険な理由と、サムネイルだけを積極的に消すべき理由を整理し、実際の使い方をコードで示します。
メタデータの全消しは副作用を招く
JPEGにはピクセルデータ以外に、さまざまなメタデータが埋め込まれています。撮影日時やカメラの設定値だけでなく、表示に直接影響する情報も含まれます。これらをまとめて削除すると、次のような不具合が起きます。
- ICCプロファイルを消すと色味が変わる
- EXIFのOrientationを消すと画像の縦横の向きが変わる
- DPI情報が抜け落ちる(Webでの影響は小さい)
数値やテキストのメタデータは、そもそもファイルサイズをほとんど押し上げません。多くの画像はWeb公開の時点で最適化され、メタデータも整理済みです。
そう考えると、メタデータは軽量化の工程で無理に削らないほうが無難です。削って得られる数バイトのために、色や向きが崩れるリスクを負う理由はありません。
例外はサムネイルデータ
ただしメタデータのなかにひとつだけ極端に大きいものがあります。それがサムネイル画像です。
EXIFのサムネイルは、多くのカメラが撮影時に埋め込む縮小版のプレビュー画像です。EXIF内部ではIFD1という領域に、独立した小さなJPEGとして格納されています。他の数値メタデータが数バイト規模なのに対し、サムネイルは本画像とは別のもう1枚の画像です。
Webで表示する本画像があれば、埋め込みのサムネイルは通常まったく使われません。表示に使われないのに容量だけ消費する、削って副作用のないデータです。ここだけは積極的に削除する価値があります。
パッケージの使い方
このパッケージは外部依存のないPure Goで書かれています。CLIとしても、ライブラリとしても使えます。
CLIで単発変換する
入力と出力のパスを渡すと、サムネイルを除いたJPEGを書き出します。
go run exifremovethumbnail.go -in input.jpg -out output.jpg
ライブラリとして組み込む
画像処理パイプラインに組み込むなら、ライブラリのAPIを使います。ファイルパスを扱う関数と、バイト列を扱う関数の2つがあります。
import exifremovethumbnail "github.com/ideamans/go-exif-remove-thumbnail"
// ファイルベース
result, err := exifremovethumbnail.ExifRemoveThumbnail(inputPath, outputPath)
// メモリ上のバイト列ベース
outputData, result, err := exifremovethumbnail.ExifRemoveThumbnailBytes(inputData)
戻り値の result には、処理の内訳がそのまま入っています。
| フィールド | 型 | 内容 |
|---|---|---|
HadThumbnail |
bool | 元画像にサムネイルがあったか |
BeforeSize |
int64 | 変換前のサイズ(バイト) |
AfterSize |
int64 | 変換後のサイズ(バイト) |
ThumbnailSize |
int64 | 取り除いたサムネイルのサイズ(バイト) |
削れた量を推測ではなく実測で受け取れる点が便利です。ThumbnailSize を集計すれば、手元の画像群でどれだけ効くのかを事前に見積もれます。HadThumbnail が false の画像は、そもそもサムネイルを持っていなかったと分かります。
まとめ
- JPEGのメタデータ全消しは、ICCプロファイルや向きを壊す副作用がある
- 数値やテキストのメタデータは小さく、無理に削る旨みは薄い
- EXIFのサムネイルだけは本画像とは別の1枚の画像で、削っても副作用がない
-
go-exif-remove-thumbnailはサムネイルだけを外し、削減量を実測で返す
色や向きを守りながらサイズを削りたいときは、この一点狙いの削り方を試してみてください。