LoginSignup
1
2

Canvasから画像へ、そして圧縮:JPEG対PNG

Last updated at Posted at 2024-04-08

このブログの内容:

  1. HTMLコンテンツから画像ファイルを生成するプロジェクトの事例紹介。
  2. PNGとJPEG画像を圧縮する方法についての考察。
  3. パフォーマンスとサイズのバランスをどう取るか。

ウェブ開発では、画像を使う際に品質とパフォーマンスのバランスを見つけることが大切です。最近、HTMLコンテンツから大きな画像を生成する必要があるプロジェクトに取り組みました。その過程でhtml2canvasライブラリを利用しました。これはDOMの一部をキャンバスに表示し、HTMLCanvasElement.toBlob()を使ってキャンバスからblobを作成し、HTMLを画像に変換します。使い方は以下の通りです:

toBlob(callback)
toBlob(callback, type)
toBlob(callback, type, quality)

PNGを選んだ理由

私のコードでは、toBlob()メソッドを次のように使用しました:

  const blob = await new Promise<Blob>((resolve, reject) => {
    canvas.toBlob((blob) => {
      if (blob) {
        resolve(blob);
      } else {
        reject(new Error('Failed to convert canvas to Blob.'));
      }
    }, 'image/png');
  });

プロジェクトで扱う元の素材がたまたまPNG形式の画像だった。ユーザーの操作によってこれらの画像を重ね合わせ、最終的な画像を作成する際にもPNGを選びました。上記のコードで示すように、それは十分に簡単でした。しかし、このPNG形式での画像生成方法では、生成された画像のサイズが最大で6MBになる場合があることに気づきました。

JPEGの選択

JPEG形式の選択を最初から検討していれば、より効率的にプロジェクトを進められた可能性があります。JPEGは品質レベルをサポートしており、toBlobの第三の「quality」パラメータを利用することができます。このパラメータは0から1の間の数値を取り、品質とファイルサイズのバランスを取るのに役立ちます。こちらがその例です:

return await new Promise<Blob>((resolve, reject) => {
  canvas.toBlob((blob) => {
    if (blob) {
      resolve(blob);
      } else {
        reject(new Error('Failed to convert canvas to Blob.'));
      }
    }, 'image/jpeg', 0.9); // <- quality (0-1)
  });

0.9という値が良いバランスだとわかりました。これで、同じキャンバスから作成された6MBのPNG画像と見分けがつかない1MBの画像を生成することができました。これにより、私の目標であったAWS Lambdaのペイロード制限(6MB)を下回ることができました。0.8から0.5までの値で実験しましたが、0.5でも画像品質の損失に気づかず、ファイルサイズの削減がパフォーマンスを大幅に改善するわけではなかったので、画像が悪くなるリスクを冒す価値はないと判断しました。

PNGを使う試み

しかし、すぐにJPEGを選択するわけではありませんでした。最初の反応はPNG画像を圧縮しようとすることでした。そのために、UPNGというパッケージを見つけました。こちらがそのコードです:

import UPNG from 'upng-js';

const arrayBuffer = await blob.arrayBuffer();
const img = UPNG.decode(arrayBuffer); 
const rgba = UPNG.toRGBA8(img)[0];
const compressedData = UPNG.encode([rgba], img.width, img.height, 256); // <- color palette 
const compressedBlob = new Blob([compressedData], {type: 'image/png'});
return compressedBlob

このコードがファイルサイズを減らす方法は、色の範囲を256色パレットに減少させることによります。この追加のステップによって、元の6MBの画像が2MBにまで減少しました。色圧縮されたPNG画像では、異なる色合いの間の遷移がかなり急になります。これにより、元のデザインにはなかった線のようなテクスチャが生じます。これらの線の目立ち方は、開発者が通常コントロールできないソース素材に大きく依存します。

下の画像がかなりズームインされており、効果が強調されています。

compression comparison.png

PNGをこのように圧縮しても、後でJPEGで達成したものの約2倍のサイズで画像が出てしまいました。その上、追加ステップは画像処理時間に少し余分な時間を要しました。

私が学んだことは、PNGは無損失(lossless)フォーマットであるということです。JPEG画像は保存時にわずかな品質を失うのに対し、PNGはそのような問題がありません。しかし、圧縮の選択肢が限られているため、品質を保証することはサイズの増加を意味します。

結論

PNGとJPEGはそれぞれ特有の利点があり、プロジェクトの要件に応じて適切なフォーマットを選択することが重要です。この経験から、ファイルサイズと画像品質のバランスを考慮しつつ、プロジェクトの目的に最適な画像フォーマットの選択がいかに重要であるかを学びました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2