質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

0回答

2610閲覧

C++でOpenCVを用いた動画の円検出

tomonari

総合スコア3

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/06/01 16:13

編集2021/06/03 16:14

前提・実現したいこと

C++でOpenCVを使い、円検出をしたいと思っています。
ネットを駆使し、円検出を行うことに成功したのですが、動画の再生速度が非常に遅くなってしまっています。(動画上の1秒が20秒ぐらい)
この問題を解決できる方いらっしゃいませんか。
よろしくお願いします。

環境

OpenCV ver.4.5.2
Visual studio 2019
windows10

該当のソースコード

C++

1# include <iostream> 2# include <opencv2/opencv.hpp> 3# include <opencv2/videoio.hpp> 4# include <opencv2/core/core.hpp> 5# include <opencv2/imgproc/imgproc.hpp> 6# include <opencv2/highgui/highgui.hpp> 7 8using namespace cv; 9using namespace std; 10 11#define WINDOW_NAME "Video" 12 13int main() { 14 cv::Mat img, frame, gray, gau; 15 cv::namedWindow(WINDOW_NAME, cv::WINDOW_NORMAL); 16 cv::VideoCapture cap("C:\Users\tomoya\VTS_09_1.mp41116_16.mp4"); //このように、動画ファイルのパスを指定する 17 //cv::moveWindow(WINDOW_NAME, 0, 0); 18 //cv::setWindowProperty(WINDOW_NAME, cv::WND_PROP_FULLSCREEN, cv::WINDOW_FULLSCREEN); //フルスクリーンにする 19 20 int v_w = cap.get(cv::CAP_PROP_FRAME_WIDTH); //縦の大きさ 21 int v_h = cap.get(cv::CAP_PROP_FRAME_HEIGHT); //横の大きさ 22 int max_frame = cap.get(cv::CAP_PROP_FRAME_COUNT); //フレーム数 23 int fps = cap.get(cv::CAP_PROP_FPS); //フレームレート 24 cout << "fps=", fps; 25 //cap.set(CAP_PROP_POS_FRAMES, 100); //100フレーム目から再生 26 27 for (int CFN = 0; CFN < max_frame; CFN++) { 28 cap >> frame; //1フレーム分取り出してframeに保持させる 29 img = frame; 30 cv::cvtColor(img, gray, COLOR_BGR2GRAY); 31 32 // Hough変換のための前処理(画像の平滑化を行なわないと誤検出が発生しやすい) 33 cv::GaussianBlur(gray, gau, cv::Size(11, 11), 2, 2); 34 35 // Hough変換による円の検出と検出した円の描画 36 std::vector<cv::Vec3f> circles; 37 cv::HoughCircles(gau, circles, HOUGH_GRADIENT, 1, 100, 20, 50); 38 39 std::vector<cv::Vec3f>::iterator i = circles.begin(); 40// std::vector<cv::Vec3f> circles; 41 for (; i != circles.end(); ++i) { 42 cv::Point center(cv::saturate_cast<int>((*i)[0]), cv::saturate_cast<int>((*i)[1])); 43 int radius = cv::saturate_cast<int>((*i)[2]); 44 cv::circle(frame, center, radius, cv::Scalar(255, 0, 0), 2); 45 cv::circle(frame, center, 3, cv::Scalar(255, 0, 0), -1); 46// p = (float*)cv::GetSeqElem(circles, i); 47// cv::circle(img, cv::Point(cvRound(p[0]), cvRound(p[1])), 3, CV_RGB(0, 255, 0), -1, 8, 0); 48// cv::circle(img, cv::Point(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(255, 0, 0), 3, 8, 0); 49// Point center(cv::Round(circles[i][0]), cv::Round(circles[i][1])); 50 } 51 cv::imshow("Video", frame); 52 //waitKey(1); 53 int key = cv::waitKey(1); // 表示のために最低1ms待つ 54 if (key == 27) { break; }// esc or enterキーで終了 55 } 56}

また、pythonではうまくいったので、ご参考程度にpythonでの画像解析を載せておきます。

python

1import cv2 2import numpy as np 3from matplotlib import pyplot as plt 4import openpyxl 5 6#動画の読み込み 7filepath="動画のパス" 8cap=cv2.VideoCapture(filepath) 9while(cap.isOpened()): 10 a=cap.read() 11 if a[0] == False: 12 break 13 #フレームを取得 14 ret,frame=a 15 #グレースケール変換 16 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 17 gray = cv2.GaussianBlur(gray, (33,33), 1) 18 cimg=gray 19 20 #円検出 21 circles=cv2.HoughCircles(cimg,cv2.HOUGH_GRADIENT,1,20,param1=150,param2=10,minRadius=55,maxRadius=75) 22 #circles=cv2.HoughCircles(cimg,cv2.HOUGH_GRADIENT,1,15,param1=150,param2=17,minRadius=30,maxRadius=50) 23 if circles is not None and len(circles)>0: 24 circles = np.uint16(np.around(circles)) 25 for i in circles[0,:]: 26 #外側の円を描く(img,center,radius,BGR,thickness) 27 cv2.circle(frame,(i[0],i[1]),i[2],(255,0,0),2) 28 #円の中心を描く 29 cv2.circle(frame,(i[0],i[1]),1,(255,0,0),-1) 30 31 #フレームを表示 32 cv2.imshow("Frame",frame) 33 34 #qキーが押されたら途中終了 35 if cv2.waitKey(1) & 0xFF==ord('q'): 36 break

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jbpb0

