ブログ記事

ノートPCで自撮り (PCのカメラをWebカメラとして画像ファイルを保存)

2022/06/24

自撮りをスマホで簡単にできるのにわざわざノートPCを使う人はいないと思いますが、AIの発達によりクラウド上で様々な顔識別のAPIが公開されている昨今世界最高精度とかにこだわらないで実用レベルでいいのであれば比較的お手軽に顔認証システムが構築できるようになっています。
大規模施設であれば専用カメラ端末から画像を送ればいいですが、固定した場所でないとか専用カメラ端末なんてコストに見合わないという場合PCなどの端末付属のカメラをWebブラウザからWebカメラとして利用して画像を送ることになります。

前提としてもちろんWeb ブラウザを動かすためのWeb サーバーが必要です。
今回はWebブラウザでアクセスしてWebサーバー上でJavaScriptを利用して画像をサーバー側に保存します。

画面表示用HTMLを作る

とりあえずページの基本HTMLはシンプルに以下の通りとします。
今のところただボタンがあるだけの画面です。
video タグはカメラ動画表示スペースで canvas タグはJavaScriptで画像ファイルを生成するために利用するスペースです。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>自撮り</title>
<script>
</script>
</head>
<body>
    <h1>自撮り</h1>
    <video id="video"></video>
    <canvas hidden id="picture" width="480" height="640"></canvas>
    <form>
        <button type="button" id="take">シャッター</button>
    </form>
</body>
</html>


PCのカメラをWebカメラとして利用する

script タグの中に処理を入れていきます。
まずブラウザで画面を開いたらすぐにカメラ動画を表示してほしいので以下のように追加します。

window.onload = () => {
    const v = video
    const c = picture
    
    const constraints = {
        audio: false,
        video: {
        width: 480,
        height: 640,
        facingMode: "user"
        }
    };
      
    navigator.mediaDevices.getUserMedia(constraints)
    .then( (stream) => {
        v.srcObject = stream;
        v.onloadedmetadata = (e) => {
        v.play();
        };
    })
    .catch( (err) => {
        console.log(err.name + ":" + err.message);
    });
};

constraintsはWebカメラの設定をいれてます。
audioは音声設定で今回いらないのでOFF、videoのwidth、heightは画像ファイルが歪まないようにcanvasと同じ大きさにしてます。facingModeはuserを指定して正面カメラを使います、使う人いないと思いますが背面カメラが使いたい人はbackを指定してください。navigator.mediaDevices.getUserMedia(constraints)はこういう書き方なので基本このままコピペで。

カメラ動画から画像を切り取る

次にシャッターボタンを押した瞬間のカメラ動画を画像として切りとります。

    document.querySelector("#take").addEventListener("click", () => {

        const ctx = c.getContext("2d");
        v.pause();
        setTimeout( () => {
            v.play();
        }, 1000);

        ctx.drawImage(v, 0, 0, c.width, c.height);

v.pause()とsetTimeout()は撮影したというのをわかりやすくするため撮影した瞬間動画を止めているだけです。ctx.drawImage(v, 0, 0, c.width, c.height) でcanvas オブジェクトに画像を書き込んでます。

次に書き込まれた画像をファイルとして保存します。

        c.toBlob(function(blob) {
            var img = document.createElement("img");
            url = URL.createObjectURL(blob);
            img.onload = function() {
                URL.revokeObjectURL(url);
            }

            const fd = new FormData();
            fd.append("picture", blob)
    
            const param = {
                method: "POST",
                body: fd
            }
        
            fetch("/test.php", param)
            .then((res)=>{
                if (res) {
                    console.log(res)
                }
            })
            .then(result => {
                if (result) {
                    console.log(result)
                }
            })
            .catch((error)=>{
                console.log('Error:', error)
            });    
        }, "image/png", 0.95);

document.createElement("img")で画像オブジェクトを作りURL.createObjectURL(blob)で画像のダウンロードURLを作って画像オブジェクトにURLから読み込んでます。
URL.revokeObjectURL(url)はダウンロードURLを一応削除します。

セキュリティ的に危ないのでJavaScriptでサーバー側のファイルを操作する機能あまりないので今回はただ確認するためだけですのでPOSTされた画像を保存するだけのtest.phpも同じところに置きます。

test.php

<?php
$pic = file_get_contents($_FILES['picture']['tmp_name']);
file_put_contents('/tmp/test.png', $pic);
?>

最後にWebブラウザでtest.htmlにアクセスしてシャッターボタンを押すと"…/tmp/test/png"に画像が保存されていると思います。やったね。

全体

<meta charset="utf-8">
<title>自撮り</title>
<script>
window.onload = () => {
    const v = video
    const c = picture
    
    const constraints = {
        audio: false,
        video: {
        width: 480,
        height: 640,
        facingMode: "user"
        }
    };
      
    navigator.mediaDevices.getUserMedia(constraints)
    .then( (stream) => {
        v.srcObject = stream;
        v.onloadedmetadata = (e) => {
        v.play();
        };
    })
    .catch( (err) => {
        console.log(err.name + ":" + err.message);
    });

    document.querySelector("#take").addEventListener("click", () => {

        const ctx = c.getContext("2d");
        v.pause();
        setTimeout( () => {
            v.play();
        }, 1000);

        ctx.drawImage(v, 0, 0, c.width, c.height);
        c.toBlob(function(blob) {
            var img = document.createElement("img");
            url = URL.createObjectURL(blob);
            img.onload = function() {
                URL.revokeObjectURL(url);
            }

            const fd = new FormData();
            fd.append("picture", blob)
    
            const param = {
                method: "POST",
                body: fd
            }
        
            fetch("/test.php", param)
            .then((res)=>{
                if (res) {
                    console.log(res)
                }
            })
            .then(result => {
                if (result) {
                    console.log(result)
                }
            })
            .catch((error)=>{
                console.log('Error:', error)
            });    
        }, "image/png", 0.95);
    });
};
</script>
</head>
<body>
    <h1>自撮り</h1>
    <video id="video"></video>
    <canvas hidden id="picture" width="480" height="640"></canvas>
    <form>
        <button type="button" id="take">シャッター</button>
    </form>
</body>
</html>

最新記事

カテゴリ

アーカイブ

%d人のブロガーが「いいね」をつけました。