Algorithm & Image Processing
輝度判定とアルファ補完で作る
高精度な「背景透過」アルゴリズム
紙に書いた文字をデジタル化する際、最大の壁となるのは「紙の白さ」と「インクの境界」の判定です。単純な閾値カットでは消えすぎてしまう繊細な線を、いかに滑らかに抽出するか。その核心部を解説します。
1. RGBから「輝度」を抽出する判定ロジック
「白い紙を消す」という処理を実装する際、単純なカラー判定では影や照明のムラに対応できません。そこで本ツールでは、ピクセルごとのRGB値に対し、人間の視覚特性に基づいた重み付けを行って「輝度(明るさ)」を算出しています。
算出した輝度と、ユーザーがスライダーで設定した「境界線」を比較することで、背景かスタンプ部分かを瞬時に判断しています。
// 輝度計算と背景判定のコア
for (let i = 0; i < data.length; i += 4) {
// 人間の目に合わせた輝度算出式
const brightness = (data[i] * 0.299) + (data[i+1] * 0.587) + (data[i+2] * 0.114);
if (brightness > userThreshold) {
// 背景とみなして透明度をゼロにする
data[i+3] = 0;
} else {
// スタンプ部分の処理(後述のアルファ補完へ)
}
} この実装のメリットは、青みがかった写真や黄色みがかった古い紙でも、明暗の差さえあれば正確に文字を分離できる汎用性の高さにあります。
2. ジャギーを防ぐ「動的アルファブレンディング」
背景を透明にする際、特定の明るさを境に「100%透明か0%透明か」を切り替えると、文字の縁がガタガタ(ジャギー)になってしまいます。
本ツールでは、境界線付近に「遊び(エッジ領域)」を設け、輝度に応じて透明度をグラデーション状に変化させる補間処理を施しています。これにより、ボールペンの掠れや印鑑の質感を残したまま、自然な切り抜きを実現しています。
// エッジを滑らかにするアルファ値の計算
const edgeRange = // 独自のチューニング値;
// 境界線に近いほど透明度を上げ、遠いほど不透明にする
if (brightness > userThreshold - edgeRange) {
// 境界付近のピクセルに動的なアルファ値を適用
// ここに線形補間の計算処理が入ります
data[i+3] = Math.floor(255 * ((userThreshold - brightness) / edgeRange));
} else {
data[i+3] = 255; // 完全に不透明
} プロの視点で見ると、この「数ピクセル分の不透明度制御」こそが、安価な画像編集ソフトと専門ツールの仕上がりの差を分けるポイントです。
3. ブラウザ完結を実現する「フロントエンドHEIC変換」
iPhoneユーザーが撮影した写真は .heic 形式であることが多く、通常のCanvasでは読み込めません。これをサーバーに送らず処理するため、heic2any ライブラリを用いてブラウザ上で動的にJPEG/PNGへ変換するパイプラインを構築しています。
この処理を Web Worker や async/await で非同期に実行することで、大容量ファイルでもUIをフリーズさせずに処理を開始できる設計にしています。
Developer's Note
一番こだわったのは、「スライダーを動かした時のプレビュー速度」です。
数千万画素の画像を毎回全ピクセル計算すると、モバイル端末では動作が重くなります。そこで本ツールでは、表示用の「軽量プレビューCanvas」と保存用の「フル解像度Canvas」を内部で分離し、操作中は軽量版のみを更新、保存時のみ重い処理を走らせる「ティアード・レンダリング(段階描画)」を採用しました。
「プライバシーを守るためにサーバーへ送らない」という制約を、フロントエンドの最適化技術で克服するのが、このツールの開発テーマでした。