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

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

新規登録して質問してみよう
ただいま回答率
85.50%
YOLO

YOLOとは、画像検出および認識用ニューラルネットワークです。CベースのDarknetというフレームワークを用いて、画像や動画からオブジェクトを検出。リアルタイムでそれが何になるのかを認識し、分類することができます。

C++/CLI

C++/CLIは、.NET Frameworkの共通言語基盤であるCLI向けにC++を拡張したプログラム言語です。前身のC++マネージ拡張と比較するとシンプルで分かりやすい構文になっており、高い可読性を持ちます。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Q&A

1回答

1794閲覧

opencv dnnモジュールで連続して検出できない

Soogle44

総合スコア8

YOLO

YOLOとは、画像検出および認識用ニューラルネットワークです。CベースのDarknetというフレームワークを用いて、画像や動画からオブジェクトを検出。リアルタイムでそれが何になるのかを認識し、分類することができます。

C++/CLI

C++/CLIは、.NET Frameworkの共通言語基盤であるCLI向けにC++を拡張したプログラム言語です。前身のC++マネージ拡張と比較するとシンプルで分かりやすい構文になっており、高い可読性を持ちます。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

0グッド

0クリップ

投稿2021/08/21 08:09

編集2022/01/12 10:55

前提・実現したいこと

C++/CLIにて学習済みのモデルを使って物体検出のプログラムの作成をしています。

https://github.com/opencv/opencv/blob/3.4.15/samples/dnn/object_detection.cpp
こちらのopencvのサンプルを参考にファイル選択ダイアログで画像、設定ファイル(.cfg,.prototxt)、モデルファイル(.weights,.caffemodel)を選択して、ピクチャーボックスに検出結果を表示するようなWindowsフォームアプリケーションを作成しました。

アプリの動作の流れとしては画像選択→設定ファイル・モデルファイル選択→検出→検出結果画像の描画となっています。
困っていることとしては、アプリを起動して1度目は検出結果画像の描画までうまくいくのですが、その後選択するモデルを切り替えてもう一度検出しようとすると、うまく検出できていない画像が表示されてしまいます。
(画像選択→設定ファイル・モデルファイル選択→検出→検出結果画像の描画→別の設定ファイル・モデルファイル選択→検出→検出結果画像の描画の順序だとうまくいかない)

なにかアイデアなどあれば教えていただきたいです。
よろしくお願いいたします。

発生している問題・エラーメッセージ

アプリ起動後はじめのyolo検出
イメージ説明

yolo検出後のmobilenetssd検出
イメージ説明

アプリ起動後はじめのmobilenetssd検出
イメージ説明

mobilenetssd検出後のyolo検出
イメージ説明

該当のソースコード

c++

1#pragma once 2 3#define _CRT_SECURE_NO_WARNINGS 4 5#include <iostream> 6#include <msclr\marshal_cppstd.h> 7#include <opencv2/opencv.hpp> 8#include "common.hpp" 9 10std::string imgpath = "", cfg = "", model = ""; 11dnn::Net *net = NULL; 12std::vector<cv::String> outNames; 13int selectModel = NOMODEL; 14 15 16namespace opencvdetectgui { 17 18 using namespace System; 19 using namespace System::ComponentModel; 20 using namespace System::Collections; 21 using namespace System::Windows::Forms; 22 using namespace System::Data; 23 using namespace System::Drawing; 24 25//////////////////////////////////////////////////////////////////////// 26////////////////////省略//////////////////////////////////////////////// 27//////////////////////////////////////////////////////////////////////// 28 29 } 30#pragma endregion 31 private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { 32 33 //ファイル選択ダイアログでもととなる画像を選択して左側のピクチャーボックスに描画 34 35 } 36 37 private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { 38 39    //ファイル選択ダイアログで設定ファイル(.cfg,.prototxt)、モデルファイル(.weights,.caffemodel)の選択 40 41 } 42 43 private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) { 44 45 //接待ファイル、モデルの読み込み 46 dnn::Net net = readNet(model, cfg); 47 net.setPreferableBackend(0); 48 net.setPreferableTarget(0); 49 outNames = net.getUnconnectedOutLayersNames(); 50 51 //ビデオキャプチャの取得(後々カメラ映像で検出したいためこのように書いています) 52 VideoCapture cap; 53 if (imgpath != "") 54 cap.open(imgpath); 55 else 56 cap.open(0); 57 cv::Mat frame, blob; 58 cap >> frame; 59 60 //yolo,mobilenetssdそれぞれのパラメータで読み込んで検出 61 if (selectModel == YOLO) { 62 preprocess(frame, net, cv::Size(416, 416), 0.00392, cv::Scalar(0, 0, 0), true); 63 } 64 else if (selectModel == SSD) { 65 preprocess(frame, net, cv::Size(300, 300), 0.007843, cv::Scalar(127.5, 127.5, 127.5), false); 66 } 67 68 std::vector<Mat> outs; 69 net.forward(outs, outNames); 70 71 postprocess(frame, outs, net, 0); 72 73 //検出したバウンディングボックスの書き込み 74 std::vector<double> layersTimes; 75 double freq = getTickFrequency() / 1000; 76 double t = net.getPerfProfile(layersTimes) / freq; 77 std::string label = format("Inference time: %.2f ms", t); 78 putText(frame, label, cv::Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0)); 79 80 //検出結果画像を右側のピクチャーボックスのサイズに調整してから表示 81 cv::Mat mat_pb(pictureBox2->Height, pictureBox2->Width, CV_8U); 82 cv::resize(frame, frame, mat_pb.size()); 83 drawImage(pictureBox2, frame); 84 } 85}; 86}

