トップブログ

背景を差し替えると輪郭がずれる。原因は測り方を間違えていた

2026年8月31日

測り方を直したら、遅れの原因の内訳がひっくり返った
測り方を直したら、遅れの原因の内訳がひっくり返った

オンライン会議で背景を差し替えると、止まっているときはきれいなのに、動いた瞬間だけ輪郭が体から遅れてついてくる。手を上げると、手の形になるのが一拍遅い。

この記事は、その遅れがどこで生まれているのかを測った記録です。最初に測った結果は間違っていました。 測り方を直したら内訳が変わり、打てる手も変わりました。同じ症状を追いかけている場合、測り方から疑ったほうが早いかもしれません。


この記事の内容
  1. 起きていること
  2. 最初の測定は間違っていた
  3. 解像度を下げても効かない理由
  4. ついでに見つかった、静かなメモリリーク
  5. 分かったことと、分からないままのこと

起きていること

背景の切り抜きは、毎フレームこの順で進みます。

  1. カメラの映像を小さく縮めて入力を作る
  2. AIモデルに通して「どこが人か」のマスクを得る
  3. マスクをGPUから読み戻す
  4. マスクを使って人物と背景を合成する

このうち2の推論が重い。カメラは滑らかに動いているのに、マスクの更新だけが追いつかない。 だから止まっていれば合っていて、動くとずれます。

対策を考えるには、1〜4のどこに時間が乗っているかを知る必要があります。そこで各段階の時間を測りました。

最初の測定は間違っていた

計算の終了を確かめる点を挟んで測り直すと、遅いのはAIの計算そのもので、読み戻しは誤差の範囲だった
計算の終了を確かめる点を挟んで測り直すと、遅いのはAIの計算そのもので、読み戻しは誤差の範囲だった

素直に測ると、こういうコードになります。

t0 = 現在時刻
モデルを実行して待つ
t1 = 現在時刻        ← 推論時間 = t1 - t0 のつもり
マスクを読み戻す
t2 = 現在時刻        ← 転送時間 = t2 - t1 のつもり

この測り方で出た結果は「読み戻しが6割」でした。だとすれば、マスクの解像度を下げて転送量を減らせば速くなるはずです。

ところが、解像度を下げても体感がほとんど変わりませんでした。

原因は測り方でした。モデルの実行を待つ関数が、GPUの計算が終わる前に返っていたのです。返ってきた時点では計算がまだ走っているので、その続きの時間が次の区間(転送)に流れ込む。推論時間を転送時間として数えていたことになります。

直し方は、推論と転送の間に「GPUの計算が終わったことを確かめる点」を差し込むことです。出てきた結果のうち1要素だけを読み出す。1要素なら転送量はほぼゼロですが、読み出すためにはGPUの計算が終わっている必要があるので、そこで待ちが入ります。

モデルを実行して待つ
結果の1要素だけ読み出す   ← ここでGPUの完了を待つ
t1 = 現在時刻            ← これが本当の推論時間
マスク全体を読み戻す
t2 = 現在時刻            ← これが本当の転送時間

測り直すと、内訳はひっくり返りました。遅いのは推論そのもので、転送は誤差の範囲でした。読み戻しているデータは746KB。今どきの環境で、これが律速になるはずがなかったのです。

解像度を下げても効かない理由

内訳が分かると、効かない対策の理由も分かります。

マスクの解像度を下げると、減るのは転送量です。転送はもともと支配的ではないので、全体はほとんど動きません。支配的なのは推論なので、そこを減らさない限り効きません。

現在は入力を576ピクセル幅に縮め、モデル内部ではさらに縮めて約346ピクセルで処理しています。この設定で17fps。これ以上入力を小さくすると、今度は髪の毛や指の輪郭が保てなくなります。品質を保てる最小の解像度が、だいたいこのあたりでした。

推論を速くする道は、モデルそのものを軽くするか、実行方式を変えるかです。実行方式のほうは試しました。モデルを作り直してGPU向けの新しい実行環境に載せると、確かに速くはなりました。ただし出てくるマットが正しくありませんでした。 数値の精度を落とした形式にすると、人物と認識できずにマスクが薄くなる。最も濃いはずの部分で、255あるべき値が120程度まで落ちました。速いが使えない、という結果です。

ついでに見つかった、静かなメモリリーク

測定の過程で、別の問題も出てきました。背景を有効にしたまま置いておくと、メモリが1.6GBまで増え続けます。

使っているモデルには条件分岐が含まれていて、そのぶん実行のたびに中間データが残ります。通常の自動解放の仕組みは、非同期の実行を管理の外に置くため、これらを回収しません。毎フレーム少しずつ取り残されて積み上がります。

対策は、実行のたびに範囲を区切り、次のフレームに渡すものだけを明示的に残して、残りをまとめて捨てることです。渡すべきものは、モデルが持っている「前のフレームの状態」だけでした。それ以外はその場で不要になります。

長い会議で使う機能ほど、こういう積み上がりが効いてきます。10分のテストでは出ません。

分かったことと、分からないままのこと

測り直して分かったのはここまでです。

「高品質のまま低遅延」には届いていません。 現状は、追従が速い代わりに輪郭が粗いモードと、輪郭がきれいな代わりに追従が遅いモードの2つを持ち、用途で選ぶ形にしています。これを1つにまとめるには、モデル自体を変える必要があります。そこはまだ手をつけていません。

ただ、どこを攻めれば効いてどこを攻めても効かないかは、はっきりしました。 測り方を直すまでは、効かないところを削り続けていたわけで、そのぶんの時間が丸ごと無駄になっていました。


Meetnect はブラウザだけで動くオンライン商談ツールで、背景の切り抜きもブラウザの中で処理しています。映像が外に出ないぶん、こうした制約と正面から付き合うことになります。この記事の数字は、すべて実際に測ってコードに記録したものです。

商談しながら、議事録ができています

Meetnect はブラウザだけで動くオンライン商談ツールです。相手は登録不要、URLを開くだけ。通話中に文字起こしが進み、終わった時点で「誰が何を言ったか」まで残った議事録ができています。7日間、カード登録なしで試せます。