Skip to content

ライトフィールドフォトアプリ — Looking Gloves のGo対応とアスペクト比調整

概要

Looking Gloves は、ライトフィールド写真(Light Field Photo)から Looking Glass(LKG)向けキルト画像を作成するための Web アプリケーションです。 本記事では、Looking Glass Go デバイスへの対応作業と、それに伴うアスペクト比調整機能の実装について記録します。

主な対応内容は以下のとおりです。

  • Looking Glass Go のキルト仕様に合わせたアスペクト比設定
  • constant.ts による設定値の一元管理
  • デバイス選択機能の実装(Go / 標準 LKG の切り替え)
  • オートフォーカス機能の試作(ラプラシアン分散によるシャープネス測定)
  • クロップ関連のバグ修正
  • Netlify へのデプロイ手順

デバイス対応

Looking Glass Go の特性

Looking Glass Go は、従来の Looking Glass と比較して以下の点が異なります。

項目Looking Glass Go標準 LKG(Portrait 等)
アスペクト比約 0.55(11:20 相当)約 0.75(3:4)
キルト列数11 列5〜8 列
推奨キルト行数6 行9 行
表示特性ポータブル、小型据え置き、大画面

Go の推奨クイルト設定(qs11x5a0.55)をそのまま使用すると、一部が暗くなる問題や、サイズが正方形にならない不具合が発生しました。 コード全体を分析し、フレーム数を制限しているスクリプトを特定して修正することで解決しています。

デバイス選択機能

当初は constant.ts の値を動的に変更するだけで対応できると想定していましたが、ビルド時に初期値が必要であることが判明しました。 初期値を設定すると、アプリ起動時にその値を読み込んで処理を開始するスクリプトが存在するため、ディスプレイ選択後に設定値を再参照させる仕組みが必要でした。

最終的に、確認モーダルがプレビュー画面の下に隠れてしまうバグも併せて修正しています。


アスペクト比設定

constant.ts の構成

アプリケーション全体の設定は utils/constant.ts に集約されています。 以下の設定値を変更することで、各デバイスのキルト仕様に対応できます。

typescript
export const COLS = 8;                    // キルトの列数
export const ASPECT_RATIO = 3 / 4;       // アスペクト比(幅 / 高さ)
export const FRAME_WIDTH = 800;           // フレーム幅(px)
export const FRAME_HEIGHT = Math.floor(FRAME_WIDTH / ASPECT_RATIO);  // 自動算出
  • COLS --- キルト画像の列数です。デバイスによって最適値が異なります
  • ASPECT_RATIO --- フレームの縦横比です。Go の場合は 0.55 を設定します
  • FRAME_WIDTH --- 各フレームの幅です。高さは比率から自動計算されます

設定変更のポイント

constant.ts 内のデータを書き換えればすべて調整可能です。 アスペクト比を設定する際は ASPECT_RATIOFRAME_WIDTH の 2 行を編集してください。 高さ(FRAME_HEIGHT)は自動計算されます。

キルト仕様の参照先

各デバイスの推奨キルト設定は公式ドキュメントで確認できます。


オートフォーカス機能

概要

ライトフィールド写真のフォーカスポイントを自動で決定する機能の試作を行いました。 シャープネス関数(ラプラシアン分散)を用いて、最も鮮明に見えるフォーカス値を探索するアプローチです。

アルゴリズムの流れ

  1. 複数のフォーカス候補値(-40 から +40 まで)を用意します
  2. 各候補値に対してフォーカスを設定し、フレームをレンダリングします
  3. レンダリング結果に対してラプラシアン分散を計算してシャープネスを測定します
  4. 最もシャープネス値が高い候補を最適フォーカス値として採用します

実装コード

typescript
// src/utils/autoFocus.ts
export async function findBestFocus(
  frames: HTMLCanvasElement[],
  focusSetterRaw: (v: number) => void,
  candidates: number[] = [-40, -30, -20, -10, 0, 10, 20, 30, 40]
): Promise<number> {
  if (!frames.length) return 0;

  const tmp = document.createElement('canvas');
  const ctx = tmp.getContext('2d')!;
  tmp.width = 160;
  tmp.height = 160;

  let best = candidates[0];
  let bestSharp = -Infinity;

  for (const f of candidates) {
    focusSetterRaw(f);
    await new Promise(r => requestAnimationFrame(r));

    ctx.drawImage(frames[0], 0, 0, tmp.width, tmp.height);
    const { data, width, height } = ctx.getImageData(0, 0, tmp.width, tmp.height);

    const sharp = laplacianVar(data, width, height);
    if (sharp > bestSharp) {
      bestSharp = sharp;
      best = f;
    }
  }
  return best;
}