2021/06/01 22:33

時間がかかってるのがOpenCVの処理のどれかの場合で、解像度(画素数)落としても円検出できるなら、 cv::cvtColor() の次でimgをリサイズして画素数減らすと、改善するかも ただし、それ以降の処理も、それに合わせた変更要ります cv::circle() の、円の中心座標と半径は、リサイズ前のものになるように調整します cv::GaussianBlur() や cv::HoughCircles() のパラメータは、リサイズに合わせて調整した方がいいかも
fana

2021/06/02 02:56

> 表示のために最低1ms待つ のであれば, cv::waitKey() の引数は 1 とするんじゃないですかね. (まぁ,ここを変えたところで速度面が改善するわけではないでしょうけども)
tomonari

2021/06/02 15:48

Pythonで以前作成した際には同じ動画を使用してきちんと再生されていたのですが、C++を使った際だけ画素数を落とさなければいけないということはあるのでしょうか?できれば精度よく円検出したいのですが、、
yominet

2021/06/02 16:00

環境がかかれてないのですが、 Debugモードでやってるとかはないでしょうか?
tomonari

2021/06/02 16:07

すみません。環境を書いていなかったのですが、Debugなしで行っています。
jbpb0

2021/06/02 22:31

fanaさんが指摘してるところを直してみたら、どうですか?
jbpb0

2021/06/02 22:40

> Pythonで以前作成した際には同じ動画を使用してきちんと再生されていた PythonのOpenCVは、共有ライブラリに処理を丸投げしてるので、違いはないはずですが、特定のバージョンでのバグかもしれないので、比較する際はOpenCVのバージョンを同じにした方がいいですよ
fana

2021/06/03 01:23

Python の実際のコードと比較して差異を見ない限りは比較はできないでしょう. 両者では関数に渡すパラメータが異なっているかもしれないし. --- あと,これは速度に関して影響がどの程度出るかわからないけども cvtColor や GaussianBlur で,入力と出力の両者を img として使っているのが気になる. これだと毎回バッファの生成と破棄が起きてると思う: ・cvtColor では毎回出力画像バッファを新規に割り当てる必要があり,それはループ末尾で毎回破棄されるハズ ・GaussianBlurの実装がどうなってるかわからんけども,入力画像バッファの上だけでやれる処理とは到底思えないので,INとOUTに同一のMatを指定された場合には内部で相応の対応を行うんじゃないかな? ループに入るよりも前の時点で必要なサイズはわかるのだから,「ループの外で出力用のバッファを用意しておき,ループ内ではそれを使いまわす」という形にできる. 大した労力でもないでしょうから,試してみてもよいのでは.
dameo

2021/06/03 01:45

これだけ言われても環境書かないので何とも言えませんが、gccとかなら、うろ覚えですが-O2とかではSIMDとか使わなかったと思います。速度が重要なモジュールはカリカリにチューニングされると聞いたことがありますし、automatic vectorizationとかでググってみたほうがいいかも。 https://gcc.gnu.org/projects/tree-ssa/vectorization.html
yominet

2021/06/03 15:20

①各所にタイマーをしこんで、どこが重いのか確認してみてはいかがでしょうか? また、Pythonのソースもはると比較しやすいと思います。 ②fpsはいくつですか? ③「OpenCV(おそらくver.4.5.2)」におそらくとありますが、  追加したlibファイル名をコピペではってもらえますでしょうか?  (コピペであることがこっそり重要) ④ img = frame; cv::cvtColor(img, img, COLOR_BGR2GRAY);    ↓   cv::cvtColor(frame, img, COLOR_BGR2GRAY); でいいんじゃないかな
tomonari

2021/06/03 16:06

①タイマーというのがあるんですね。 少し調べてやってみようと思います。 ②初学者で恐れ多いのですが、fpsをcout<<で出力しようとしたのですが、動画が表示されてfpsがうまく表示されません。(もしくは動画が最後まで行かないからfpsが表示されないのか) ③opencv_world452d やはり4.5.2みたいです。 ④そこは他の方の意見を参考に直させていただきました。
dameo

2021/06/03 19:50

与えるパラメータがC++とPythonで違ってるせいですね。。。 ハフ変換で顕著なようです
jbpb0

2021/06/03 21:39

