回答編集履歴

2 無駄なコピーが生じていた部分を修正

LouiS0616

LouiS0616 score 35024

2017/11/06 16:07  投稿

原因
---
[ご提示のサンプルコード](https://github.com/opencv/opencv/blob/master/samples/cpp/smiledetect.cpp)を動かしてみました。
確かに質問者様と同じような状態になりましたが、これは**仕様**なようです。
> ```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++
#include <iostream>
#include <array>
#include <vector>
#include <opencv2/opencv.hpp>
const static std::array<cv::Scalar, 8> colors = {
   //         B   G   R
   cv::Scalar(255,  0,  0), // Blue
   cv::Scalar(255, 128,  0),
   cv::Scalar(255, 255,  0), // Cyan
   cv::Scalar( 0, 255,  0), // Green
   cv::Scalar( 0, 128, 255),
   cv::Scalar( 0, 255, 255), // Yellow
   cv::Scalar( 0,  0, 255), // Red
   cv::Scalar(255,  0, 255)  // Magenta
};
void detectFacesAndSmiles(
   const cv::Mat &grayImg, cv::Mat &dst,
   cv::CascadeClassifier& faceCascade,
   cv::CascadeClassifier& smileCascade
);
void detectAndDraw(
   const cv::Mat& grayImg, cv::Mat &dst,
   cv::CascadeClassifier& cascade,
   std::vector<cv::Rect>& detected = std::vector<cv::Rect>()
);
int main(void)
{
   // Load cascades
   cv::CascadeClassifier faceCascade, smileCascade;
   std::string faceCascadeName = "haarcascade_frontalface_alt.xml";
   if(!faceCascade.load(faceCascadeName)) {
       std::cerr << "ERROR: Could not load face cascade" << std::endl;
       return -1;
   }
   std::string smileCascadeName = "haarcascade_smile.xml";
   if(!smileCascade.load(smileCascadeName)) {
       std::cerr << "ERROR: Could not load smile cascade" << std::endl;
       return -1;
   }
   // Open web camera
   cv::VideoCapture capture(0);
   if(!capture.isOpened()) {
       std::cerr << "ERROR: Could not initiate capture" << std::endl;
       return -1;
   }
   // Main Process
   std::cout << "Video capturing has been started ..." << std::endl;
   cv::Mat frame, dst, gray;
   while(true) {
       capture >> frame;
       detectFacesAndSmiles(frame, dst, faceCascade, smileCascade);
       cv::imshow("Result", dst);
       char c = cv::waitKey(10);
       if(c == 27 || c == 'q' || c == 'Q') {
           break;
       }
   }
   return 0;
}
void detectFacesAndSmiles(
   const cv::Mat &img, cv::Mat &dst,
   cv::CascadeClassifier& faceCascade,
   cv::CascadeClassifier& smileCascade)
{
   // Prepare images
   cv::Mat gray;
   
   dst = img.clone();
   cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
   // Detect faces
   std::vector<cv::Rect> detectedFaces;
   detectAndDraw(
       gray, dst, faceCascade, detectedFaces
   );
   // Detect Smiles
   int colorNum = 0;
   for(auto face: detectedFaces) {
   for(const auto& face: detectedFaces) {
       cv::Scalar color = colors[colorNum++ % colors.size()];
       detectAndDraw(
           gray(face), dst(face), smileCascade
       );
   }
}
void detectAndDraw(
   const cv::Mat& grayImg, cv::Mat& dst,
   cv::CascadeClassifier& cascade,
   std::vector<cv::Rect>& detected)
{
   // Detect
   cascade.detectMultiScale(
       grayImg,           // image
       detected,          // dst objects
       1.1,               // scale factor
       2,                 // min neighbors
       0,                 // flag
       cv::Size(40, 40)   // min size
   );
   // Draw rectangle
   int colorNum = 0;
   for(auto face: detected) {
       cv::rectangle(
           dst, 
           cv::Point(face.x, face.y),                             
           cv::Point(face.x + face.width, face.y + face.height),
                       // Vertex of the rectangle
           colors[colorNum++ % colors.size()],
           3,         // thickness
           cv::LINE_8 // line type
       );
   }
}
```
本当はコードを分割すべきなのですが、そうすると前方宣言がややこしいので避けました。
一応これで、顔の矩形と笑顔領域が取得できます。
謎仕様について
---
**笑顔の領域が乱立しますが、これも仕様であるようです。**
おそらく当該カスケードは次のように解釈した方がよさそうです。
- **×** 笑顔を検出するカスケードファイル
- **○** 笑顔であると判断できるパーツのカスケードファイル
(未確認ですが、サンプルコードを見る限りそうとしか思えない)
ですので、笑顔検出がしたいのなら、こいつの検出個数を数えた方が良いみたいです。
追記
---
yohhoyさんがコメント欄で貼られているリンクをここにも置いておきます。
- [http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html](http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html)
- [https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82](https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82)
1 追記

LouiS0616

LouiS0616 score 35024

2017/11/03 20:13  投稿

原因
---
[ご提示のサンプルコード](https://github.com/opencv/opencv/blob/master/samples/cpp/smiledetect.cpp)を動かしてみました。
確かに質問者様と同じような状態になりましたが、これは**仕様**なようです。
> ```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++
#include <iostream>
#include <array>
#include <vector>
#include <opencv2/opencv.hpp>
const static std::array<cv::Scalar, 8> colors = {
   //         B   G   R
   cv::Scalar(255,  0,  0), // Blue
   cv::Scalar(255, 128,  0),
   cv::Scalar(255, 255,  0), // Cyan
   cv::Scalar( 0, 255,  0), // Green
   cv::Scalar( 0, 128, 255),
   cv::Scalar( 0, 255, 255), // Yellow
   cv::Scalar( 0,  0, 255), // Red
   cv::Scalar(255,  0, 255)  // Magenta
};
void detectFacesAndSmiles(
   const cv::Mat &grayImg, cv::Mat &dst,
   cv::CascadeClassifier& faceCascade,
   cv::CascadeClassifier& smileCascade
);
void detectAndDraw(
   const cv::Mat& grayImg, cv::Mat &dst,
   cv::CascadeClassifier& cascade,
   std::vector<cv::Rect>& detected = std::vector<cv::Rect>()
);
int main(void)
{
   // Load cascades
   cv::CascadeClassifier faceCascade, smileCascade;
   std::string faceCascadeName = "haarcascade_frontalface_alt.xml";
   if(!faceCascade.load(faceCascadeName)) {
       std::cerr << "ERROR: Could not load face cascade" << std::endl;
       return -1;
   }
   std::string smileCascadeName = "haarcascade_smile.xml";
   if(!smileCascade.load(smileCascadeName)) {
       std::cerr << "ERROR: Could not load smile cascade" << std::endl;
       return -1;
   }
   // Open web camera
   cv::VideoCapture capture(0);
   if(!capture.isOpened()) {
       std::cerr << "ERROR: Could not initiate capture" << std::endl;
       return -1;
   }
   // Main Process
   std::cout << "Video capturing has been started ..." << std::endl;
   cv::Mat frame, dst, gray;
   while(true) {
       capture >> frame;
       detectFacesAndSmiles(frame, dst, faceCascade, smileCascade);
       cv::imshow("Result", dst);
       char c = cv::waitKey(10);
       if(c == 27 || c == 'q' || c == 'Q') {
           break;
       }
   }
   return 0;
}
void detectFacesAndSmiles(
   const cv::Mat &img, cv::Mat &dst,
   cv::CascadeClassifier& faceCascade,
   cv::CascadeClassifier& smileCascade)
{
   // Prepare images
   cv::Mat gray;
   
   dst = img.clone();
   cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
   // Detect faces
   std::vector<cv::Rect> detectedFaces;
   detectAndDraw(
       gray, dst, faceCascade, detectedFaces
   );
   // Detect Smiles
   int colorNum = 0;
   for(auto face: detectedFaces) {
       cv::Scalar color = colors[colorNum++ % colors.size()];
       detectAndDraw(
           gray(face), dst(face), smileCascade
       );
   }
}
void detectAndDraw(
   const cv::Mat& grayImg, cv::Mat& dst,
   cv::CascadeClassifier& cascade,
   std::vector<cv::Rect>& detected)
{
   // Detect
   cascade.detectMultiScale(
       grayImg,           // image
       detected,          // dst objects
       1.1,               // scale factor
       2,                 // min neighbors
       0,                 // flag
       cv::Size(40, 40)   // min size
   );
   // Draw rectangle
   int colorNum = 0;
   for(auto face: detected) {
       cv::rectangle(
           dst, 
           cv::Point(face.x, face.y),                             
           cv::Point(face.x + face.width, face.y + face.height),
                       // Vertex of the rectangle
           colors[colorNum++ % colors.size()],
           3,         // thickness
           cv::LINE_8 // line type
       );
   }
}
```
本当はコードを分割すべきなのですが、そうすると前方宣言がややこしいので避けました。
一応これで、顔の矩形と笑顔領域が取得できます。
謎仕様について
---
**笑顔の領域が乱立しますが、これも仕様であるようです。**
おそらく当該カスケードは次のように解釈した方がよさそうです。
- **×** 笑顔を検出するカスケードファイル
- **○** 笑顔であると判断できるパーツのカスケードファイル
(未確認ですが、サンプルコードを見る限りそうとしか思えない)
ですので、笑顔検出がしたいのなら、こいつの検出個数を数えた方が良いみたいです。
ですので、笑顔検出がしたいのなら、こいつの検出個数を数えた方が良いみたいです。
追記
---
yohhoyさんがコメント欄で貼られているリンクをここにも置いておきます。
- [http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html](http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html)
- [https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82](https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82)

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る