MeeeetupSDK ドキュメントEN

クイックスタート(React)

@meeeetup/camera-web は、カメラ・検出器・キャプチャセッションを管理するプロバイダと、そこから状態を読み取るコンポーネントを提供します。以下のコードはすべてクライアント側で動作し、利用側が送信処理を書かないかぎり、ブラウザの外にデータが出ることはありません。

pnpm add @meeeetup/camera-web

最小構成のカメラ

import { MeeeetUpCamProvider, CameraViewport, FaceGrid } from "@meeeetup/camera-web";

export function Reception() {
  return (
    <MeeeetUpCamProvider
      mode="passive"
      onBatchCapture={async (faces) => {
        await fetch("/capture", {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ images: faces.map((f) => f.dataUrl) }),
        });
      }}
    >
      <CameraViewport />
      <FaceGrid />
    </MeeeetUpCamProvider>
  );
}

これだけでパッシブ構成として完結します。ビューポートがカメラの利用を要求してトラッキング枠を描画し、すべての顔について最良のフレームが選ばれ、onBatchCapture が10秒ごとに、その時点で選ばれた顔を渡して呼び出されます。

設定をオブジェクトにまとめる

キャプチャ設定は単なるデータなので、1つのオブジェクトにまとめて展開して渡します。多くの導入がこの形に落ち着きます。設定を1つのモジュールから export し、コンポーネント側で受け取る形です。

import type { PassiveCaptureOnlyMeeeetUpCamProviderProps } from "@meeeetup/camera-web";

type CaptureConf = Omit<PassiveCaptureOnlyMeeeetUpCamProviderProps, "children">;

export const conf = {
  mode: "passive",
  minDetectionScore: 0.6,   // ignore what the detector is unsure about
  minFaceHeight: 0.18,      // ignore faces smaller than 18% of frame height
  minFrontalness: 55,       // ignore faces not turned towards the lens
  captureWindowMs: 2_000,   // search two seconds for the best frame
  cooldownMs: 30_000,       // then leave that person alone for 30s
  onBatchCapture: send,
} satisfies CaptureConf;
<MeeeetUpCamProvider {...conf}>
  <CameraViewport />
  <FaceGrid />
</MeeeetUpCamProvider>

mode と受け取り口(パッシブでは onBatchCapture、インタラクティブでは onCapture)以外のフィールドは、すべて省略できます。省略した項目には出荷時の既定値が適用されます。一覧は設定を参照してください。

キャプチャ状態を読み取る

useMeeeetUpCam() は、セッションの現在の状態を返します。mode による判別可能なユニオン型なので、モード固有のフィールドに触れる前に絞り込んでください。

import { useMeeeetUpCam } from "@meeeetup/camera-web";

function CaptureStatus() {
  const cam = useMeeeetUpCam();

  if (cam.error) return <p role="alert">{cam.error}</p>;
  if (!cam.ready) return <p>Starting the camera…</p>;
  if (cam.mode !== "passive") return null;

  return (
    <div>
      <p>{cam.trackedCount} in frame · {cam.totalCount} captured this session</p>
      <button type="button" onClick={cam.flushBatch}>Send now</button>
      <button type="button" onClick={cam.rearm}>Capture everyone again</button>
    </div>
  );
}
フィールド 意味
ready / error カメラとモデルの状態です。
trackedCount その瞬間にトラッキングされている顔の数です。
totalCount このセッションで1回以上選択された、異なる顔の数です。
livePreviews 表示中の顔ごとの、現時点で最良の切り出し画像です。FaceGrid が表示するサムネイルにあたります。
selectedFaces バッファされ、次のバッチ送信を待っている顔です。
flushBatch() バッファを今すぐ onBatchCapture に渡します。ページが非表示になるときに呼び出してください。
rearm() すべてのキャプチャウィンドウをやり直し、すでにキャプチャ済みの顔も再び探索の対象にします。
devices, currentDeviceId, setDeviceId, toggleCamera カメラの選択。localStorage に保存されます。

コンポーネント

いずれもコンテキスト経由でプロバイダの状態を読み取るため、props は不要で、プロバイダの内側であればどこにでも配置できます。

コンポーネント 表示内容
CameraViewport <video> と、トラッキング用のオーバーレイキャンバスです。
FaceGrid トラッキング中の顔ごとのライブサムネイル。品質が上がるたびに更新されます。
FaceCounter トラッキング中の数とキャプチャ済みの数です。
DevicePicker カメラの選択 UI です。
CameraToggle 前面・背面の切り替えです。
AlignmentRing, CaptureButton インタラクティブモード用のガイドリングとシャッターです。

標準コンポーネントでは実現できないレイアウトが必要な場合は、cam.videoRef と cam.overlayRef を自前の要素に渡し、残りは自分で描画してください。プロバイダは DOM の持ち主を問いません。

インタラクティブモード

プロバイダは同じで、振る舞いが変わります。対象は1人、写真は1枚で、リングの中で静止したときに撮影されます。

const conf = {
  mode: "interactive",
  alignmentCaptureDelayMs: 2_000,
  onCapture: async (faceImage, fullFrame) => {
    await register(faceImage, fullFrame);
  },
} as const;

<MeeeetUpCamProvider {...conf}>
  <CameraViewport />
  <AlignmentRing />
  <CaptureButton />
</MeeeetUpCamProvider>

上の表にあるキャプチャ条件はパッシブモードの設定です。インタラクティブモードのキャプチャは、トラッカーの選択パイプラインではなくガイドリングによって決まります。