猫の手ツール

Algorithm & UI/UX

数学的に「揺らす」:
ピクセル置換による波紋の再現

このツールでは、単なる画像のオーバレイではなく、Canvasのピクセルデータを直接操作する「ディスプレイスメント・マッピング」という手法を採用しています。静止画に物理的な実在感を与えるためのアルゴリズムを解説します。

このツールの入力から出力まで

現行コードの確認日:

入力
写真を選び、タップで波紋の中心、強さ・細かさ・範囲を指定します。
処理
中心からの距離と正弦波で参照する画素位置をずらし、外側へ効果を減衰させます。
出力
出力は長辺最大 1500px の JPEG(品質指定 0.92)の静止画です。

まず確認する点:強いずれでは小数を切り捨てる参照による段差が出ます。波紋の中心と、影響する半径を別の設定として見ます。

仕上がりの例

写真に水面の波紋のようなゆがみを加えた例です。

加工前・加工後
左はキャットタワーで口を開けた愛猫の写真。右は写真の一部に波紋状のゆがみを加えた画像。

加工前・加工後

左はキャットタワーで口を開けた愛猫の写真。右は写真の一部に波紋状のゆがみを加えた画像。

掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。

保存済み検証記録の条件を見る

資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。

記録の入力画像
800×805px (JPEG)
記録の出力画像
800×805px (JPEG)
保存済み記録の主な操作・出力条件
画面の項目記録の値現行コードの初期値・範囲
波の強さ(歪み量)40初期値 40 / 範囲 0〜100
波の細かさ(周波数)60初期値 60 / 範囲 5〜150
波の影響範囲100初期値 100 / 範囲 10〜150

記録では「この揺らぎで完成!」から完成表示へ進んでいます。初期値と記録値を比べてから、効果を1項目ずつ変えると作用を確認しやすくなります。

資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。

実装の確認箇所:src/pages/image/water-ripple/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。

仕組みを読み進めるときは、イベントリスナーの基礎(タップやドラッグから描画へつなぐ流れ)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。

1. 正弦波(サイン波)による座標の歪み計算

波紋の正体は、中心からの距離に応じて「元画像のどの位置からピクセルを拾ってくるか」を計算した結果です。当ツールでは Math.sin を活用し、中心からの距離(dist)に基づいてピクセルの取得位置を意図的にずらしています。

この実装のメリットは、静止画でありながら光の屈折のような動的な変化をシミュレートできる点にあります。

波紋中心からの距離に応じたずらし量の例です。dist と maxDist の単位は処理画像上の px です。

function rippleAmount(dist, maxDist, strength, frequency) {
  if (dist <= 0 || dist >= maxDist) return 0;
  return Math.sin(dist * frequency / 50) * strength / 10 * (1 - dist / maxDist);
}

2. メモリ効率を重視したピクセルサンプリング

高解像度の画像を処理する際、Canvas APIの getImageData と putImageData を高頻度で回すとブラウザがフリーズする恐れがあります。そこで、計算用の「オフスクリーン・キャンバス」と「表示用キャンバス」を分離し、さらに全ピクセルを一括で TypedArray として操作する工夫を施しました。

ループ内での計算を最小限に抑え、同じサイズの配列へ結果を書き込みます。ただし全画素の計算はメインスレッドで行うため、サイズや端末によって操作待ちが生じます。

3. iPhone(HEIC形式)への動的対応と最適化

このツールでは、HEICを選んだ場合はブラウザ内で別の画像形式に変換してから編集します。当ツールでは、必要なときだけ heic2any ライブラリを動的にインポートすることで、初期ロードの軽量化と、iPhoneユーザーの利便性を両立させています。

また、高解像度すぎる画像はブラウザのメモリ制限に抵触するため、一定以上のサイズはアスペクト比を維持しながら長辺 1500px 以下へ自動リサイズする処理を実装しています。

出力の仕様と確認する点

強さは 0〜100 で座標のずらし幅は最大 10px 相当、細かさは 5〜150 で正弦波の周期を変えます。範囲は画像の対角線長に対する 10〜150% です。クリックまたはタップで中心を変更します。

外側に近づくほど 1−距離/半径 の線形係数で減衰します。参照画素は小数を切り捨てて取得するため、強い設定では細部に段差が見える場合があります。範囲外は画像端の画素を使います。

出力は長辺最大 1500px の JPEG(品質指定 0.92)の静止画です。水の時間変化を計算する物理シミュレーションや動画ではなく、写真の画素位置をずらす効果です。