3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

なでしこさんの新機能「カメラ」で簡単な動体検知を試すよ!

3
Last updated at Posted at 2025-12-09

カメラとビデオ部品

 なでしこさん、今年も色々とバージョンアップしていますが、中でも遊んで面白そうな機能と言えば、カメラでしょう📷️

 PCやスマホのカメラをなでしこさんで利用出来るようになりました!
 内部的にはgetUserMedia()を利用しています。

 マニュアルに簡単なサンプルがあり、実際にカメラの映像を画面上に表示したり、静止画として保存したりも出来ます。

👉️ビデオ作成のサンプル

 これを利用して今回は簡単な動体検知を試してみたいと思います。

カメラを起動

# まずカメラを表示するビデオ要素を作成する。
ビデオビデオ作成

# 作成したビデオ要素にカメラを起動。
ビデオカメラ起動

 たったこれだけで、画面上にカメラの映像が表示されますよ✨️

カメラボタン「カメラ終了」ボタン作成
カメラボタンクリックした時にはビデオカメラ終了。。。 

 カメラを終了させるボタンも用意しておきます。
 正しくカメラ終了させずに終わらせますと、カメラが起動しっぱなしになってしまう場合があるのでご注意です。(Windowsのタブブラウザならそのタブを閉じれば大丈夫だと思うんですが、スマホで試した時、タブを閉じてもブラウザを終了させても、なぜかカメラ使用中のマークが消えなくて焦りました💧️)

とりあえず動体検知

 動体検知の手法にはいろいろあると思いますが、今回はタイマーを用意して、一つ前の映像と今の映像を比較して、差異があったら動いた! ということにします。

フレーム間差分法?

 連続した二つのフレームを比較してピクセルの変化を動きとして検出する方法、ということなので、フレームレートごとのタイマーを用意すればよいということになります。
 フレームレートとは、1秒間の動画に何枚の画像が使われているかなので、(1/フレームレート)秒のタイマーでいいんでしょうかねー?

フレームレート取得

 カメラ設定取得したカメラ設定オブジェクトの中にframeRateがあります。

カメラ設定ビデオカメラ設定取得
フレームレートカメラ設定.frameRate

 ウチではPCでもスマホでも30fpsのようでした。
 フレームレートはカメラ起動時にカメラオプションに指定することもできます。

カメラオプション.video{ frameRate10 }

 これでふつーに通ったけどMDNを見るとなんかむじゅかしいコト書いてあって、こんな感じ?

カメラオプション.video{ frameRate{ ideal10max15 } }

 設定したい値をidealにして、minmaxで範囲を指定するってこと?

ビデオの幅と高さ

 フレームレートを取得した「カメラ設定」から取得してもいいんですが、カメラ起動時にビデオに反映されていますから、ビデオから直接取るとオブジェクトプロパティ構文の和属性が使えるのでオススメです✨️

ビデオ幅ビデオ.幅属性
ビデオ高さビデオ.高さ属性

 ビデオもキャンバス同様、属性値のwidthとheightなので、オブジェクトプロパティ構文を使う場合は「幅」「高さ」ではなく「幅属性」「高さ属性」ですのでご注意です。

動体検知の設定項目

 後には値指定バーで変更したりできるようにしたいため、わかりやすいよう1~100って感じで設定できるっぽくしています。

精度30精度精度/100
感度80感度255/100*(100-感度)+1切り捨て
閾値20閾値ビデオ幅*ビデオ高さ*精度^2/1000*閾値切り捨て
  • 精度
     ミリ秒単位で映像の全てのピクセルを走査すると大変なので、縮小した画像を使って比較します。
     ビデオのサイズを100として、比較用画像を何パーセントに縮小するか、です。
  • 感度
     画面のちらつきやノイズまで拾ってしまわないよう、色差がどの程度あったら動いたとするか。
  • 閾値
     最終的に動いたピクセルがどの程度あったら動いたとするか。

