LoginSignup
5
4

More than 1 year has passed since last update.

USBカメラで動画撮影しよう!(on python3)

Posted at

USBカメラを使って,動画を撮影する必要に迫られて,作成したソースコードになります.
なぜ,プログラムでやらなければいけないのか?というと,いつも使っているLinuxにインストールした,cheeseではカメラ認識しないんですよ.
そんなこんなで,プログラム作成しました.

 プログラム動作環境

PCはDebian 11(test版)
ノートPCで内蔵カメラ有り.

あらかじめ,opencvはインストールしておきましょう.

aptitude search python3-opencv
p   python3-opencv   
p   python3-opencv-apps  

パッケージ名はpython3-opencv
aptitudeが入ってない方はapt-getで入れましょう.
参考までに,私のPCのバージョンを以下に示しておきます.
Python3のバージョン: 3.9.2
opencvのバージョン: 4.5.2

ソースコードの説明

なんだかゴチャゴチャしていてスミマセン.
1つのプログラムでやりたいことすべてできるようにしたので,この有様です.
簡単にソースコードに説明をしますと,まず,camera_checkモードで接続されているカメラを調べます.
sys.exit()にしているので,調べて終了です.
最後にカメラのインデックス番号を表示するので,表示された番号をCAMERA_INDEXという定数に代入してください.
※内蔵カメラがあるPCでは,内蔵カメラはだいたい0番になります.その後に接続された順番に2,4,6...と振られていきます.
なぜ飛び飛びかというのは,1つのカメラにつき,/dev/以下のvideo0,video1,video2,...が2つ生成されるからです.
すなわち,内蔵カメラはvideo0,video1
次に接続したカメラはvideo2,video3...というように増えていくからです.

各変数名の説明

変数名 具体的な内容
auto_setting カメラのFPSと解像度を自動で設定します.
mirror 正像,虚像を入れ替えます.
save_fig キャプチャしたframeを静止画として保存します.
save_video 動画として保存します.
imshow 画像を見ます.
calibration 画像の中央を拡大して表示します.
rotation 画像を回転させます.
check 接続されているカメラのインデックスを表示します.

calibrationする場合の抜き出す幅はtriming_rangeで決めています.
例のソースでは,真ん中から上下左右に50ピクセル分だけ抜き出しています.

rotationの数値はプラスだと半時計周りに回転,マイナスだと時計回りに回転します.
わかりにくいと思うので,東西南北を例として解説すると,
0°で北方向が画像の上部だったものが,90°とすると,西方向が画像の上部となります.
反対に-90°とすると,東方向が画像の上部となります.

おまけ

トリミングする最初のx,yピクセルの値を以下のように計算しています.
そこで,一度だけ沼ったので,メモ程度に.

triming_start_x = (width//2) - (triming_range//2)
triming_start_y = (hight//2) - (triming_range//2)

もしくは

triming_start_x = int((width/2) - (triming_range/2))
triming_start_y = int((hight/2) - (triming_range/2))

元々は,以下のように記載していました.

triming_start_x = (width/2) - (triming_range/2)
triming_start_y = (hight/2) - (triming_range/2)

何がいけないのかというと,/2で演算を行うと,演算結果は,floatになってしまいます.
それを//2にすることで,整数部分だけを演算結果として代入してくれるそうです.
つまり,演算結果がint型になるということです.
じゃあ,int()でかこえばいいんでしょ?
(多分同じ......)

プログラム例

camera.py
#!/usr/bin/python3

import cv2
import numpy as np
import platform
import time
import datetime as dt
import sys
import os

def check_camera_connection():
    """
    接続されているカメラをチェック
    """

    print('接続されているカメラの番号を調べています...')
    true_camera_is = []  # 空の配列を用意
    cam_number = []

    # カメラ番号を0~9まで変えて、COM_PORTに認識されているカメラを探す
    for camera_number in range(0, 10):
        try:
            cap = cv2.VideoCapture(camera_number)
            ret, frame = cap.read()
        except:
            ret = False
        if ret == True:
            true_camera_is.append(camera_number)
            print("カメラ番号->", camera_number, "接続済")

            cam_number.append(camera_number)
        else:
            print("カメラ番号->", camera_number, "未接続")
    print("接続されているカメラは", len(true_camera_is), "台です。")
    print("カメラのインデックスは", true_camera_is,"です。")
    sys.exit("カメラ番号を調べ終わりました。")
    return 0


camera_auto_setting = "n" # FPSと解像度を自動的に設定
camera_mirror = "n"       # 反転して映る際はyesに変更      
show_version = "y"        # pythonとopencvのバージョンを確認する場合はyes
save_fig = "n"            # 静止画保存
imshow = "y"              # キャプチャー画像を見る場合はyes
calibration = "n"         # センターキャリブレーション
rotation = "y"            # キャプチャ画像を回転
camera_check = "n"        # 接続されているカメラの番号を調べる

# 各定数定義
FPS = 40
size_X = 640
size_Y = 480

disp_X = 640
disp_Y = 480

CAMERA_INDEX=0


# 撮影する画像を回転させる角度
# ラジアンではなく,角度(°)ディグリー
ANGLE = 0

# 拡大比率
SCALE = 1.0
size = ( size_X , size_Y )
disp_size = (disp_X , disp_Y )

if camera_check == "y":
    check_camera_connection()

save_video=input('# 録画しますか(y/n default=y)')
if save_video=='':
    save_video = "y"
if save_video=='y':
    filename = 'out.mp4'
    while os.path.exists(filename):
        print("# %sはすでに存在しています." % filename)
        filename = input('## 新しい出力ファイル名:')
        filename = filename + '.mp4'
    print('%s に動画を書き出します.' % filename)
     # 動画保存用設定
    cap_now = dt.datetime.now()
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') # 動画保存時のfourcc設定(mp4用)
    video = cv2.VideoWriter(filename, fourcc, fps, (width, hight))  
    # 動画の仕様(ファイル名、fourcc, FPS, サイズ)




camera = cv2.VideoCapture(CAMERA_INDEX)#camera インスタンス生成
# 0:内蔵カメラ , -1:自動的にカメラが選択される
# 0以外の番号はcamera_check.pyで確認する.

# 動画ファイル保存用の設定
if camera_auto_setting == 'y':
    fps = int(camera.get(cv2.CAP_PROP_FPS))           # カメラのFPSを取得
    width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)) # カメラの横幅を取得
    hight = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))# カメラの縦幅を取得
