私はmjpegストリームを提供するウェブカメラでビデオをキャプチャしています。ワーカースレッドでビデオキャプチャを行いました。次のようにキャプチャを開始します。
const std::string videoStreamAddress = "http://192.168.1.173:80/live/0/mjpeg.jpg?x.mjpeg";
qDebug() << "start";
cap.open(videoStreamAddress);
qDebug() << "really started";
cap.set(CV_CAP_PROP_FRAME_WIDTH, 720);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 576);
カメラは20fpsでストリームを供給しています。しかし、次のように20fpsで読み取りを行った場合:
if (!cap.isOpened()) return;
Mat frame;
cap >> frame; // get a new frame from camera
mutex.lock();
m_imageFrame = frame;
mutex.unlock();
その後、3秒以上の遅れがあります。その理由は、キャプチャされたビデオが最初にバッファに保存されるためです。最初にカメラを起動したとき、バッファは蓄積されますが、フレームを読み取れませんでした。したがって、バッファから読み取ると、常に古いフレームが表示されます。私が現在持っている唯一の解決策は、30fpsでバッファを読み取ることです。これにより、バッファが迅速にクリーニングされ、深刻な遅延がなくなります。
カメラを起動するたびに手動でバッファをクリーニング/フラッシュできる他の解決策はありますか?
this sourceによると、_cv::VideoCapture
_オブジェクトのバッファサイズを設定できます。
_cv::VideoCapture cap;
cap.set(CV_CAP_PROP_BUFFERSIZE, 3); // internal buffer will now store only 3 frames
// rest of your code...
_
ただし、重要な制限があります。
CV_CAP_PROP_BUFFERSIZE内部バッファメモリに格納されているフレームの量(注:DC1394 v 2.xバックエンドのみで現在サポートされています)
コメントからの更新。OpenCVの新しいバージョン(3.4+)では、制限がなくなったようで、コードはスコープ付き列挙を使用します。
_cv::VideoCapture cap;
cap.set(cv::CAP_PROP_BUFFERSIZE, 3);
_
解決策が機能しない場合は、 この投稿 をご覧ください。問題を回避する方法を説明しています。
簡単に言うと、フレームのクエリに必要な時間が測定されます。低すぎる場合、フレームがバッファから読み取られ、破棄できることを意味します。測定された時間が特定の制限を超えるまで、フレームのクエリを続けます。この場合、バッファは空で、返されたフレームは最新です。
(リンクされた投稿の答えは、バッファからフレームを返すには、最新のフレームを返す時間の約1/8がかかることを示しています。もちろん、走行距離は異なる場合があります!)
this postに触発された別の解決策は、バッファーを空に保つために高速でフレームを継続的に取得する3番目のスレッドを作成することです。このスレッドは、オーバーヘッドを回避するために cv::VideoCapture.grab()
を使用する必要があります。
単純なスピンロックを使用して、実際のワーカースレッドと3番目のスレッドの間で読み取りフレームを同期できます。
みんなこれはかなり愚かで厄介な解決策ですが、受け入れられた答えはいくつかの理由で私を助けませんでした。 (pythonのコードですが、本質はかなり明確です)
# vcap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
data = np.zeros((1140, 2560))
image = plt.imshow(data)
while True:
vcap = cv2.VideoCapture("rtsp://admin:@192.168.3.231")
ret, frame = vcap.read()
image.set_data(frame)
plt.pause(0.5) # any other consuming operation
vcap.release()
フレームをつかむのに少し時間がかかったことを確認できます。コーディングは簡単ですが、少し信頼できません。潜在的に、このコードはデッドロックを引き起こす可能性があります。
#include <chrono>
using clock = std::chrono::high_resolution_clock;
using duration_float = std::chrono::duration_cast<std::chrono::duration<float>>;
// ...
while (1) {
TimePoint time_start = clock::now();
camera.grab();
if (duration_float(clock::now() - time_start).count() * camera.get(cv::CAP_PROP_FPS) > 0.5) {
break;
}
}
camera.retrieve(dst_image);
コードはC++ 11を使用します。
カメラのフレームレートがわかっている場合は、この情報(1秒あたり30フレーム)を使用して、フレームレートが低くなるまでフレームを取得できます。グラブ機能が遅れると(つまり、標準フレームレートよりもフレームをグラブする時間が長くなると)、バッファ内のすべてのフレームを取得し、opencvが次のフレームがカメラから来るのを待つ必要があるためです。
while(True):
prev_time=time.time()
ref=vid.grab()
if (time.time()-prev_time)>0.030:#something around 33 FPS
break
ret,frame = vid.retrieve(ref)