キャンバス作成

 フレームごと交互に描画し前後を比較する2枚のキャンバスと、動いたフレームを表示するキャンバスを作ります。

# 動いたフレームを表示するキャンバス
撮影用[ビデオ幅,ビデオ高さ]キャンバス作成

# フレームごと交互に描画し前後を比較する2枚のキャンバス
比較用空配列
:
    
比較用[回数-1][ビデオ幅*精度,ビデオ高さ*精度]キャンバス作成
    比較用[回数-1]「visibility」「hidden」DOMスタイル設定# 可視はオフ

 比較用のキャンバスは見えないように可視をオフにしておきたいですが、配列に入れたキャンバスにオブジェクトプロパティ構文が使えなくてツライ💧️
 サンプルなので、あえて見えるようにしておくとゆうことも考えられますが🤣️

音を鳴らす

「https://n3s.nadesi.com/image.php?f=213.mp3」オーディオ開く

 動いたら音でお知らせされるようにしたいと思います。
 オーディオ開いてからオーディオ再生です。

 音を鳴らす命令は他にもありますが、スマホなどでも使うかもしれないことを考えた場合にはオーディオ命令が間違いないですね。

メイン

 あとはもう画像処理ですね。

C-1比較データ空配列
(1/フレームレート)秒タイマー開始した時にはタイマーID
    C1増やす
    比較データ[C%2]空配列
    比較用[C%2]描画開始
    ビデオ[0,0,ビデオ幅,ビデオ高さ][0,0,ビデオ幅*精度,ビデオ高さ*精度]画像部分描画
    色データ[0,0,ビデオ幅*精度,ビデオ高さ*精度]色データ取得
    I0
    N0から色データ要素数-1まで4ずつ増やし繰り返す
        色平均(色データ[N] + 色データ[N+1] + 色データ[N+2])/3
        比較データ[C%2]色平均配列追加
        もしC>0ならば:     #初回は前データがないので比較しない
            もし(((比較データ[0][N/4])-(比較データ[1][N/4]))絶対値)感度ならばI1増やす# ピクセルが動いたと判定
    ここまで
    もしC>10ならば:     # 起動時のちらつき回避で少し待ってみる
        もしI>閾値ならば:    # 最終的に動いたと判定
            オーディオ再生
            撮影用描画開始
            [0,0]ビデオ画像描画
ここまで

 比較用のキャンバスに、ビデオから縮小して画像部分描画をして、それの色データを取得します。
 色データの取得については一日目の電光掲示板でもやりました。

 四つ飛びにアクセスしてってRGBの値の平均を取ります。この平均値をRGBそれぞれに戻してやればグレースケールになるんですが、今回は比較データ配列に保存していって一個前の時のデータと比較。
 感度や閾値は、もしもしで反映させて最終的に動いたかどうか判定しています。

 これで大体動くようにはなりました。
 カメラの前で大きく手を振ったりすると、ピッピ鳴ります。

 でも、イマイチよく分かりませんので、動いたピクセルをモニターしてみます。

モニター

 とりあえず簡単に今まで使っていなかった標準キャンバスへモニターすることにします。
 モニターなので、ビデオの半分のサイズとしておきます。

モニター描画中キャンバス
モニター.幅属性ビデオ幅/2
モニター.高さ属性ビデオ高さ/2

 それから比較用のキャンバスと同じサイズで動いたピクセルを描き出す処理用キャンバスを作成します。

処理用[ビデオ幅*精度,ビデオ高さ*精度]キャンバス作成
処理用.可視オフ

 1フレーム毎にモニターにはビデオの映像をコピーし、処理用キャンバスをクリア。
 ピクセルが動いたと判定された場合には、処理用キャンバスの同じ場所に赤い点を描画。
 そして、比較用キャンバスの走査が終わったら、処理用をモニターに拡大描画。