else:
    fps = int(FPS)
    width = int(size_X)
    hight = int(size_Y)

# pythonとopencvのバージョンを確認
if show_version == "y":
    py_ver = str(platform.python_version_tuple())
    py_ver = py_ver.replace("'",'')
    py_ver = py_ver.replace(",",'.')
    py_ver = py_ver.replace("(",'')
    py_ver = py_ver.replace(")",'')
    py_ver = py_ver.replace(" ",'')
    print("Python3のバージョン: ",end="")
    print(py_ver)
    print("opencvのバージョン: ",end="")
    print(cv2.__version__)




# トリミングする際の幅,高さを計算
triming_range = 100
triming_start_x = (width//2) - (triming_range//2)
triming_start_y = (hight//2) - (triming_range//2)



print(" fps= %d " % fps , end="" )
print(" width= %d " % width , end="" )
print(" hight= %d" % hight )
size = ( width , hight )
now = time.time()
start = now
count = 0


while True:
    ret, frame = camera.read()
    if camera_mirror == "y":
        frame = frame[:,::-1]

    if save_fig == "y":
        cap_time = dt.datetime.now()
        cap_time = str(cap_time.strftime('%y%m%d_%H%M%S'))
        cap_time = cap_time.replace("'",'')
        picname = cap_time+".png"
        cv2.imwrite(picname, frame)

    if save_video == "y":
        video.write(frame)
    if rotation == "y":
         #回転させる処理
        center2 = tuple(np.array([frame.shape[1] * 0.5, frame.shape[0] * 0.5]))
        rotation_matrix = cv2.getRotationMatrix2D(center2, ANGLE, SCALE)
        frame = cv2.warpAffine(frame, rotation_matrix, size, flags=cv2.INTER_CUBIC)
    if calibration == "y":
         #キャプチャ画像のセンターを表示する処理
        frame = frame[triming_start_x:triming_start_x + triming_range , triming_start_y:triming_start_y + triming_range]
        frame = cv2.resize(frame, disp_size)
    if imshow == "y":
        cv2.imshow("camera", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    count = count + 1       
    now = time.time()
rate=count/(now-start)
speed=1.0/rate*1000
print("rate=%5.2f (Hz)" % rate)
print("speed=%5.2f (msec)" % speed)
camera.release()
cv2.destroyAllWindows()

実行結果

カメラチェックを行うと,以下のような結果になるかと思います.

接続されているカメラの番号を調べています...
カメラ番号-> 0 接続済
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video1): can't open camera by index
カメラ番号-> 1 未接続
カメラ番号-> 2 接続済
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video3): can't open camera by index
カメラ番号-> 3 未接続
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video4): can't open camera by index
カメラ番号-> 4 未接続
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video5): can't open camera by index
カメラ番号-> 5 未接続
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video6): can't open camera by index
カメラ番号-> 6 未接続
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video7): can't open camera by index
カメラ番号-> 7 未接続
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video8): can't open camera by index
カメラ番号-> 8 未接続
[ WARN:0] global /tmp/pip-req-build-qxrf2chl/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2:/dev/video9): can't open camera by index
カメラ番号-> 9 未接続
接続されているカメラは 2 台です。
カメラのインデックスは [0, 2] です。
カメラ番号を調べ終わりました。

5
4
0

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
5
4