OpenCVでのビデオの読み取りと書き込みは、画像の読み取りと書き込みに非常に似ています。ビデオは、一般にフレームと呼ばれる一連の画像に他なりません。したがって、ビデオ シーケンス内のすべてのフレームをループして、一度に 1 フレームずつ処理するだけです。この投稿では、ファイル、画像シーケンス、およびWeb カメラからビデオを読み取り、表示、および書き込む方法を示します。また、途中で発生した可能性のあるいくつかのエラーを調査し、それらを修正する方法を理解するのに役立ちます.
まず、ビデオ ファイルを読み取るためのコード例を見てみましょう。基本的に、ディスクからビデオを読み取って表示する機能が含まれています。進行するにつれて、この実装で使用される関数について詳しく説明します。
パイソン
import cv2
# ビデオ キャプチャ オブジェクトを作成します。この場合、ファイルからビデオを読み取ります
vid_capture = cv2.VideoCapture( 'person_test.mp4' )
if (vid_capture.isOpened() == False ):
print ( " video file" )
# fps とフレーム カウントを読み取る
else :
# フレーム レート情報を取得する
# 5 を CAP_PROP_FPS に置き換えることもできます。これらは列挙
型ですfps = vid_capture.get ( 5 )
print ( FPS' ) # フレーム数を取得
# 7 を CAP_PROP_FRAME_COUNT に置き換えることもできます。それらは列挙です
frame_count = vid_capture.get( 7 )
print ( 'Frame count : ' , frame_count)
while (vid_capture.isOpened()):
# vid_capture.read() メソッドはタプルを返します、最初の要素は bool
# で 2 番目の要素はフレーム
ret 、frame = vid_capture.read()
if ret == True :
cv2.imshow( 'Frame' , frame)
# 20 はミリ秒単位です。値を増やしてみてください。 50 そして観察する
key = cv2.waitKey( 20 )
if key ==ord ( 'q' ):
break
else :
break
# ビデオ キャプチャ オブジェクトを解放します
vid_capture.release()
cv2.destroyAllWindows()
これらは、このブログ投稿で説明するOpenCVビデオI/Oの主な機能です。
- cv2.VideoCapture –ビデオのストリーミングまたは表示を容易にするビデオ キャプチャ オブジェクトを作成します。
- cv2.VideoWriter –出力ビデオをディレクトリに保存します。
- さらに、フレームの高さ、幅、 fpsなどのビデオ メタデータを読み取るために必要な他の関数cv2.imshow() 、cv2.waitKey() 、およびget()メソッドについても説明しました。
ファイルからビデオを読み取る
以下のコードの次のブロックでは、VideoCapture()
このクラスを使用してVideoCaptureオブジェクトを作成し、これを使用してビデオ ファイルを読み取ります。このクラスを使用するための構文は次のとおりです。
VideoCapture(path, apiPreference)
最初のパラメーターは、ビデオ ファイルのファイル名/パスです。2 番目はオプションのパラメータで、API
設定を示します。このオプションのパラメーターに関連するいくつかのオプションについては、以下で詳しく説明します。詳細についてはapiPreference
、公式ドキュメント リンクVideoCaptureAPIsにアクセスしてください。
# ビデオ キャプチャ オブジェクトを作成します。この場合、ファイルからビデオを読み取ります
vid_capture = cv2.VideoCapture( 'person_test.mp4' )
ビデオ キャプチャ オブジェクトができたので、このisOpened()
メソッドを使用して、ビデオ ファイルが正常に開かれたことを確認できます。このisOpened()
メソッドは、ビデオ ストリームが有効かどうかを示すブール値を返します。そうしないと、エラー メッセージが表示されます。エラー メッセージは、多くのことを暗示している可能性があります。そのうちの 1 つは、ビデオ全体が破損しているか、一部のフレームが破損していることです。ビデオ ファイルが正常に開かれたと仮定すると、このget()
メソッドを使用して、ビデオ ストリームに関連付けられた重要なメタデータを取得できます。この方法は Web カメラでは機能しないことに注意してください。このメソッドは、ここにget()
記載されているオプション列挙リストから 1 つの引数を取ります。以下の例では、フレームレート( )とフレーム数( )に対応する数値5と7を指定しています。値または名前のいずれかを指定できます。CAP_PROP_FPS
CAP_PROP_FRAME_COUNT
パイソン
if (vid_capture.isOpened() == False ):
print ( "ビデオ ファイルを開く際のエラー" )
# fps とフレーム カウントを読み取る
else :
# フレーム レート情報を取得する
# 5 を CAP_PROP_FPS に置き換えることもできます。これらは列挙型です
fps = vid_capture.get( 5 )
print ( 'Frames per second : ' , fps , 'FPS' )
# フレーム カウントを取得します
# 7 を CAP_PROP_FRAME_COUNT に置き換えることもできます。これらは列挙です
frame_count = vid_capture.get( 7 )
print ( 'Frameカウント: ' ,frame_count)
ビデオ ファイルに関連付けられた必要なメタデータを取得したら、ファイルから各画像フレームを読み取る準備が整いました。これは、ループを作成し、vid_capture.read()メソッドを使用してビデオ ストリームから一度に 1 フレームずつ読み取ることによって行われます。
vid_capture.read ()メソッドは、最初の要素がブール値で、次の要素が実際のビデオ フレームであるタプルを返します。最初の要素がTrueの場合、ビデオ ストリームに読み取るフレームが含まれていることを示します。
読み取るフレームがある場合は、imshow()を使用して現在のフレームをウィンドウに表示できます。それ以外の場合は、ループを終了します。動画フレーム間で20ミリ秒間一時停止するために、 waitKey()関数も使用していることに注意してください。waitKey()関数を呼び出すと、キーボードでのユーザー入力を監視できます。この場合、たとえば、ユーザーが「 q 」キーを押すと、ループが終了します。
while (vid_capture.isOpened()):
# vid_capture.read() メソッドはタプルを返します。最初の要素は bool
# 2 番目の要素はフレーム
ret 、frame = vid_capture.read()
if ret == True :
cv2.imshow( 'Frame' , frame)
# 20 はミリ秒単位です。値を増やしてみて、たとえば 50 にして観察します
key = cv2.waitKey( 20 )
if key == ord ( 'q' ):
break
else :
break
ビデオ ストリームが完全に処理されるか、ユーザーが途中でループを終了したら、次のコードを使用して、ビデオ キャプチャ オブジェクト( vid_capture
)を解放し、ウィンドウを閉じます。
パイソン
# ビデオ キャプチャ オブジェクトを解放します
vid_capture.release()
cv2.destroyAllWindows()
ウェブカメラからビデオを読み取る
Web カメラからのビデオ ストリームの読み取りも、上記の例と非常によく似ています。どうすればいいの?これはすべて、 OpenCVのビデオ キャプチャ クラスの柔軟性のおかげです。OpenCV には、利便性のためにさまざまな入力パラメーターを受け入れるオーバーロードされた関数がいくつかあります。ビデオ ファイルまたは画像シーケンスのソースの場所を指定する代わりに、以下に示すようにビデオ キャプチャ デバイスのインデックスを指定するだけで済みます。
- システムに Web カメラが内蔵されている場合、カメラのデバイス インデックスは"
0
"になります。 - システムに複数のカメラが接続されている場合、追加のカメラごとに関連付けられたデバイス インデックスが増加します (例
1
:2
など)。
パイソン
vid_capture = cv2.VideoCapture( 0 , cv2.CAP_DSHOW)
flags について疑問に思うかもしれませんCAP_DSHOW
。これはオプションのパラメーターであるため、必須ではありません。Direct Display Via Video Input の略であるCAP_DSHOW
もう 1 つのビデオ キャプチャAPI設定です。
ビデオを書く
それでは、動画の書き方を見ていきましょう。ビデオの読み取りと同じように、任意のソース (ビデオ ファイル、画像シーケンス、または Web カメラ) からビデオを作成できます。ビデオ ファイルを書き込むには:
- メソッドを使用して、イメージ フレームの高さと幅を取得します
get()
。 - ビデオ ストリームをメモリに読み込むために、前述のソースのいずれかを使用して、(前のセクションで説明したように) ビデオ キャプチャ オブジェクトを初期化します。
- ビデオ ライター オブジェクトを作成します。
- ビデオ ライターオブジェクトを使用して、ビデオ ストリームをディスクに保存します。
実行中の例を続けて、get()
メソッドを使用してビデオ フレームの幅と高さを取得することから始めましょう。
パイソン
# get()メソッドでフレームサイズ情報を取得
frame_width = int (vid_capture.get( 3 ))
frame_height = int (vid_capture.get( 4 ))
frame_size = (frame_width , frame_height)
fps = 20
前述のように、クラスのget()
メソッドはVideoCapture()
次のことを行う必要があります。
- ビデオ フレームに関連付けられたさまざまなメタデータを取得できる列挙型リスト内の 1 つのパラメーター。
利用可能なメタデータは広範で、ここで見つけることができます。
- この場合、
3
(CAP_PROP_FRAME_WIDTH
)と4
(CAP_PROP_FRAME_HEIGHT
)を指定してフレームの幅と高さを取得します。ビデオ ファイルをディスクに書き込むときは、これらのサイズを後で使用します。
ビデオ ファイルを書き込むには、次のコードに示すように、まずこのクラスからビデオ ライターオブジェクトVideoWriter()
を作成する必要があります。
構文は次のとおりです。VideoWriter()
VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor])
このクラスはVideoWriter()
次のパラメータを取ります。
filename
:出力動画ファイルのパス名apiPreference
: APIバックエンド識別子fourcc
: 圧縮フレームのコーデックの4文字コード( fourcc )fps
:作成されたビデオ ストリームのフレーム レートframe_size
: ビデオ フレームのサイズisColor
: 0 以外の場合、エンコーダーはカラー フレームを想定してエンコードします。それ以外の場合は、グレースケール フレームに適用されます (このフラグは現在Windowsでのみサポートされています)。
次のコードは、クラスoutput
からVideoWriter()
ビデオ ライター オブジェクトを作成します。ビデオ ライター オブジェクトの 2 番目の引数として、4 文字のコーデックを取得するための特別な便利な関数が必要ですcv2
。
VideoWriter_fourcc('M', 'J', 'P', 'G')
で。_VideoWriter::fourcc('M', 'J', 'P', 'G')
C++で。
ビデオ コーデックは、ビデオ ストリームの圧縮方法を指定します。非圧縮ビデオを圧縮形式に、またはその逆に変換します。AVIまたはMP4形式を作成するには、次のFourcc仕様を使用します。
ビデオ:cv2.VideoWriter_fourcc('M','J','P','G')
MP4 :cv2.VideoWriter_fourcc(*'XVID')
次の 2 つの入力パラメーターは、フレーム レートとフレーム サイズ (幅、高さ) をFPSで指定します。
パイソン
# ビデオ ライター オブジェクトの初期化
output = cv2.VideoWriter( 'Resources/output_video_from_file.mp4' , cv2.VideoWriter_fourcc(* 'XVID' ) , 20 , frame_size)
ビデオ ライター オブジェクトを作成したので、それを使用して、次のコードに示すように、一度に 1 フレームずつビデオ ファイルをディスクに書き込みます。ここでは、MP4ビデオ ファイルを20フレーム/秒でディスクに書き込んでいます。前の例からループに単純化した方法に注目してください。
パイソン
while (vid_capture.isOpened()):
# vid_capture.read() メソッドはタプルを返します。最初の要素は bool
# で 2 番目の要素はフレーム
ret 、frame = vid_capture.read()
if ret == True :
output.write(フレーム)
最後に、以下のコードでは、ビデオ キャプチャ オブジェクトとビデオ ライターオブジェクトが解放されます。
パイソン
# ビデオ キャプチャ オブジェクトを解放します
vid_capture.release()
output.release()
ビデオを読んだり書いたりするときに遭遇する可能性のある間違い
ビデオ読書
フレームの読み取り時に、パスが間違っているか、ファイルが破損しているか、フレームが失われていると、エラーが発生することがあります。そのため、 loops でif
ステートメントを使用します。while
の場合、行に表示されますret == True
。このようにして、フレームが存在する場合にのみフレームを処理します。以下は、この場合に観測されたエラー ログの例です。これは完全なログではなく、重要なパラメーターのみです。
cap_gstreamer.cpp:890: error: (-2) GStreamer: unable to start pipeline in function
間違ったパスの場合:
VideoCapture()
このクラスを使用すると、間違ったビデオ パスを指定してもエラーや警告が表示されません。ビデオフレームで何かをしようとすると問題が発生します。これを行うには、例で行ったように、単純なifブロックを使用して、ビデオ ファイルを読み取ったかどうかを確認できます。次のメッセージが表示されます。
Error opening the video file
ビデオライティング
このステップでさまざまなエラーが発生する可能性があります。最も一般的なのは、間違ったフレーム サイズと間違ったAPI設定です。フレームサイズがビデオと似ていない場合、出力ディレクトリにビデオファイルを取得しても、空白になります。NumPy shape メソッドを使用してフレーム サイズを取得する場合、OpenCV はheight x width x channelsを返すため、出力を反転することを忘れないでください。API設定エラーがスローされる場合は、パラメータにフラグを渡す必要がある場合があります。これは、警告の生成を回避するために使用する Web カメラの例で確認できます。CAP_ANY
VideoCapture()
CAP_DHOW
エラー ログの例を次に示します。
CAP_DSHOW が失敗した場合:
[WARN:0]...cap_msmf.cpp(438) …. terminating async callback
フレームサイズが正しくない場合:
cv2.error: OpenCV(4.5.2) :-1: error: (-5:Bad argument) in function 'VideoWriter'
> Overload resolution failed:
> - Can't parse 'frameSize'. Sequence item with index 0 has a wrong type
> - VideoWriter() missing required argument 'frameSize' (pos 5)
> - VideoWriter() missing required argument 'params' (pos 5)
> - VideoWriter() missing required argument 'frameSize' (pos 5)
知識を広げる
要約する
このブログでは、ビデオ キャプチャ オブジェクトを使用して、3 つの異なるソースからのビデオ ストリームを読み取って表示する方法を学習しました。ビデオ キャプチャ オブジェクトを使用してビデオ ストリームから重要なメタデータを取得する方法も確認しました。また、ビデオ ライターオブジェクトを使用してビデオ ストリームをディスクに書き込む方法も示しました。ビデオ フレームのサイズを変更したり、図形やテキストで注釈を付けたりすることも役立つ場合があります。これを行うには、単一の画像フレームのみを変更する必要があります。
ビデオの読み書きの方法を理解し、OpenCV の使用に慣れたので、速度を上げてください。そして、学び続けてください。