スマートフォンのカメラで心拍を測ろうとしたとき、最初に驚いたのはアルゴリズムではありません。指でレンズとフラッシュを覆うと画面は赤くぼやけますが、数百フレームを並べると小さなリズムが見えてきます。

MonoPump はそのリズムを使います。写真から心臓を認識するのではなく、カメラを光学センサーとして使い、赤チャンネルの変化から BPM を推定します。
まず現象をつかむ
フラッシュの光が指先に入り、心拍ごとに血液量が変わると、光の吸収と散乱もわずかに変わります。1 枚のフレームには意味がありません。連続したフレームが波形になります。
This diagram could not be rendered. Its source is still available below.
View diagram source
flowchart LR
A[フラッシュが指先を照らす] --> B[血液量が拍ごとに変わる]
B --> C[赤チャンネルが少し揺れる]
C --> D[時系列信号を作る]
D --> E[ピークを検出して BPM を推定]
最初は高度な信号処理が難所だと思っていました。実際には、その前にこのフレームを信号として使ってよいのかを見分ける方が先でした。指が浮く、光が漏れる、押し方が変わる、露出が動き続ける。こうした要素は脈ではなくノイズを作ります。
カメラを安定させてから計算する
MonoPump の取得経路は素直です。背面広角カメラ、BGRA フレーム、30 fps、トーチ点灯、そして光が安定した後の露出ロック。自動露出が動き続けると、見たい明るさの揺れをカメラ自身が打ち消してしまいます。
This diagram could not be rendered. Its source is still available below.
View diagram source
sequenceDiagram
participant UI as 測定画面
participant C as CameraManager
participant F as フレーム解析
UI->>C: カメラフレームを要求
C->>C: 30 fps BGRA 出力を設定
C->>C: トーチを点灯して露出を固定
C-->>F: CMSampleBuffer ストリーム
F-->>UI: 覆い状態、波形、信頼度
フレームそのものは心拍サンプルではありません。MonoPump は中央領域をサンプリングし、平均 RGB、明るさ、赤の比率、赤の優位性を計算します。
redRatio = avgRed / max((avgGreen + avgBlue) / 2, 1)
redDominance = avgRed / max(avgGreen, avgBlue)
redIntensity = redRatio * (avgRed / 255.0)
大事なのは二つの問いを分けることです。このフレームは指で覆われているように見えるか。そのうえで、波形に入れる値は何か。MonoPump は医療診断ではなく、品質と信頼度を添えた記述的な推定として扱います。