OpenCVを使ってデバイスを読み込んだり、mvkファイルを読み込む
ゲームプレイ動画は何らかの方法で保存されている前提で進める
手元にあるのはmkv形式のファイルやから、そこから画像データを取り出すことを考える
ただこのとき、1フレームずつ全部画像データとして取り出すのは数が多すぎる上に、
処理するときに時間を無駄にしそうやから、ある程度間隔をあけて画像化することが出来たらいいなーって感じ
あとこれは個人的な思想の話で、基本的にはライブラリが充実してそうなPythonで実装しながら、
Goだとどんな実装になるかも調べていく感じ
とりあえず、mkv形式の動画ファイルから指定したフレーム数目か、指定した秒数後の画面の状態を画像として取り出す
mkv形式のファイルは、マトリョーシカというマルチメディアコンテナのフォーマットらしい
マルチメディアってことは、動画 + 音声が複数個ひとつのファイルにまとめられるってことだと思う
実際に音声トラックを複数持っているケースを確認している
そこまで真正面から取り組むかは別にして、データレイアウトが公開されている
動画から画像を取り出すのに必ずといっていいほど登場するのがopencv
機械学習の文脈にも絡んでるみたいで、Pythonも含んだいくつかの言語用のライブラリみたい
またGo言語ではOpenCVで実装されてるGoCVなるものがあるみたい
C++でOpenCVを使うのに比べて少しだけ遅いみたいやけど、ぱっとみ誤差レベルかな
Go
ってなわけで、GoCVを使ってmkv形式のファイルから任意のフレームの画像を取り出してみる
また、その過程で「こんな使い方もできるのかー」ってのもあればメモっていく
環境をどうしようか迷い中
windows上でやってもいいし、wslのubuntu上でもいい
と思ったけど、wslってホスト側にあるデバイスって認識できるんかな
今回はmkvファイルから取り出すつもりで触ってるけど、
ウェブカメラとか、キャプボからのデータを直接使えるならそれも試してみたいし
そのへんも調べていく
とりあえず下記
windows 11
wsl2
不便が出たらその都度解決するか、環境を変えるかは検討する
wslのubuntuにgocv経由で環境構築する
$ go get -u -d gocv.io/x/gocv
$ ~/go/pkg/mod/gocv.io/x/gocv@v0.40.0/
$ make install
そこそこ時間がかかる
$ go run ./cmd/version/main.go
gocv version: 0.40.0
opencv lib version: 4.11.0
とりあえずWebカメラにつなぐ
$ go run main.go
WARN:0@0.018 global cap_v4l.cpp:913 open VIDEOIO(V4L2:/dev/video0): can't open camera by index ERROR:0@0.018 global obsensor_uvc_stream_channel.cpp:158 getStreamChannelGroup Camera index out of range terminate called after throwing an instance of 'cv::Exception'
what(): OpenCV(4.11.0) /tmp/opencv/opencv-4.11.0/modules/highgui/src/window.cpp:973: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'imshow'
ってなわけで、直接windowsで動かすことを目指す
これをしようとすると、普段wsl側のディレクトリにプロジェクトを作ってるのができないのが悲しい
dockerを通す方法もあるっぽいけど、とりあえずwindowsでやろう
windows 11
wsl2
gocvを取得
$ go get -u -d gocv.io/x/gocv
go: -d flag is deprecated. -d=true is a no-op
go: added gocv.io/x/gocv v0.40.0
MinGW-W64をインストール
x86_64-14.2.0-release-win32-seh-msvcrt-rt_v12-rev1.7z をダウンロード
展開して、Cドライブ直下にでも置いておく
binにPATHを通す
$ gcc -v
gcc version 14.2.0 (x86_64-win32-seh-rev1, Built by MinGW-Builds project)
CMakeをインストール
Binary distributionsからInstallerかZipをダウンロードする、僕はZipを選択
任意のディレクトリに展開し、binにPATHを通す
$ cmake --version
cmake version 3.31.4
opencvをインストール
$ cd $GOPATH/pkg/mod/gocv.io/x/gocv\@v0.40.0/
$ cmd
git bashを使っていたのでcommand promptを起動
$ win_build_opencv.cmd
そこそこ時間がかかる
インストール確認
$ go run cmd/version/main.go
exit status 0xc0000135
PATHを通す
C:\opencv\build\bin にPATHを通す
$ opencv_version
4.11.0
opencv自体はちゃんと入ってる
$ go run cmd/version/main.go
gocv version: 0.40.0
opencv lib version: 4.11.0
このコードから、VideoCaptureDeviceの番号を変え、キャプボを指定して取得出来ていることを確認
https://scrapbox.io/files/6797cc1fc0e788bef1725988.png
opencv環境はできた
んでですよ、mkvファイルから指定したフレームを取得するのを試す
っていっても、VideoCaptureDeviceをOpenVideCaptureに置き換えて、
ファイルを相対パスなりで渡すだけで読み込むことは出来る
ただ速度がなんかあわなくて、上手く再生できない
どのくらいの時間ごとに画像を進めるかはFPSに依存する
FPSはgocvの機能でちゃんと取れて、1フレーム毎に何ミリ秒必要かを計算すれば、
その時間だけディレイして画像をめくっていくだけ
code:main.go
func main() {
video := "./testdata/1718887317_sample_1920x1080.mkv"
webcam, _ := gocv.OpenVideoCapture(video)
fps := webcam.Get(gocv.VideoCaptureFPS)
frames := webcam.Get(gocv.VideoCaptureFrameCount)
fmt.Printf("FPS: %v, Frames: %v, Seconds: %v\n", fps, frames, frames/fps)
window := gocv.NewWindow("Read mvk file")
img := gocv.NewMat()
for {
if ok := webcam.Read(&img); !ok {
fmt.Printf("Device closed: %v\n", video)
return
}
if img.Empty() {
continue
}
window.IMShow(img)
if window.WaitKey(int(1/fps*1000)) == 27 {
break
}
}
}
fps := webcam.Get(gocv.VideoCaptureFPS) でfpsを取得出来、
window.WaitKey(int(1/fps*1000)) が何ミリ秒待機して次の画像に行くかを表している
次に、指定したフレームを画像に出す
webcam.Set(gocv.VideoCapturePosFrames, float64(frame)) でframeを指定すると
次に webcam.Read(&img) したときに読み込まれるframeが、指定したframeになる
これを使えば任意のframeを読み込むことが出来る
そして画像に出すためのメソッドとして、 gocv.IMWrite がある
第一引数がファイルパス、第二引数が Mat で img 変数を渡す
が、ここでエラーが出た
Exception 0xc0000005 0x1 0x0 0x7ff977d2d85e
PC=0x7ff977d2d85e
signal arrived during external code execution
何か分からん
ただ、これをgit bashじゃなくて、cmdとかpowershellとかでなら実行できた
git bashの色々変えて試したけど、結局通らなかった
python側で似た現象が起きたらもっと調べることにして、Goはいったんおわり
Python
GoでWSLでできなかったので、こっちもwindowsのホスト側でやる
Goでやったのと同じく、デバイスの読み込み、mvkの読み込み、mvkのフレーム指定での読み込み
$ python --version
Python 3.13.1
$ pip3 --version
pip 24.3.1
$ python.exe -m pip install --upgrade pip
Successfully installed pip-25.0
$ pip3 --version
pip 25.0
プロジェクトを作る
$ mkdir opencv-getting-started
$ python -m venv myenv
$ myenv/Scripts/activate
$ pip3 install opencv-python
Installing collected packages: numpy, opencv-python
Successfully installed numpy-2.2.2 opencv-python-4.11.0.86
デバイスの値を読み込み
code:capture.py
import cv2 as cv
webcam = cv.VideoCapture(1)
while True:
ret, frame = webcam.read()
cv.imshow("Video", frame)
key = cv.waitKey(1)
if key == 27:
break
webcam.release()
cv.destroyAllWindows()
APIはgocvであらかた理解していたおかげで簡単にできた
mvkの読み込みと再生
code:mvk.py
import cv2 as cv
video = "./testdata/1718887317_sample_1920x1080.mkv"
webcam = cv.VideoCapture(video)
fps = webcam.get(cv.CAP_PROP_FPS)
frames = webcam.get(cv.CAP_PROP_FRAME_COUNT)
print(f"FPS: {fps}, Frames: {frames}, Seconds: {frames / fps}")
while True:
ret, frame = webcam.read()
cv.imshow("Video", frame)
key = cv.waitKey(int(1 / fps * 1000))
if key == 27:
break
webcam.release()
cv.destroyAllWindows()
gocvでやったときと同じく、fpsやframesを取得し、計算に利用することも出来ている
mvkファイルの特定のフレームを画像に出力
code:mvk_save_image.py
import cv2 as cv
video = "./testdata/1718887317_sample_1920x1080.mkv"
webcam = cv.VideoCapture(video)
fps = webcam.get(cv.CAP_PROP_FPS)
frames = webcam.get(cv.CAP_PROP_FRAME_COUNT)
print(f"FPS: {fps}, Frames: {frames}, Seconds: {frames / fps}")
for i in range(0, int(frames), 100):
webcam.set(cv.CAP_PROP_POS_FRAMES, i)
ret, frame = webcam.read()
if not ret:
break
filepath = f"./testdata/frame_{i:06d}.jpg"
cv.imwrite(filepath, frame)
webcam.release()
cv.destroyAllWindows()
python経由でのcv.imwriteはgit bashからでも実行できた
画像の取り出しをGoとPythonでやってみたけど、
読み込みのあたりを一回理解したらopencvのapiは分かりやすそう
更新履歴
元記事から切り出し