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

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

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

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

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

VMware

VMwareとは、 ハードウェアで動作するOS上で仮想マシンを作成、実行するソフトウェアです。 Windows上でUNIX系OSを動作させたり、他のOS上で別の仮想OSを動作することが可能です。

C++

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

Q&A

解決済

2回答

1758閲覧

OpenCVを使った笑顔検出での問題

kotetu

総合スコア34

OpenCV

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

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

VMware

VMwareとは、 ハードウェアで動作するOS上で仮想マシンを作成、実行するソフトウェアです。 Windows上でUNIX系OSを動作させたり、他のOS上で別の仮想OSを動作することが可能です。

C++

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

0グッド

0クリップ

投稿2017/11/03 03:58

編集2017/11/03 06:42

###前提・実現したいこと
OpenCVで元からあるサンプルコード「smiledetect.cpp」を用いて笑顔検出のシステムを作っています。
検出機能を実装中に以下の問題が発生しました。

###発生している問題・状況

問題:笑顔検出されていたら四角で囲むはずが、囲まれていない。 状況:リアルタイムで検出している顔だけが移っている状態。    画像に笑顔検出のプログラムを実行したところ、画像のようになった。 [画像での笑顔検出](https://imgur.com/a/npC1H)

###該当のソースコード

C++

1#include "opencv2/objdetect.hpp" 2#include "opencv2/highgui.hpp" 3#include "opencv2/imgproc.hpp" 4#include "opencv2/opencv.hpp" 5#include <iostream> 6using namespace std; 7using namespace cv; 8static void help() 9{ 10cout<<"Program build!!!! "<<CV_VERSION<< "\n"<<endl; 11} 12void detectAndDraw( Mat& img, CascadeClassifier& cascade, 13 CascadeClassifier& nestedCascade, 14 double scale, bool tryflip ); 15string cascadeName; 16string nestedCascadeName; 17int main(int argc, const char** argv) 18{ 19 VideoCapture capture; 20 Mat frame, image; 21 string inputName; 22 bool tryflip; 23 help(); 24 CascadeClassifier cascade, nestedCascade; 25 double scale; 26 //カメラからのビデオキャプチャを初期化する 27 CvCapture*videoCapture = cvCreateCameraCapture(1); 28 if(videoCapture ==NULL) 29 { 30 return -1; 31 } 32 cv::CommandLineParser parser(argc, argv, 33 "{help h||}{scale|1|}" 34 "{cascade|../../data/haarcascades/haarcascade_frontalface_alt.xml|}" 35 "{smile-cascade|../../data/haarcascades/haarcascade_smile.xml|}" 36 "{try-flip||}{@input||}"); 37 if (parser.has("help")) 38 { 39 help(); 40 return 0; 41 } 42 //ウィンドウを作成する 43 char windowName[]="camera"; 44 cvNamedWindow(windowName,CV_WINDOW_AUTOSIZE); 45 while(cvWaitKey(1)==-1) 46 { 47 //カメラから1フレーム習得する 48 IplImage*image=cvQueryFrame(videoCapture); 49 //ウィンドウに画像を表示する 50 cvShowImage(windowName,image); 51 } 52 cascadeName = parser.get<string>("cascade"); 53 nestedCascadeName = parser.get<string>("smile-cascade"); 54 tryflip = parser.has("try-flip"); 55 inputName = parser.get<string>("@input"); 56 scale = parser.get<int>("scale"); 57 if (!parser.check()) 58 { 59 help(); 60 return 1; 61 } 62 if (scale < 1) 63 scale = 1; 64 if( !cascade.load( cascadeName ) ) 65 { 66 cerr << "ERROR: Could not load face cascade" << endl; 67 help(); 68 return -1; 69 } 70 if( !nestedCascade.load( nestedCascadeName ) ) 71 { 72 cerr << "ERROR: Could not load smile cascade" << endl; 73 help(); 74 return -1; 75 } 76 if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) ) 77 { 78 int c = inputName.empty() ? 0 : inputName[0] - '0' ; 79 if(!capture.open(c)) 80 cout << "Capture from camera #" << c << " didn't work" << endl; 81 } 82 else if( inputName.size() ) 83 { 84 if(!capture.open( inputName )) 85 cout << "Could not read " << inputName << endl; 86 } 87 if( capture.isOpened() ) 88 { 89 cout << "Video capturing has been started ..." << endl; 90 cout << endl << "NOTE: Smile intensity will only be valid after a first smile has been detected" << endl; 91 while(1) 92 { 93 capture >> frame; 94 if( frame.empty() ) 95 break; 96 Mat frame1 = frame.clone(); 97 detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip ); 98 char c = (char)waitKey(10); 99 if( c == 27 || c == 'q' || c == 'Q' ){ 100 break; } 101 //else if(c==32|| c=='s'||c=='S'){ 102 //cv::imwrite("smile.png", frame); } 103 } 104 } 105 else 106 { 107 cerr << "ERROR: Could not initiate capture" << endl; 108 help(); 109 return -1; 110 } 111 //ビデオキャプチャを開放する 112 cvReleaseCapture(&videoCapture); 113 //ウィンドウを破棄する 114 cvDestroyWindow(windowName); 115 return 0; 116 } 117 void detectAndDraw( Mat& img, CascadeClassifier& cascade, 118 CascadeClassifier& nestedCascade, 119 double scale, bool tryflip) 120 { 121 vector<Rect> faces, faces2; 122 const static Scalar colors[] = 123 { 124 Scalar(255,0,0), 125 Scalar(255,128,0), 126 Scalar(255,255,0), 127 Scalar(0,255,0), 128 Scalar(0,128,255), 129 Scalar(0,255,255), 130 Scalar(0,0,255), 131 Scalar(255,0,255) 132 }; 133 Mat gray, smallImg; 134 cvtColor( img, gray, COLOR_BGR2GRAY ); 135 double fx = 1 / scale; 136 resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR ); 137 equalizeHist( smallImg, smallImg ); 138 cascade.detectMultiScale( smallImg, faces, 139 1.1, 2, 0 140 //|CASCADE_FIND_BIGGEST_OBJECT 141 //|CASCADE_DO_ROUGH_SEARCH 142 |CASCADE_SCALE_IMAGE, 143 Size(30, 30) ); 144 if( tryflip ) 145 { 146 flip(smallImg, smallImg, 1); 147 cascade.detectMultiScale( smallImg, faces2, 148 1.5, 2, 0 149 //|CASCADE_FIND_BIGGEST_OBJECT 150 //|CASCADE_DO_ROUGH_SEARCH 151 |CASCADE_SCALE_IMAGE, 152 Size(30, 30) ); 153 for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r ) 154 { 155 faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height)); 156 } 157 } 158 for ( size_t i = 0; i < faces.size(); i++ ) 159 { 160 Rect r = faces[i]; 161 Mat smallImgROI; 162 vector<Rect> nestedObjects; 163 Point center; 164 Scalar color = colors[i%8]; 165 int radius; 166int face[] = {cv::FONT_HERSHEY_SIMPLEX, cv::FONT_HERSHEY_PLAIN, cv::FONT_HERSHEY_DUPLEX, cv::FONT_HERSHEY_COMPLEX, 167 cv::FONT_HERSHEY_TRIPLEX, cv::FONT_HERSHEY_COMPLEX_SMALL, cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 168 cv::FONT_HERSHEY_SCRIPT_COMPLEX, cv::FONT_ITALIC}; 169 double aspect_ratio = (double)r.width/r.height; 170 if( 0.75 < aspect_ratio && aspect_ratio < 1.3 ) 171 { 172 center.x = cvRound((r.x + r.width*0.5)*scale); 173 center.y = cvRound((r.y + r.height*0.5)*scale); 174 radius = cvRound((r.width + r.height)*0.25*scale); 175 circle( img, center, radius, color, 3, 8, 0 ); 176 } 177 else 178 rectangle( img, cvPoint(cvRound(r.x*scale), cvRound(r.y*scale)), 179 cvPoint(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),color, 3, 8, 0); 180 const int half_height=cvRound((float)r.height/2); 181 r.y=r.y + half_height; 182 r.height = half_height-1; 183 smallImgROI = smallImg( r ); 184 nestedCascade.detectMultiScale( smallImgROI, nestedObjects, 185 1.1, 0, 0 186 //|CASCADE_FIND_BIGGEST_OBJECT 187 //|CASCADE_DO_ROUGH_SEARCH 188 //|CASCADE_DO_CANNY_PRUNING 189 |CASCADE_SCALE_IMAGE, 190 Size(30, 30) ); 191 // The number of detected neighbors depends on image size (and also illumination, etc.). The 192 // following steps use a floating minimum and maximum of neighbors. Intensity thus estimated will be 193 //accurate only after a first smile has been displayed by the user. 194 const int smile_neighbors = (int)nestedObjects.size(); 195 static int max_neighbors=-1; 196 static int min_neighbors=-1; 197 if (min_neighbors == -1) 198 min_neighbors = smile_neighbors; 199 max_neighbors = MAX(max_neighbors, smile_neighbors); 200 // Draw rectangle on the left side of the image reflecting smile intensity 201 float intensityZeroOne = ((float)smile_neighbors - min_neighbors) / (max_neighbors - min_neighbors + 1); 202 //(float)img.rows 203 double rect_height = cvRound((float)img.rows* intensityZeroOne)/2; 204 char rectchar[256]; 205 Scalar col = Scalar((float)255 * intensityZeroOne, 0, 0); 206 sprintf(rectchar, "%f", rect_height); 207 //greenline 208 line(img,Point(0,240), Point(100,240), Scalar(0,200,0), 5, CV_AA); 209 if(rect_height>0) 210 //positive line 211 rectangle(img, cvPoint(0, 235), cvPoint(img.cols/10, 240-rect_height), CV_RGB(200,0,0), CV_FILLED); 212 else 213 //negative line 214 rectangle(img, cvPoint(0,245), cvPoint(img.cols/10,480+rect_height*50), CV_RGB(0,0,200), CV_FILLED); 215 //Text 216 rectangle(img, cvPoint(400,430),cvPoint(630,395),CV_RGB(0,0,200), 3, 4); 217 putText(img, rectchar, cv::Point(400,425), face[0], 1.2, cv::Scalar(0,0,200), 2, CV_AA); 218 } 219 imshow( "result", img ); 220}

###試したこと
コマンド「lsusb」を打って、webカメラが認識されていることは分かっています。

###補足情報(言語/FW/ツール等のバージョンなど)
Webカメラを使っての、リアルタイム表示は出来ています。
言語:c++
環境:Windows8
仮想空間:VMware Player
ツール:OpenCV3.3.1
参考サイト:https://so-zou.jp/software/tech/library/opencv/camera/ https://github.com/opencv/opencv/blob/master/samples/cpp/smiledetect.cpp

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

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

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

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

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

LouiS0616

2017/11/03 04:49

なにかサイトなどを参考にされたのでしょうか?新旧のOpenCVが入りまじり、必要以上にごちゃごちゃした印象を受けます。
LouiS0616

2017/11/03 04:50

あと、コードブロック内では画像やリンクの貼り付けは出来ません。``` ``` より外側に記述してください。
kotetu

2017/11/03 04:52

少々お待ちください。参考にしたサイトがあるはずなので探してきます。
LouiS0616

2017/11/03 04:57

前者のサイトの情報は古く、C++ではなくC言語向けの情報ですね。基本的には、cvXXXという関数は、現在ではcv::xXXとリネームされています。
kotetu

2017/11/03 04:58

cvWaitKeyでしたらcv::waitKeyという認識で宜しいでしょうか?
LouiS0616

2017/11/03 05:28

その通りです。あと、IplImageなども現在では使われません。Matに一本化した方が良いです。
kotetu

2017/11/03 06:28

matに一本化のやり方が分からないです
LouiS0616

2017/11/03 06:33

このコードの場合、IplImageが含まれているwhileブロック全体が不要に見えます。
LouiS0616

2017/11/03 06:37

C++自体のコーディング経験、OpenCVの利用経験はどれくらいあるでしょうか?
kotetu

2017/11/03 06:37

IpImageが含まれているwhileのところをコメントアウトさせました。
kotetu

2017/11/03 06:39

C++自体のコーディング経験は、少しだけあります。OpenCVは、最近研究で使い始めました。
guest

回答2

0

ベストアンサー

原因

ご提示のサンプルコードを動かしてみました。
確かに質問者様と同じような状態になりましたが、これは仕様なようです。

C++

// Draw rectangle on the left side of the image reflecting smile intensity
float intensityZeroOne = ((float)smile_neighbors - min_neighbors) / (max_neighbors - min_neighbors + 1);
int rect_height = cvRound((float)img.rows * intensityZeroOne);
Scalar col = Scalar((float)255 * intensityZeroOne, 0, 0);
rectangle(img, cvPoint(0, img.rows), cvPoint(img.cols/10, img.rows - rect_height), col, -1);

**『笑顔強度』**を計算して、左側にバーで示しているのだとか。
試しにcaptureをWebカメラに接続して、いろんな表情をしてみてください。
実際にバーが上下すること、色合いが変わることが観測できます(顔面攣りかけました)。

サンプル

サンプルコードにはその他突っ込みたい点がいくつかありました。
初心者の方がこれを見て理解するのはちょっとしんどすぎると思います。

ですので、適当にコードを書いてみました。

C++

1#include <iostream> 2#include <array> 3#include <vector> 4 5#include <opencv2/opencv.hpp> 6 7const static std::array<cv::Scalar, 8> colors = { 8 // B G R 9 cv::Scalar(255, 0, 0), // Blue 10 cv::Scalar(255, 128, 0), 11 cv::Scalar(255, 255, 0), // Cyan 12 cv::Scalar( 0, 255, 0), // Green 13 cv::Scalar( 0, 128, 255), 14 cv::Scalar( 0, 255, 255), // Yellow 15 cv::Scalar( 0, 0, 255), // Red 16 cv::Scalar(255, 0, 255) // Magenta 17}; 18 19void detectFacesAndSmiles( 20 const cv::Mat &grayImg, cv::Mat &dst, 21 cv::CascadeClassifier& faceCascade, 22 cv::CascadeClassifier& smileCascade 23); 24void detectAndDraw( 25 const cv::Mat& grayImg, cv::Mat &dst, 26 cv::CascadeClassifier& cascade, 27 std::vector<cv::Rect>& detected = std::vector<cv::Rect>() 28); 29 30int main(void) 31{ 32 // Load cascades 33 cv::CascadeClassifier faceCascade, smileCascade; 34 std::string faceCascadeName = "haarcascade_frontalface_alt.xml"; 35 if(!faceCascade.load(faceCascadeName)) { 36 std::cerr << "ERROR: Could not load face cascade" << std::endl; 37 return -1; 38 } 39 std::string smileCascadeName = "haarcascade_smile.xml"; 40 if(!smileCascade.load(smileCascadeName)) { 41 std::cerr << "ERROR: Could not load smile cascade" << std::endl; 42 return -1; 43 } 44 45 // Open web camera 46 cv::VideoCapture capture(0); 47 if(!capture.isOpened()) { 48 std::cerr << "ERROR: Could not initiate capture" << std::endl; 49 return -1; 50 } 51 52 // Main Process 53 std::cout << "Video capturing has been started ..." << std::endl; 54 55 cv::Mat frame, dst, gray; 56 while(true) { 57 capture >> frame; 58 detectFacesAndSmiles(frame, dst, faceCascade, smileCascade); 59 60 cv::imshow("Result", dst); 61 char c = cv::waitKey(10); 62 if(c == 27 || c == 'q' || c == 'Q') { 63 break; 64 } 65 } 66 67 return 0; 68} 69 70void detectFacesAndSmiles( 71 const cv::Mat &img, cv::Mat &dst, 72 cv::CascadeClassifier& faceCascade, 73 cv::CascadeClassifier& smileCascade) 74{ 75 // Prepare images 76 cv::Mat gray; 77 78 dst = img.clone(); 79 cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY); 80 81 // Detect faces 82 std::vector<cv::Rect> detectedFaces; 83 detectAndDraw( 84 gray, dst, faceCascade, detectedFaces 85 ); 86 87 // Detect Smiles 88 int colorNum = 0; 89 for(const auto& face: detectedFaces) { 90 cv::Scalar color = colors[colorNum++ % colors.size()]; 91 92 detectAndDraw( 93 gray(face), dst(face), smileCascade 94 ); 95 } 96} 97 98void detectAndDraw( 99 const cv::Mat& grayImg, cv::Mat& dst, 100 cv::CascadeClassifier& cascade, 101 std::vector<cv::Rect>& detected) 102{ 103 // Detect 104 cascade.detectMultiScale( 105 grayImg, // image 106 detected, // dst objects 107 1.1, // scale factor 108 2, // min neighbors 109 0, // flag 110 cv::Size(40, 40) // min size 111 ); 112 113 // Draw rectangle 114 int colorNum = 0; 115 for(auto face: detected) { 116 cv::rectangle( 117 dst, 118 cv::Point(face.x, face.y), 119 cv::Point(face.x + face.width, face.y + face.height), 120 // Vertex of the rectangle 121 colors[colorNum++ % colors.size()], 122 3, // thickness 123 cv::LINE_8 // line type 124 ); 125 126 } 127}

本当はコードを分割すべきなのですが、そうすると前方宣言がややこしいので避けました。
一応これで、顔の矩形と笑顔領域が取得できます。

謎仕様について

笑顔の領域が乱立しますが、これも仕様であるようです。
おそらく当該カスケードは次のように解釈した方がよさそうです。

  • × 笑顔を検出するカスケードファイル
  • 笑顔であると判断できるパーツのカスケードファイル

(未確認ですが、サンプルコードを見る限りそうとしか思えない)
ですので、笑顔検出がしたいのなら、こいつの検出個数を数えた方が良いみたいです。

追記

yohhoyさんがコメント欄で貼られているリンクをここにも置いておきます。

投稿2017/11/03 08:55

編集2017/11/06 07:07
LouiS0616

総合スコア35658

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

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

kotetu

2017/11/03 09:02

ありがとうございます!コードをコンパイルして、動かしてみますね。
LouiS0616

2017/11/03 09:05 編集

もう一度申し上げておきますと、このコードは『顔領域とその内部のわちゃわちゃ』を矩形で囲みます。 ですので、純粋な『笑顔検出器』としては機能していないことに注意してください。 また、検出時のパラメータは特に調整していないので精度は低いです。
yohhoy

2017/11/03 10:46

LouiS0616さんも指摘する通り、OpenCVの笑顔検出器は(おそらく)kotetuさんが期待する様な魔法の万能アルゴリズムでは全くありません。実際に行われているのは「カスケード分類器」というアルゴリズムに、「人の笑顔らしい微小パーツの特徴カタログ」を入力しているだけです。非常に高速に動作するという反面、人間の目には全く関係ない領域も多数HITするのは、このアルゴリズムと特徴カタログの性能そのものです。 http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82 より高精度な検出を行うには、他アルゴリズムや既知情報(人数や大きさなど)と組み合わせるか、機械学習技術などを用いた異なるアプローチが必要です。
LouiS0616

2017/11/03 11:11

OpenCV3.3からDNNも導入されましたし、やはり精度を追求するならNNですよね。 GPUのスペックが充分あるなら速度もそれなりに思えます。
LouiS0616

2017/11/03 11:11

説明に有用なリンクのご提供ありがとうございます、 コメント欄ではハイパーリンクが効かないので、回答に引用させていただきます。
kotetu

2017/11/06 06:44

コードをコンパイルしてみた所、';'がない事や、auto:faceのfaceがないと吐きました。
LouiS0616

2017/11/06 06:48

For-each文を使うには、コンパイラがC++11に対応している必要があります。
guest

0

知り合いからデュアルブートでも同じ現象になるのかと提案されたので、デュアルブートをしてみました。

デュアルブートをしたUbuntuでは、問題なく顔検出や笑顔検出されるようになりました。

今回の私が学んだ事としては、デュアルブートができる環境ではデュアルブートをする事をお勧めします。また、ネットワーク関係で少し苦労したので、関係ないかもしれませんが一応書いておきます。

IPアドレスなどの設定の際には、(私の場合は、有線でした。)Windwsのネットワーク設定の情報をそのままUbuntuの方で設定するといいでしょう。

投稿2017/11/08 15:38

編集2017/11/08 15:39
kotetu

総合スコア34

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問