ラプラシアン分散の計算

シャープネス測定の中核となる laplacianVar 関数では、以下の処理を行います。

  1. RGB 画像をグレースケールに変換します(ITU-R BT.601 加重平均)
  2. 3x3 ラプラシアンカーネル [0,1,0, 1,-4,1, 0,1,0] で畳み込みを行います
  3. 畳み込み結果の分散値を算出します(分散が大きいほどシャープ)

現状の課題

試作段階では期待どおりの結果が得られず、フォーカス調整後の表示に問題が発生しました。 ChatGPT にスクリーンショットを共有して修正を試みましたが、1 日では修正しきれていません。 今後の改善課題として残っています。


クロップ機能のバグ修正

開発中に以下のバグが確認され、修正を行いました。

バグ内容状態
クロップ後に画像が正方形になる修正済み
Go 以外のデバイスへの対応修正済み
前後数フレームを黒(または透明)にする設定の追加修正済み
クロップのデフォルトが細長くなる修正済み
横向き画像を正方形フレームに圧縮する機能未対応

クロップのバグ修正中に、クロップ以外のすべての画像が正方形になってしまう予期しない不具合が発生しました。 原因を特定できなかったため、ChatGPT の履歴を参照しながら一から作成し直すことで解決しています。


デプロイ

Looking Gloves は Netlify にデプロイしています。 Next.js の静的エクスポート機能を使用してビルドし、Netlify CLI でデプロイします。

デプロイ手順

bash
# 1. プロジェクトディレクトリに移動します
cd C:\Users\m-mizukami\Desktop\looking-gloves-main

# 2. Netlify の認証トークンを設定します
set NETLIFY_AUTH_TOKEN=<your-token>

# 3. 静的ファイルをエクスポートします
npm run export

# 4. 本番環境にデプロイします
netlify deploy --dir=out --prod
  • npm run export は Next.js の next export を実行し、out/ ディレクトリに静的 HTML を生成します
  • --prod フラグを付けることで本番 URL にデプロイされます

AI補完

AI補完 --- ライトフィールド写真の基礎知識

ライトフィールド写真とは、通常の 2D 写真とは異なり、光線の方向情報も記録する撮影方式です。 通常のカメラが「どの位置にどの色の光があるか」だけを記録するのに対し、ライトフィールドカメラは「どの方向から光が来ているか」も記録します。 この追加情報により、撮影後にフォーカスポイントの変更や視点のシフトが可能になります。 Looking Glass 向けコンテンツとの相性が良く、1 回の撮影から複数視点の画像を生成できるため、キルト画像の素材として有用です。

AI補完 --- ラプラシアン分散によるフォーカス検出

ラプラシアン分散は、画像処理の分野で広く使われるシャープネス(ピント合焦度)の指標です。 ラプラシアンフィルタは画像の二次微分を近似するカーネルであり、エッジや輝度変化の急峻な部分に強く反応します。 ピントが合っている画像ではエッジが鮮明であるため、ラプラシアンフィルタの出力値の分散が大きくなります。 逆に、ピントがぼけた画像ではエッジが滑らかに変化するため、分散値が小さくなります。 この性質を利用して、複数のフォーカス候補の中から最も分散値が大きいものを選択することで、自動フォーカスを実現します。

AI補完 --- Next.js の静的エクスポートと Netlify デプロイ

Next.js の next export コマンドは、サーバーサイドレンダリング(SSR)を使用せず、完全に静的な HTML/CSS/JS ファイルを生成します。 生成されたファイルは out/ ディレクトリに出力され、任意の静的ホスティングサービスにデプロイできます。

Netlify は静的サイトのホスティングに特化したサービスであり、CLI ツールを使ったデプロイが簡単です。 netlify deploy --dir=out --prod コマンドで out/ ディレクトリの内容が本番環境にアップロードされます。 開発中のプレビューデプロイ(--prod なし)と本番デプロイを使い分けることで、安全にリリース管理が行えます。