💡 はじめに
モバイルアプリでHEIC画像の処理を実装していると、「解像度を下げたのになぜかファイルサイズが大きくなる...」という謎の現象に遭遇したことはありませんか?
自分も以前、画像リサイズ機能を実装していた際、解像度は正しく縮小されているのに、なぜかWebViewがメモリ不足でクラッシュするという問題に直面しました。
この記事では、その原因と解決策について、実体験をもとに紹介します。
📱 実装していた処理の流れ
当初想定していたのは、こんなシンプルな流れでした
// 想定していた処理
1. HEIC画像を読み込み
2. Canvas に縮小して描画
3. toDataURL('image/jpeg') でJPEG変換
4. ファイルサイズ削減完了!✨
解像度もサイズも両方小さくなって、メモリにも優しい...はずでした。
⚠️ 問題発生:予想外のファイル肥大化
実際にテストしてみると、驚きの結果が、、
- ✅ 解像度:2048x1536 → 1024x768 に正常に縮小
- ❌ ファイルサイズ:1.2MB → 3.8MB に増大!
- 💥 WebViewのメモリ使用量が上限(通常32MB)に到達してクラッシュ
明らかにおかしい...🤔
🔍 原因調査:Canvas描画の隠れた仕様
デバッグしてみると、意外な事実が判明しました。
Canvas描画時の自動PNG変換が犯人でした
// 期待していた処理
canvas.toDataURL('image/jpeg', 0.8); // 直接JPEG変換
// 実際に起きていたこと
// 1. Canvas描画時に自動的にPNG形式で内部保存
// 2. toDataURL()でJPEGに再変換
// 3. PNG→JPEG変換でファイルサイズが肥大化
圧縮率の違いが致命的
フォーマット | 圧縮方式 | 同じ画像のファイルサイズ |
---|---|---|
HEIC | 高効率圧縮 | 1.2MB 🟢 |
PNG | 可逆圧縮 | 3.8MB 🔴 |
JPEG | 非可逆圧縮 | 0.8MB 🟡 |
Canvas内部でPNG形式になったことで、圧縮効率の悪いフォーマットに変換され、結果的にファイルサイズが3倍以上に膨らんでいました!
💥 WebViewメモリ制限との戦い
ファイルサイズ肥大化の影響⤵️
- 📈 1つの画像処理で4MB近いメモリを消費
- ⏰ 複数画像処理時に累積でメモリ使用量が急増
- 🚫 WebViewの制限(32MB〜64MB)に到達
- ❌ Out of Memory エラーで処理が強制終了
🚀 解決策:効率的な画像変換手法
この問題を解決するために、以下のアプローチを実装しました。
1. HEICの自動JPEG変換
- ファイル形式を事前に判定してHEICを検出
-
auto
モードでHEIC画像を自動的にJPEG形式に変換 - ファイル名の拡張子も
.heic
から.jpg
に自動変更
2. 二分探索による品質最適化
- 目標ファイルサイズ以下で最高品質を自動探索
- 品質パラメータを0.1〜0.8の範囲で動的調整
- JPEG/WebP等の品質調整可能フォーマットのみ対象
3. 積極的なメモリ管理
- Canvas要素、画像要素、ObjectURLの即座解放
- ガベージコレクションを促進する遅延処理
- 中間生成ファイルの参照削除
4. 段階的リサイズによるメモリ制約対応
- ファイルサイズに応じて1段階または2段階でリサイズ
- 7MB超:60%→50%の2段階処理
- 5-7MB:30%の1段階処理
- 段階間でのメモリ解放待機
5. 再帰制御によるクラッシュ防止
- 最大再帰深度の制限(通常5回)
- 循環参照の検出と防止
- タイムアウト処理(30秒)
- 処理失敗時の安全な元ファイル返却
📈 導入効果:劇的な改善結果
改善後の結果:
- ✅ ファイルサイズ:3.8MB → 0.8MB に削減(78%減)
- ✅ メモリ使用量:32MB → 8MB に削減(75%減)
- ✅ クラッシュ率:15% → 0% に改善
- ✅ 処理速度:3秒 → 1.2秒 に高速化
- ✅ HEIC→JPEG変換:自動対応で互換性向上
特に複数画像の連続処理で、安定性が大幅に向上しました!✨
🔧 実装時の注意点
同様の問題を避けるためのポイント💡
Canvas描画の特性を理解する
- Canvas内部では自動的にPNG形式で保存される
-
toDataURL()
の第一引数で出力形式を指定 - HEICは事前にJPEG形式に変換してから処理
メモリ制限を常に意識
- WebViewの制限値を把握(通常32MB〜64MB)
- 段階的処理でメモリ使用量を分散
- 積極的なリソース解放でメモリリークを防止
エラーハンドリングと制御
- 再帰制御による無限ループ防止
- タイムアウト設定でハングアップ回避
- 適切な例外処理とフォールバック
✅ まとめ
画像処理を実装する際は、見た目の解像度だけでなく、内部的なフォーマット変換も意識することが重要です。
特にCanvas描画は便利ですが、意図しないPNG変換によるファイル肥大化のリスクがあります。
💡今回の解決策のポイント
- 事前のフォーマット判定でHEICを自動JPEG変換
- 二分探索による最適品質の自動調整
- 段階的リサイズでメモリ使用量を分散
- 積極的なリソース管理でメモリリークを防止
- 再帰制御でクラッシュを完全回避
たった数行の修正で、メモリ使用量を75%削減し、クラッシュを完全に解消できました。
同様の問題でお困りの方は、ぜひ一度Canvas描画の処理フローを見直してみてください!