回答編集履歴
4
add ref
answer
CHANGED
@@ -35,6 +35,13 @@
|
|
35
35
|
|
36
36
|
`cv::VideoCapture::get`の値は信用できないという話もある。例えばdecoderが`IntelMFX`, `GPhoto2`の場合、内部実装はガバガバで信用できない。
|
37
37
|
|
38
|
+
ref:
|
39
|
+
- [/modules/videoio/src/cap.cpp](https://github.com/opencv/opencv/blob/b500ae2d21e858bebd68b34b6cfef352979c269a/modules/videoio/src/cap.cpp)
|
40
|
+
- [/modules/videoio/src/cap_mjpeg_decoder.cpp](https://github.com/opencv/opencv/blob/638a01a014cb1972a6ffa63755474ce89e3db9d8/modules/videoio/src/cap_mjpeg_decoder.cpp)
|
41
|
+
- [/modules/videoio/src/cap_gphoto2.cpp](https://github.com/opencv/opencv/blob/05b15943d6a42c99e5f921b7dbaa8323f3c042c6/modules/videoio/src/cap_gphoto2.cpp)
|
42
|
+
- [/modules/videoio/src/cap_mfx_reader.cpp](https://github.com/opencv/opencv/blob/8b664d61221d8fef9a1f2cab2c69d9481dbca5d7/modules/videoio/src/cap_mfx_reader.cpp)
|
43
|
+
|
44
|
+
|
38
45
|
## CFR(固定フレームレート)の場合のループ制御
|
39
46
|
|
40
47
|
多くのサンプルで`cv::waitKey(30)`としているが、これは**明確な誤りである**。動画のフレームレートは多くの場合`29.97`や`24`fpsであることが多いが、動画を開いてみるまでFPSは確定しない。`30`fps決め打ちはやってはいけない。
|
3
fix include header list
answer
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
`cvCaptureFromFile`は[deprecatedになっています](http://docs.opencv.org/3.2.0/dd/d01/group__videoio__c.html#ga709ac3b8d05afd27c1d86f6636cf55fb)。またC API自体もメンテナンスされていません。代わりに[`cv::VideoCapture`クラス](http://docs.opencv.org/3.2.0/d8/dfe/classcv_1_1VideoCapture.html)を利用してください。どうしてもC APIを利用する場合は`cvCreateFileCapture`を利用してください。
|
2
2
|
|
3
3
|
```cpp
|
4
|
-
#include <opencv2/
|
4
|
+
#include <opencv2/core.hpp>
|
5
|
+
#include <opencv2/videoio.hpp>
|
6
|
+
#include <opencv2/highgui.hpp>
|
5
7
|
#include <iostream>
|
6
8
|
#include <thread>
|
7
9
|
#include <chrono>
|
2
注意事項整理
answer
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
std::cerr << "Fail to open video" << std::endl;
|
15
15
|
return 1;
|
16
16
|
}
|
17
|
+
cv::namedWindow("Live", cv::WINDOW_AUTOSIZE);
|
17
18
|
const double fps = video.get(cv::VideoCaptureProperties::CAP_PROP_FPS);
|
18
19
|
const double mspf = 1000.0 / fps;
|
19
20
|
const auto begin_time = ch::steady_clock::now();
|
@@ -26,6 +27,44 @@
|
|
26
27
|
}
|
27
28
|
```
|
28
29
|
|
29
|
-
注意
|
30
|
+
# 注意点
|
30
|
-
そもそも`cv::VideoCapture::get`の値は信用できないという話もある。例えばdecoderが`IntelMFX`, `GPhoto2`の場合、内部実装はガバガバで信用できない。
|
31
31
|
|
32
|
+
## `cv::VideoCapture::get`の信憑性
|
33
|
+
|
34
|
+
`cv::VideoCapture::get`の値は信用できないという話もある。例えばdecoderが`IntelMFX`, `GPhoto2`の場合、内部実装はガバガバで信用できない。
|
35
|
+
|
36
|
+
## CFR(固定フレームレート)の場合のループ制御
|
37
|
+
|
38
|
+
多くのサンプルで`cv::waitKey(30)`としているが、これは**明確な誤りである**。動画のフレームレートは多くの場合`29.97`や`24`fpsであることが多いが、動画を開いてみるまでFPSは確定しない。`30`fps決め打ちはやってはいけない。
|
39
|
+
|
40
|
+
また`cv::VideoCapture::read`, `cv::imshow`の実行時間も無視してはいけない。つまり`cv::waitKey`はあくまでキー入力を受けるために使うべきであり、**フレームレート制御に用いてはならない**。
|
41
|
+
[`cv::waitKey`](http://docs.opencv.org/3.2.0/d7/dfc/group__highgui.html#ga5628525ad33f52eab17feebcfba38bd7)はドキュメントによると引数はミリ秒単位であり、`0`以下の値(どういうわけかこいつ負の値もとる、頭がおかC)が渡されるとキー入力があるまで動かないので、キー入力を待たない最小時間である`1`msec.を渡す。
|
42
|
+
|
43
|
+
ではどうやってフレームレート制御するかというと、C++11で追加された時間ライブラリとスレッドライブラリを利用する。つまり[`std::chrono::steady_clock`](https://cpprefjp.github.io/reference/chrono/steady_clock.html),
|
44
|
+
[`std::chrono::milliseconds`](https://cpprefjp.github.io/reference/chrono/milliseconds.html), [`std::this_thread::sleep_until`](https://cpprefjp.github.io/reference/thread/this_thread/sleep_until.html)を利用する。
|
45
|
+
|
46
|
+
`steady_clock`はたとえプログラム起動中にOSが例えばNTP同期するなどして時刻が逆行した場合でもその影響を受けずに動作する。
|
47
|
+
|
48
|
+
## VFR(可変フレームレート)の場合のループ制御
|
49
|
+
|
50
|
+
上記サンプルはVFRな動画に対しては全く正常に動作しない。
|
51
|
+
|
52
|
+
可変フレームレートというくらいだから、各フレームごとに表示時間を決定する必要がある。これを怠るプログラムのなんとまあ多いこと。
|
53
|
+
|
54
|
+
ref: [本の虫: VFRはいまだ浸透せず](https://cpplover.blogspot.jp/2008/01/vfr.html)
|
55
|
+
|
56
|
+
VFRというのは圧縮効率の観点から極めて合理的でありサポートするべきだというのに。
|
57
|
+
|
58
|
+
しかしながら、OpenCVではどのようにしてその情報を手に入れればいいのだろうか?
|
59
|
+
|
60
|
+
1. `cv::VideoCaptureProperties::CAP_PROP_POS_MSEC`を利用して`cv::VideoCapture::read`のあとにこの値を`cv::VideoCapture::get`で読み、`std::this_thread::sleep_until`などでwaitする
|
61
|
+
2. `cv::VideoCaptureProperties::CAP_PROP_FPS`を利用して`cv::imshow`のあとにこの値を`cv::VideoCapture::get`で読み、`std::this_thread::sleep_until`などでwaitする
|
62
|
+
|
63
|
+
しかしながらすでに述べたとおり、`cv::VideoCapture::get`は
|
64
|
+
|
65
|
+
> 正しい値が返ってくるといいなぁ(願望)
|
66
|
+
|
67
|
+
程度の信憑性であり、実用に耐えない。唯一信頼が置ける場合は、コーデックが事前に決め打ちでき、かつOpenCVでのそのコーデックの実装が正しい場合のみである。
|
68
|
+
|
69
|
+
つまり、OpenCVの動画再生機能はまともにVFRな動画を再生することができない。FFmpegやそのラッパーである[L-SMASH Works](https://github.com/VFR-maniac/L-SMASH-Works)でdecodeするべきである。
|
70
|
+
|
1
信用できない場合
answer
CHANGED
@@ -27,4 +27,5 @@
|
|
27
27
|
```
|
28
28
|
|
29
29
|
注意:このサンプルは動画がVFR(可変フレームレート)な場合は正常に動作しません。つーかどうやって1frameあたりの表示時間を取得すりゃいいんだ・・・?
|
30
|
-
そもそも`cv::VideoCapture::get`の値は信用できないという話もある。
|
30
|
+
そもそも`cv::VideoCapture::get`の値は信用できないという話もある。例えばdecoderが`IntelMFX`, `GPhoto2`の場合、内部実装はガバガバで信用できない。
|
31
|
+
|