こちらは、下記の #GWアドベントカレンダー の 6日目の記事です。
●日数分だけ記事を書く!( @youtoy ) | GWアドベントカレンダー
https://gw-advent.9wick.com/calendars/2020/69
はじめに
この記事に書いた内容をやろうとしたきっかけは、以下のハンズオンに参加したことです。
もう少し説明を書くと、このときはラズパイで USBカメラを利用してコマンドでカメラ画像を取得していたのですが、カメラ画像取得を node.js で行ったり、Mac の内蔵のカメラでラズパイ上でやったのと似たようなことを行ったりできないかが気になったためです。
今日はこちらです。
— you (@youtoy) May 4, 2020
●(オンライン)ハンズオン - ねこ様見守りカメラを作ろう! - connpass
https://t.co/haAsiLIlXj#nekocierge
以下で、試した内容を備忘録も兼ねて書いていこうと思います。
Mac のターミナル上で 内蔵カメラの画像を取得
Mac のターミナル上でコマンドを使ってカメラ画像の取得ができないか調べました。
いろいろとググってみた結果、自分の今の Mac の環境で手軽に実現する方法は Homebrew を使って「imagesnap」を利用するのが良さそうでした。
●rharder/imagesnap: Capture Images from the Command Line
https://github.com/rharder/imagesnap
imagesnap の導入
まず、imagesnap を導入します。
自分は Homebrew をセッティング済みだったため、以下のコマンドを実行するだけで導入は完了しました。
$ brew install imagesnap
この後、実際にカメラ画像を取得してみます。
カメラ画像の取得(オプションなし)
公式の情報を見ると、シンプルな画像の取得と、出力する画像のファイル名を指定するコマンドは、それぞれ以下のとおりでした。
# シンプルなコマンドで画像取得
$ imagesnap
# 出力する画像のファイル名を指定しての画像取得
$ imagesnap output.jpg
自分の環境で上記を試すと、ターミナルを実行しているフォルダに画像が出力されたのですが、意図通りの画像を取得できませんでした。
その原因は仮想カメラの CamTwist を導入していたためです。
上記のコマンドで取得できた画像は以下のようなものになっており、内蔵カメラより優先して CamTwist が使われたようでした。
カメラ画像の取得(特定のカメラデバイスを指定)
公式のサイトを見直すと、複数のカメラがある場合にリストを得るコマンドと、カメラを指定するコマンドが書かれていました。
# カメラのリストを得る
$ imagesnap -l
# カメラを指定して画像を取得する
$ imagesnap -d 【カメラ名】
出力を確認すると「FaceTime HD Camera」という名称で指定すれば良さそうなことが分かったため、以下のコマンドを実行しました。
「FaceTime HD Camera」の名称が半角スペースを含むため、コマンドではダブルクォーテーションで囲んで実行しました。
$ imagesnap -d "FaceTime HD Camera" output.jpg
上記を実行するとコマンドを実行したフォルダ内に 1280x720 のサイズの画像が出力されましたが、画像の中身が黒い領域ばかりのおかしなデータになっていました。
カメラ画像の取得(カメラの初期化待ちを加える)
そして、imagesnap についての記事をググったところ、とある日本語の記事で以下の記載を見つけました。
しかし、カメラの初期化には多少の時間が必要なようで、デフォルトの状態では真っ黒なJPEGが生成されてしまう。これを避けるためには、「-w」オプションで撮影を遅延させればいい(単位は秒、小数点可)。試したところ、0.2秒(-w 0.2)あたりから薄暗く映るようになり、0.7秒(-w 0.7)も経てばまずまずの明るさで撮影できる。1秒(-w 1)を指定しておけば確実だろう。なお、一度確認すればデバイス名を表示する必要はないので、ついでに「-q」オプションで出力を止めてしまおう。
これに従ってコマンドの内容をさらに修正し、以下を実行することで無事に Mac の内蔵カメラで画像を取得できました。
$ imagesnap -d "FaceTime HD Camera" -w 1 output.jpg
以下が実際に取得した画像です(※ 少しクリッピング・リサイズをしています)。
様々なオプション
なおコマンドでヘルプを見ることもでき、他にも今回使っていないオプションが複数ありました。
$ imagesnap -h
#【途中省略】
-h This help message
-v Verbose mode
-l List available video devices
-t x.xx Take a picture every x.xx seconds
-q Quiet mode. Do not output any text
-w x.xx Warmup. Delay snapshot x.xx seconds after turning on camera
-d device Use named video device
Node.js でのカメラ画像取得
試すものの選定
Node.js のパッケージでカメラを利用するものを探してみて、見つかったものの中の 1つに以下がありました。
●node-webcam - npm
https://www.npmjs.com/package/node-webcam
そして、上記のページ内でちょうど以下のような記載もあったので、こちらを試してみることにしました。
Mac OSX
#Mac OSX relies on imagesnap
#Repo https://github.com/rharder/imagesnap
#Avaliable through brewbrew install imagesnap
なお、Linux の場合は「fswebcam」を利用し、Windows の場合は「CommandCam(exe は同梱)」を利用する形のようです。
導入してサンプルを動かしてみる
とりあえず、以下のコマンドを実行します。
$ npm install node-webcam
その後、適当な名前で JavaScript のファイルを作成し、公式ページにある以下のプログラムをそのまま実行してみました(なんだか、やたらと改行が多いサンプルだったので、それは適宜削りました)。
var NodeWebcam = require( "node-webcam" );
//Default options
var opts = {
//Picture related
width: 1280,
height: 720,
quality: 100,
//Delay in seconds to take shot
//if the platform supports miliseconds
//use a float (0.1)
//Currently only on windows
delay: 0,
//Save shots in memory
saveShots: true,
// [jpeg, png] support varies
// Webcam.OutputTypes
output: "jpeg",
//Which camera to use
//Use Webcam.list() for results
//false for default device
device: false,
// [location, buffer, base64]
// Webcam.CallbackReturnTypes
callbackReturn: "location",
//Logging
verbose: false
};
//Creates webcam instance
var Webcam = NodeWebcam.create( opts );
//Will automatically append location output type
Webcam.capture( "test_picture", function( err, data ) {} );
//Also available for quick use
NodeWebcam.capture( "test_picture", opts, function( err, data ) {
});
//Get list of cameras
Webcam.list( function( list ) {
//Use another device
var anotherCam = NodeWebcam.create( { device: list[ 0 ] } );
});
//Return type with base 64 image
var opts = {
callbackReturn: "base64"
};
NodeWebcam.capture( "test_picture", opts, function( err, data ) {
var image = "<img src='" + data + "'>";
});
そして、内容に手を加えずに上記を実行すると「test_picture.jpg
」というファイルは出力されたのですが、予想通り imagesnap を直接使った場合と同じで CamTwist から取得した画像が出力されていました。
オプションに変更を加える
カメラの指定は以下が関係しそうなので、GitHub上のソースコードで以下の指定に関係しそうな部分を見てみました。
//Which camera to use
//Use Webcam.list() for results
//false for default device
device: false,
そうすると、「/node-webcam/blob/master/src/webcams/ImageSnapWebcam.js
」の 72行目あたりに以下の部分がありました。
これを見ると、カメラのデバイス名をそのまま指定してやれば良さそうです。
そこで、サンプルのソースコード中の「device: false
」の部分を「device: "FaceTime HD Camera"
」に変更して、再度プログラムを実行しました。
その結果、無事に以下の画像を得ることができました(サンプルを動かした時と同じく、コマンドを実行したフォルダ内に「test_picture.jpg」というファイル名で出力されました)。
なお、imagesnap を直接ターミナル上で使った時はカメラの初期化待ちのディレイを 1秒いれる必要がありそうでしたが、JavaScript のプログラムを実行したときのオプションではデフォルトの「delay: 0
」で問題なく動作しました(プログラムを実行してから画像が出力されるまでは、ディレイが入ったような待ちが入った気もしますが)。
念のため、最終的に動作させてソースコードを掲載しておきます。
var NodeWebcam = require( "node-webcam" );
//Default options
var opts = {
//Picture related
width: 1280,
height: 720,
quality: 100,
//Delay in seconds to take shot
//if the platform supports miliseconds
//use a float (0.1)
//Currently only on windows
delay: 0,
//Save shots in memory
saveShots: true,
// [jpeg, png] support varies
// Webcam.OutputTypes
output: "jpeg",
//Which camera to use
//Use Webcam.list() for results
//false for default device
device: "FaceTime HD Camera",
// [location, buffer, base64]
// Webcam.CallbackReturnTypes
callbackReturn: "location",
//Logging
verbose: false
};
//Creates webcam instance
var Webcam = NodeWebcam.create( opts );
//Will automatically append location output type
Webcam.capture( "test_picture", function( err, data ) {} );
//Also available for quick use
NodeWebcam.capture( "test_picture", opts, function( err, data ) {
});
//Get list of cameras
Webcam.list( function( list ) {
//Use another device
var anotherCam = NodeWebcam.create( { device: list[ 0 ] } );
});
//Return type with base 64 image
var opts = {
callbackReturn: "base64"
};
NodeWebcam.capture( "test_picture", opts, function( err, data ) {
var image = "<img src='" + data + "'>";
});
おわりに
今回、Mac のターミナル上で Mac内蔵のカメラから画像を取得する方法と、それを利用した Node.js のパッケージを利用して画像を取得する方法とを試しました。
今後、さらに取得した画像を単に出力するだけでなく、取得した画像を処理に用いる JavaScript のプログラムを作ったりしてみたいと思います。