モニター描画開始
ビデオ[0,0,ビデオ幅,ビデオ高さ][0,0,ビデオ幅/2,ビデオ高さ/2]画像部分描画
処理用描画開始全描画クリア赤色塗り色設定線色設定
I0
N0から色データ要素数-1まで4ずつ増やし繰り返す
    色平均(色データ[N] + 色データ[N+1] + 色データ[N+2])/3
    比較データ[C%2]色平均配列追加
    もしC>0ならば:     #初回は前データがないので比較しない
        もし(((比較データ[0][N/4])-(比較データ[1][N/4]))絶対値)感度ならば:# ピクセルが動いたと判定
            I1増やす
            # 処理用に点を打つ
            xN/4%(処理用.幅属性)
            yN/4/(処理用.幅属性)切り捨て
            [x,y,1,1]四角描画
ここまで
もしC>10ならば:     # 起動時のちらつき回避で少し待ってみる
    モニター描画開始
    [0,0,ビデオ幅/2,ビデオ高さ/2]処理用[0,0,ビデオ幅*精度,ビデオ高さ*精度]画像部分描画

 できました!

ねこ.gif

 バッチリ動いたところがモニターできるようになりました。
 やっぱり目に見えて動いているところが分かると面白いですね。

設定変更できるようにして完成(?)

#----- 設定 ---------------
# 動体検知の設定項目
精度30
感度80
閾値20

# 開閉できる設定画面
母艦DOM親要素
設定画面「DETAILS」DOM部品作成
それ.openオン
それDOM親要素設定
「SUMMARY」DOM部品作成
それ「設定」テキスト設定
母艦DOM親要素設定

# カメラ終了のボタン
改行作成
カメラボタン「カメラ終了」ボタン作成
改行作成

#----- カメラを起動 ---------------
# まずカメラを表示するビデオ要素を作成する。
ビデオビデオ作成

# 作成したビデオ要素にカメラを起動。
ビデオカメラ起動

# カメラ終了ボタンのイベント
カメラボタンクリックした時にはビデオカメラ終了。。。

#----- フレームレートごとに画像比較 ---------------
# フレームレート取得
カメラ設定ビデオカメラ設定取得
フレームレートカメラ設定.frameRate

# ビデオの幅と高さ取得
ビデオ幅ビデオ.幅属性
ビデオ高さビデオ.高さ属性

# 標準キャンバスに動いた点をモニターする
モニター描画中キャンバス
モニター.幅属性ビデオ幅/2
モニター.高さ属性ビデオ高さ/2

# 動いたフレームを表示するキャンバス
撮影用[ビデオ幅,ビデオ高さ]キャンバス作成

# フレームごと交互に描画し前後を比較する2枚のキャンバス
比較用空配列
:
    
比較用[回数-1][1,1]キャンバス作成
    比較用[回数-1]「visibility」「hidden」DOMスタイル設定

# 比較した情報を描き出すキャンバス
処理用[1,1]キャンバス作成
処理用.可視オフ

# 音を鳴らす
「https://n3s.nadesi.com/image.php?f=213.mp3」オーディオ開く

#--- メイン ---
C-1比較データ空配列
(1/フレームレート)秒タイマー開始した時にはタイマーID
    C1増やす
    比較データ[C%2]空配列
    比較用[C%2]描画開始
    ビデオ[0,0,ビデオ幅,ビデオ高さ][0,0,ビデオ幅*精度,ビデオ高さ*精度]画像部分描画
    色データ[0,0,ビデオ幅*精度,ビデオ高さ*精度]色データ取得
    モニター描画開始
    ビデオ[0,0,ビデオ幅,ビデオ高さ][0,0,ビデオ幅/2,ビデオ高さ/2]画像部分描画
    処理用描画開始全描画クリア赤色塗り色設定線色設定
    I0
    N0から色データ要素数-1まで4ずつ増やし繰り返す
        色平均(色データ[N] + 色データ[N+1] + 色データ[N+2])/3
        比較データ[C%2]色平均配列追加
        もしC>0ならば:     #初回は前データがないので比較しない
            もし(((比較データ[0][N/4])-(比較データ[1][N/4]))絶対値)感度ならば:# ピクセルが動いたと判定
                I1増やす
                # 処理用に点を打つ
                xN/4%(処理用.幅属性)
                yN/4/(処理用.幅属性)切り捨て
                [x,y,1,1]四角描画
    ここまで
    もしC>10ならば:     # 起動時のちらつき回避で少し待ってみる
        モニター描画開始
        [0,0,ビデオ幅/2,ビデオ高さ/2]処理用[0,0,ビデオ幅*精度,ビデオ高さ*精度]画像部分描画
        もしI>閾値ならば:
            
