OpenCVでWebカメラ(USBカメラ)から画像をキャプチャして保存する方法を紹介します。
必要なもの
●Python
●OpenCV(pip install opencv-pythonでOpenCVをインストールします)
●Webカメラ(USBカメラ)
サンプルプログラム
OpenCVでカメラから画像を取得し、画像ファイルに保存するサンプルプログラムを以下に示します。
import cv2
# カメラを開く
cap = cv2.VideoCapture(0)
# 画像をキャプチャする
ret, frame = cap.read()
# 画像を保存する
cv2.imwrite("image.jpg", frame)
# カメラを閉じる
cap.release()
これで、Webカメラから画像を取得し、 image.jpg というファイル名で画像が保存されます。
次に各コードの説明をします。
VideoCapture()
cap = cv2.VideoCapture(0)
の部分で、カメラを開いています。
関数の引数で渡している 0 は、最初に見つけたカメラを開く事を意味しています。
ほとんどの場合、0 で大丈夫だと思いますが、例えば、PC内臓のカメラのあるノートPCに、別途、USBカメラを刺して使う場合には、この番号を 1 や 2 に調整してみてください。
read()
ret, frame = cap.read()
この部分で、現在、撮影している画像データを取得します。
画像の取得し失敗すると ret は Falseになります。
失敗する原因としては、VideoCaptureの部分で指定する番号を間違えたとか、他のプログラムでカメラを使用していた場合などがあります。
frameが撮影した画像データとなります。
その他のサンプルを以下に示します。
画像をリアルタイムに表示するサンプル
import cv2
# カメラを開く
cap = cv2.VideoCapture(0)
while True:
# 画像をキャプチャする
ret, frame = cap.read()
# 画像を表示する
cv2.imshow("Image", frame)
# `q`キーを押すとループを終了する
if cv2.waitKey(1) == ord('q'):
break
# カメラを閉じる
cap.release()
# すべてのウィンドウを閉じる
cv2.destroyAllWindows()
このサンプルで q キーを押すまで画像を取得し、表示します。
キーを押すたびに連番でファイルに保存するサンプル
import cv2
# カメラを開く
cap = cv2.VideoCapture(0)
indexNo = 0
while True:
# 画像をキャプチャする
ret, frame = cap.read()
# 画像を表示する
cv2.imshow("Image", frame)
# `q`キーを押すとループを終了する
if cv2.waitKey(1) == ord('q'):
break
elif cv2.waitKey(1) == ord('c'):
cv2.imwrite(f"image{indexNo:05d}.jpg", frame)
indexNo = indexNo + 1
# カメラを閉じる
cap.release()
# すべてのウィンドウを閉じる
cv2.destroyAllWindows()
このサンプルでは、一つ前のサンプルに加えて、 c キーを入力するたびに、連番のjpegファイルに画像を保存します。
read()での画像の取得タイミングについて
ここからが、個人的に興味のあった部分。
画像を取得する際に、read()関数をwhile文で繰り返し呼んでいますが、カメラのフレームレートよりも速くread()関数が呼ばれている可能性が高いです。
このとき、read()関数は、呼ばれるたびに、即、画像データを取得しているのか?それとも、1フーレムの撮影が終わるのを待ってから、画像を取得しているのか?気になった。。
そこで、こんなプログラムを実行してみました。
import cv2
import time
# カメラを開く
cap = cv2.VideoCapture(0)
# 撮影開始時間
start = time.perf_counter()
for i in range(300):
# 画像をキャプチャする
ret, frame = cap.read()
# 画像300枚を撮影するのにかかった時間
print(time.perf_counter() - start)
# カメラを閉じる
cap.release()
# すべてのウィンドウを閉じる
cv2.destroyAllWindows()
このサンプルプログラムでは、30fpsのカメラを使って、300枚の画像を撮影しています。
実際に、プログラムを実行すると、300枚の画像を撮影するのにかっかった時間は、10.4秒という結果になりました。
30fpsのカメラで300枚撮影しているので、計算上は300枚撮影するのに10秒かかる事になり、ほぼ、理論通りの結果となりました。
このことから、read()関数が呼ばれたら、即、画像データを取得しているのではなく、カメラが1フレーム分、撮影が完了するのを待ってから画像を取得しているものと、推測されます。
少なくとも、read()関数を繰り返し呼んでも、同じ画像を重複して取得する事は無さそうです。