> ③opencv_world452d それはデバッグ用なので、 opencv_world452 を使ってみてください Visual Studioのソリューション構成は、DebugじゃなくてReleaseですよね?
fana

2021/06/04 01:10

> ②fpsはいくつですか? とうのは, cap.get(cv::CAP_PROP_FPS); で得られる数値のことではないと思いますよ.(情報として無価値ですし) 「秒間で何ループくらい処理できているのか」的なことかと.
tomonari

2021/06/04 04:53

Releaseにしてopencv_world452を使ってみたら解決しました! 教えていただいたみなさん、特に解決していただいたjbpb0さん、ありがとうございました!
jbpb0

2021/06/04 05:13

> 特に解決していただいたjbpb0さん 実質解決したのはyominetさんです 早くから > Debugモードでやってるとかはないでしょうか? と聞いてましたし、使ってるlibファイル名をコピペで貼れって書いたのもyominetさんだし
dameo

2021/06/04 06:20

とりあえずめでたしめでたしですね。 ライブラリがデバッグ版とかお茶目な前提だと、もう時間測定以前だったりしますが、一応念の為ウンチクだけ書いておきます。 処理時間はエッジ検出量で大きく変わるようです。今回でいうと、HoughCircle()のparam1と使用した動画の兼ね合いですね。 Win10+VS2019+vcpkgという構成で64bitRelease版を作成して https://www.videezy.com/backgrounds/14400-circles-diagram の動画を処理させたらparam1=20だと固まったかのごとく遅かったです。無意味に解像度高いのもあって、1フレーム分の処理が数分待っても終わりません。param1=100にすると1フレーム1秒以内くらいにはなってくれました。何が言いたいかというと、Pythonと比較するならパラメータくらい合わせないとまるで結果が違うときがありますよ、ということです。 そのまま動くソースもあった方がいいだろうし、ほとんど変えてないし汚いけど、ご参考までにそのまま載せときます。 # include <iostream> # include <opencv2/opencv.hpp> # include <opencv2/videoio.hpp> # include <opencv2/core/core.hpp> # include <opencv2/imgproc/imgproc.hpp> # include <opencv2/highgui/highgui.hpp> using namespace cv; using namespace std; #define WINDOW_NAME "Video" int main() { cv::Mat img, frame, gray, gau, canny; cv::namedWindow(WINDOW_NAME, cv::WINDOW_NORMAL); // https://www.videezy.com/backgrounds/14400-circles-diagram (ここから頂きました) cv::VideoCapture cap("C:\\diagram_circles.mp4"); //このように、動画ファイルのパスを指定する int v_w = cap.get(cv::CAP_PROP_FRAME_WIDTH); //縦の大きさ int v_h = cap.get(cv::CAP_PROP_FRAME_HEIGHT); //横の大きさ int max_frame = cap.get(cv::CAP_PROP_FRAME_COUNT); //フレーム数 int fps = cap.get(cv::CAP_PROP_FPS); //フレームレート cout << "v_w=" << v_w << endl; cout << "v_h=" << v_h << endl; cout << "fps=" << fps << endl; cout << "max_frame=" << max_frame << endl; // 時間調整用に100フレームでやめる const int MAX_FRAME = 100; max_frame = (max_frame > MAX_FRAME) ? MAX_FRAME : max_frame; cv::TickMeter meterForHough; for (int CFN = 0; CFN < max_frame; CFN++) { cap >> frame; //1フレーム分取り出してframeに保持させる img = frame; cv::cvtColor(img, gray, COLOR_BGR2GRAY); cv::GaussianBlur(gray, gau, cv::Size(11, 11), 2, 2); //Canny処理単体目視確認パラメータ調整用 //cv::Canny(gau, canny, 100, 50); //cv::Canny(gau, canny, 20, 10); // Hough変換による円の検出と検出した円の描画 std::vector<cv::Vec3f> circles; meterForHough.start(); //cv::HoughCircles(gau, circles, HOUGH_GRADIENT, 1, 100, 20, 50); cv::HoughCircles(gau, circles, HOUGH_GRADIENT, 1, 300, 100, 40, 200, 0); meterForHough.stop(); std::vector<cv::Vec3f>::iterator i = circles.begin(); for (; i != circles.end(); ++i) { cv::Point center(cv::saturate_cast<int>((*i)[0]), cv::saturate_cast<int>((*i)[1])); int radius = cv::saturate_cast<int>((*i)[2]); cv::circle(frame, center, radius, cv::Scalar(255, 0, 0), 2); cv::circle(frame, center, 3, cv::Scalar(255, 0, 0), -1); } cv::imshow("Video", frame); //cv::imshow("Video", canny); int key = cv::waitKey(1); // 表示のために最低1ms待つ if (key == 27) { break; }// esc or enterキーで終了 } cout << "avg_meter(hough): " << meterForHough.getAvgTimeMilli() << "[ms]" << endl; }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問