オーディオ再生
            撮影用描画開始
            [0,0]ビデオ画像描画
ここまで

#----- 設定変更用のUIを設定画面に追加する ---------------
改行作成
設定画面DOM親部品設定
設定項目{精度:精度,感度:感度,閾値:閾値}
設定タイトル空辞書設定バー空辞書設定確認空辞書
設定項目反復:
    
設定タイトル[対象キー]対象キー「:」ラベル作成
    設定バー[対象キー][0,100,対象]値指定バー作成
    設定確認[対象キー]対象エディタ作成
    改行作成
    設定バー[対象キー]「幅」「300px」DOMスタイル設定
    設定バー[対象キー]対象キーポケット設定
    設定確認[対象キー]「幅」「50px」DOMスタイル設定
    設定確認[対象キー]「行揃え」「右」DOMスタイル設定
    設定確認[対象キー]「無効化」オンDOM属性設定
    設定バー[対象キー]変更した時には:
        
No対象ポケット取得
        対象テキスト取得
        設定確認[No]それテキスト設定
        設定反映

設定反映
●設定反映
    精度(設定バー.精度テキスト取得)/100
    感度255/100*(100-(設定バー.感度テキスト取得))+1切り捨て
    閾値ビデオ幅*ビデオ高さ*精度^2/1000*(設定バー.閾値テキスト取得)切り捨て
    :
        
比較用[回数-1]「幅」ビデオ幅*精度DOM属性設定
        比較用[回数-1]「高さ」ビデオ高さ*精度DOM属性設定
    処理用「幅」ビデオ幅*精度DOM属性設定
    処理用「高さ」ビデオ高さ*精度DOM属性設定
    C=-1
ここまで
#-----------------------------------------------
●(xywhの|xywhを)色データ取得
 (描画中コンテキスト「getImageData」xywhJSメソッド実行).data戻す
ここまで
#-----------------------------------------------

👇️動作確認

反省または課題あるいは目標

 撮影用の画面があまり用をなしてないですね。
 モニター画面と並べて、感度と閾値の可視化としたほうが、サンプルとしては良かったかも?

 そして、知ってたけどフレーム間の差分だと、サッと速く動いたものは検知しやすいけど、ゆ~っくり動いた場合は、よほど閾値を低く設定しないと検知しない。
 抜き足差し足忍び足の泥棒さんが検知できなかったら防犯カメラの意味ないですよね~💧️
 設置式の防犯カメラで背景が変わらないのであれば、背景との差分のほうがいいのかもしれないですが~、むしろ今どきはAIが人間だ車だと判定して検知してくれちゃったりするようです!

 でもですね、ウチの安物のペットカメラも明らかにこのフレーム間差分法のようで、野良ちゃんを保護するためにお外のえさ場に向けておいたら、カラスやスズメはめっちゃ検知するけど、警戒しながらこしょーっと近づいてくる野良ちゃんを検知せず、食べ終わってサッと逃げてくところでやっと検知するという無能っぷり😹️(普段は動体検知オフで家のねこどもを出先から愛でているだけなので問題ないです)
 野良にゃんが来たのをお知らせしてくれるだけで良いので、次の機会? までに家のカメラよりもうちょっとマシにニャン検知できるようなのを考えたい。

3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?