common.hppは以下のものをコピペしました。
(https://github.com/opencv/opencv/blob/3.4.15/samples/dnn/common.hpp)

試したこと

アプリ起動→yolo検出(正常)→mobilenetssd検出(異常)→yolo検出(正常)
となるのでなにかの変数が検出ごとに初期化されずに保存されていることが原因なのですが、その変数がわからず困っています。
common.hppの関数でstaticで宣言されているところも確認しましたが改善されませんでした。

補足情報(FW/ツールのバージョンなど)

opencv 3.4.15
visual studio 2019
Windows 10 64 bit

追記

モデルを読み込んでから検出して描画まで複数の関数があるのですが、その中でどの関数を検出手法間で共有(SSDで使ってからYOLOで使う)すると不具合が起きるのか調べてみました。

調べたところ、postprocess()という関数を共有すると不具合が起こっているようです。
一応postprocess()と内容が同じ関数のpostprocess_yolo()とpostprocess_ssd()を定義して、共有しないようにするととりあえず画像に関しては連続して(SSD検出後にYOLO検出)は問題なくできるようにはなりました。

ただ理由がわからず困っています。

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

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

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

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

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

guest

回答1

0

当方で下記コードで確認したところ、「postprocess()」の「static」を取るだけで直りました

c++

1(中略) 2std::string keys = 3(中略) 4 "{ nms | .4 | Non-maximum suppression threshold. }" 5 "{ scale2 | 1.0 | Scale 2 }" 6 "{ width2 | 100 | Width 2 }" 7 "{ height2 | 100 | Height 2 }" 8 "{ model2 | | Model 2 }" 9 "{ config2 | | Config 2 }" 10 "{ classes2 | | Optional path to a text file with names of classes to label detected objects. 2 }" 11 "{ backend | 0 | Choose one of computation backends: " 12(中略) 13 14using namespace cv; 15using namespace dnn; 16 17float confThreshold, nmsThreshold; 18std::vector<std::string> classes; 19std::vector<std::string> classes2; 20int classesnum; 21 22(中略) 23int main(int argc, char** argv) 24{ 25 (中略) 26 float scale2 = parser.get<float>("scale2"); 27 int inpWidth2 = parser.get<int>("width2"); 28 int inpHeight2 = parser.get<int>("height2"); 29 std::string modelPath2 = findFile(parser.get<String>("model2")); 30 std::string configPath2 = findFile(parser.get<String>("config2")); 31 32 // Open file with classes names. 33 if (parser.has("classes")) 34 { 35 std::string file = parser.get<String>("classes"); 36 std::ifstream ifs(file.c_str()); 37 if (!ifs.is_open()) 38 CV_Error(Error::StsError, "File " + file + " not found"); 39 std::string line; 40 while (std::getline(ifs, line)) 41 { 42 classes.push_back(line); 43 } 44 } 45 if (parser.has("classes2")) 46 { 47 std::string file2 = parser.get<String>("classes2"); 48 std::ifstream ifs2(file2.c_str()); 49 if (!ifs2.is_open()) 50 CV_Error(Error::StsError, "File " + file2 + " not found"); 51 std::string line2; 52 while (std::getline(ifs2, line2)) 53 { 54 classes2.push_back(line2); 55 } 56 } 57 58 for (int num = 0; num < 2; num++) { 59 classesnum = num; 60 // Load a model. 61 Net net = readNet(modelPath, configPath, parser.get<String>("framework")); 62 if (num != 0) 63 net = readNet(modelPath2, configPath2, parser.get<String>("framework")); 64 int backend = parser.get<int>("backend"); 65 net.setPreferableBackend(backend); 66 net.setPreferableTarget(parser.get<int>("target")); 67 std::vector<String> outNames = net.getUnconnectedOutLayersNames(); 68 69 (中略) 70 71 // Process the frame 72 if (!frame.empty()) 73 { 74 if (num == 0) 75 preprocess(frame, net, Size(inpWidth, inpHeight), scale, mean, swapRB); 76 else 77 preprocess(frame, net, Size(inpWidth2, inpHeight2), scale2, mean, swapRB); 78 processedFramesQueue.push(frame); 79 80 (中略) 81 82 // Process frames. 83 Mat frame, blob; 84 while (waitKey(1) < 0) 85 { 86 cap >> frame; 87 if (frame.empty()) 88 { 89 waitKey(); 90 break; 91 } 92 if (num == 0) 93 preprocess(frame, net, Size(inpWidth, inpHeight), scale, mean, swapRB); 94 else 95 preprocess(frame, net, Size(inpWidth2, inpHeight2), scale2, mean, swapRB); 96 97 (中略) 98 99 } 100 return 0; 101} 102 103void postprocess(Mat& frame, const std::vector<Mat>& outs, Net& net, int backend) 104{ 105 //static std::vector<int> outLayers = net.getUnconnectedOutLayers(); 106 //static std::string outLayerType = net.getLayer(outLayers[0])->type; 107 std::vector<int> outLayers = net.getUnconnectedOutLayers(); 108 std::string outLayerType = net.getLayer(outLayers[0])->type; 109 110 (中略) 111} 112 113void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame) 114{ 115 rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 255, 0)); 116 117 std::string label = format("%.2f", conf); 118 if (classesnum == 0) { 119 if (!classes.empty()) 120 { 121 CV_Assert(classId < (int)classes.size()); 122 label = classes[classId] + ": " + label; 123 } 124 } else { 125 if (!classes2.empty()) 126 { 127 CV_Assert(classId < (int)classes2.size()); 128 label = classes2[classId] + ": " + label; 129 } 130 } 131 132 (中略) 133}

投稿2021/09/02 06:55

編集2021/09/02 08:18
jbpb0

総合スコア7651

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

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

Soogle44

2021/09/02 07:12

回答ありがとうございます。 私もstaticで値が初期化されていないからと考えてstaticを削除しましたが同様に正しく検出出来ませんでした。 追記にあるようにyoloとssdで関数名をだけ変えて対応していますが、yoloであればyolov2であろうとyolov3であろうと連続で検出しても問題ありませんでした。 postprocessが呼んでいる関数も確認する必要がありそうです。
jbpb0

2021/09/02 08:31 編集

当方では、「postprocess()」の「static」を取るだけで直りました 「postprocess()」の「static」を付けたら、質問者さんと同じ現象になります 確認したコード全体を回答に書こうと思ったのですが、文字数制限のためにかなり削らないとならず、OpenCVのサンプルの元コードからの変更箇所しか記載できなかったのですが、必要最小限の変更しかしてないので、元のコードと突き合せたら復元できると思います main()のパラメータを解釈するところが終わった以降を、forループで2回繰り返してて、その中で「net =...」と「preprocess(...」は、1回目と2回目で変えてます 1回目と2回目で使用するファイルとかを変えられるように、パラメータを増やしています (「--config2」等)
jbpb0

2021/09/02 10:12 編集

コンパイル後に、下記のように実行して確認しました (「--classes=...」と「--classes2=...」は、無くても大丈夫です) object_detection2 --config=yolov3.cfg --model=yolov3.weights --classes=coco-labels-2014_2017.txt --width=416 --height=416 --scale=0.00392 --input=dog.jpg --rgb --config2=MobileNetSSD_deploy.prototxt --model2=MobileNetSSD_deploy.caffemodel --classes2=names-voc.txt --width2=300 --height2=300 --scale2=0.00784 object_detection2 --config=MobileNetSSD_deploy.prototxt --model=MobileNetSSD_deploy.caffemodel --classes=names-voc.txt --width=300 --height=300 --scale=0.00784 --input=dog.jpg --rgb --config2=yolov3.cfg --model2=yolov3.weights --classes2=coco-labels-2014_2017.txt --width2=416 --height2=416 --scale2=0.00392 【追記】 当方で確認したコード全体を、下記からダウンロードできるようにしました https://19.gigafile.nu/1101-b214ea0e13d9ace62ab57b0e5ac179f83 「ダウンロードキー」を入力する欄がありますが、何も入力せずに「ダウンロード開始」をクリックすれば、ダウンロードできます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

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

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

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問