4.31 How to detect people in a cv::Mat image by using HOGDescriptor of OpenCV?

As for HOGDescriptor, we have used only cpu version cv::HOGDescriptor, not used gpu::HOGDescriptor in this program.
We have used the following DetectedImage::detect method to detect peoples in an original image. For details of cv::HOGDescriptor and cv::HOGDescriptor::detectMultiScale, see HOGDescriptor


  class DetectedImage ::public OpenCVImageView {
 
    void detect(DESCRIPTOR descriptor)
    {
      detectedImage  = originalImage.clone();
      cv::HOGDescriptor hog ;
  
      switch(descriptor) {
      case DEFAULT:
        //Set default values to member-variables of cv::HOGDescriptor
        hog.winSize     = cv::Size(64,128);
        hog.blockSize   = cv::Size(16, 16);
        hog.blockStride = cv::Size( 8,  8);
        hog.cellSize    = cv::Size( 8,  8);
        hog.nbins       = 9;
        
        //Use default people detector
        hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); 
        break;
        //
      }
      std::vector<cv::Rect> people;
      //Call detectMultiScale method of cv::HOGDescriptor.
      hog.detectMultiScale(grayImage, people);
      
      for (std::vector<cv::Rect>::const_iterator it = people.begin();
                          it != people.end(); ++it) {
        cv::Rect r = *it;
        //Draw bounding rectangles for detected people.
        cv::rectangle(detectedImage, r.tl(), r.br(), CV_RGB(255, 0, 0), 3);
      }
      refresh();
    }
  }
The following HOGPeopleDetector is a simple example to detect people in cv::Mat image. In this example, you can use one of the three PeopleDetector Default, Daimpler and UserDefined from the In practive, this is a very primitive method, but it gives a decent result for an object with a simple background as shown bellow.







/*
 * HOGPeopleDetector.cpp 
 * Copyright (c) 2018 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 */
//2018/04/10
//On cv::HOGDescriptor Struct, see:  https://docs.opencv.org/3.4.1/d5/d33/structcv_1_1HOGDescriptor.html
/*

cv::HOGDescriptor::HOGDescriptor ( Size  _winSize, //Detection window size. Align to block size and block stride. Default value is Size(64,128). 
  Size  _blockSize,                                //Block size in pixels. Align to cell size. Default value is Size(16,16). 
  Size  _blockStride,                              //Block stride. It must be a multiple of cell size. Default value is Size(8,8). 
  Size  _cellSize,                                 //Cell size. Default value is Size(8,8). 
  int  _nbins,                                     //Number of bins used in the calculation of histogram of gradients. Default value is 9. 
  int  _derivAperture = 1,                         //not documented ?
  double  _winSigma = -1,  
  int  _histogramNormType = HOGDescriptor::L2Hys,  //histogramNormType 
  double  _L2HysThreshold = 0.2,                   //L2-Hys normalization method shrinkage. 
  bool  _gammaCorrection = false,                  //Flag to specify whether the gamma correction preprocessing is required or not. 
  int  _nlevels = HOGDescriptor::DEFAULT_NLEVELS,  //Maximum number of detection window increases. Default value is 64. 
  bool  _signedGradient = false                    //Indicates signed gradient will be used or not. 
 )  

*/

/*
train_HOG.cpp
See also: https://docs.opencv.org/3.4.1/d5/d77/train_HOG_8cpp-example.html#a34
*/

#define _CONSOLE_

#include <sol/ModuleFileName.h>
#include <sol/DropFiles.h>
#include <sol/Pair.h>
#include <sol/LabeledComboBox.h>
#include <sol/FileDialog.h>
#include <sol/opencv/OpenCVApplicationView.h>
#include <sol/opencv/OpenCVImageView.h>

namespace SOL {

class MainView :public OpenCVApplicationView {

private:
  typedef enum {
    DEFAULT      = 0,
    DAIMLER      = 1,
    USER_DEFINED = 2
  } DESCRIPTOR; 
  
  ////////////////////////////////////////////////////////////////////////////////////////
  //Inner classes start.
  class OriginalImageView :public OpenCVImageView {
  private:
    cv::Mat originalImage;
    
    //This is a mandatory method, because in parent class it's declared
    //as a pure virtual function.
    cv::Mat& getMat()
    {
      return originalImage;
    }
    
    void display()
    {
      show(originalImage);
    }
    
  public:
    OriginalImageView(View* parent, const char* name, Args& args)
    :OpenCVImageView(parent, name, args)
    {
      try {
        const char* filename = (const char*)args.get(XmNimageFileName);
        int imageLoadingFlag = args.get(XmNimageLoadingFlag);
        loadImage(filename, imageLoadingFlag);
      } catch (SOL::Exception ex) {
        caught(ex);
      }
    }
    
    ~OriginalImageView()
    {
    }
    
    void loadImage(const char* filename, int imageLoadingFlag= CV_LOAD_IMAGE_COLOR)
    {
      try {
        originalImage = readImage(filename, imageLoadingFlag);
        refresh();
      } catch (Exception& ex) {
        caught(ex);
      }
    }    
  };
  
  class DetectedImageView  :public OpenCVImageView {
  private:

    cv::Mat originalImage;
    cv::Mat grayImage;
    cv::Mat detectedImage;
  
    cv::Mat& getMat()
    {
      return detectedImage;
    }
    
    void display()
    {
      show(detectedImage);
    }
    
  public:
    DetectedImageView(View* parent, const char* name, Args& args)
    :OpenCVImageView(parent, name, args)
    {
      try {
        const char* filename = (const char*)args.get(XmNimageFileName);
        int imageLoadingFlag = args.get(XmNimageLoadingFlag);
        loadImage(filename, imageLoadingFlag);

      } catch (SOL::Exception ex) {
        caught(ex);
      }
    }
    
    ~DetectedImageView()
    {
    }
    
    void loadImage(const char* filename, int imageLoadingFlag= CV_LOAD_IMAGE_COLOR)
    {
      try {
        originalImage = readImage(filename, imageLoadingFlag);
        //1 Convert it to a gray image.
        cv::cvtColor(originalImage, grayImage, COLOR_BGR2GRAY); 
        
        //2 Apply equalizeHist to the gray image.
        cv::equalizeHist(grayImage, grayImage);
        
        detectedImage  = originalImage.clone();
        refresh();
      } catch (Exception& ex) {
        caught(ex);
      }
    }

    void detect(DESCRIPTOR descriptor)
    {
      detectedImage  = originalImage.clone();

      //Please refer the method size_t HOGDescriptor::getDescriptorSize() const in opencv-3.4.1/module/objdetect/src/hog.cpp
      //Note the following restrictions on blockSize, winSize, blockStride and cellSize:
      // (blockSize.width % cellSize.width == 0 &&
      //  blockSize.height % cellSize.height == 0);
      //((winSize.width - blockSize.width) % blockStride.width == 0 && 
      // (winSize.height - blockSize.height) % blockStride.height == 0) 
      
      cv::HOGDescriptor hog ;
  
      switch(descriptor) {
      case DEFAULT:
        hog.winSize     = cv::Size(64,128);
        hog.blockSize   = cv::Size(16, 16);
        hog.blockStride = cv::Size( 8,  8);
        hog.cellSize    = cv::Size( 8,  8);
        hog.nbins       = 9;
        hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); 
        break;
        
      case DAIMLER:
        hog.winSize     = cv::Size(48, 96);
        hog.blockSize   = cv::Size(16, 16);
        hog.blockStride = cv::Size( 8,  8);
        hog.cellSize    = cv::Size( 8,  8);
        hog.nbins       = 9;
        hog.setSVMDetector(cv::HOGDescriptor::getDaimlerPeopleDetector()); 
        break;
        
      case USER_DEFINED:
        hog.winSize     = cv::Size(32,64);
        hog.blockSize   = cv::Size( 8, 8);
        hog.blockStride = cv::Size( 4, 4);
        hog.cellSize    = cv::Size( 4, 4);
        hog.nbins       = 9;
        hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); 
        break;
      }
  
      std::vector<cv::Rect> people;
      hog.detectMultiScale(grayImage, people);
      
      for (std::vector<cv::Rect>::const_iterator it = people.begin();
                          it != people.end(); ++it) {
        cv::Rect r = *it;
        cv::rectangle(detectedImage, r.tl(), r.br(), CV_RGB(255, 0, 0), 3);
      }
     
      refresh();
    }
  };
  //Inner classes end.
  ////////////////////////////////////////////////////////////////////////////////////////
  
  StringT<char>                imageFile;
  StringT<char>                savedImageFile;
  
  SmartPtr<OriginalImageView>  originalImage;
  SmartPtr<DetectedImageView>  detectedImage;

  DESCRIPTOR                   descriptorIndex;  
  SmartPtr<LabeledComboBox>    descriptorComboBox;

  FileDialog                   filedlg;

  const Pair<char*, DESCRIPTOR>* getDescriptorList(int& count)
  {
    static const Pair<char*, DESCRIPTOR> descriptors[] = {
      {"Default",     DEFAULT},   
      {"Daimler",     DAIMLER},
      {"UserDefined", USER_DEFINED},
    };
    
    count = CountOf(descriptors);
    return descriptors;
  }
  
  void updateCaption()
  {
    char caption[MAX_PATH];
    sprintf_s(caption, CountOf(caption), "%s - %s", 
        (const char*)imageFile, 
        getAppName() ); 
    setText(caption);
  }
  
  void resize(int w, int h)
  {
    if (originalImage && detectedImage) {
      originalImage      -> reshape(2,           2,  (w-160)/2-1,    h-4);
      detectedImage      -> reshape((w-160)/2+1, 2,  (w-160)/2-1,    h-4);
      descriptorComboBox -> reshape(w-160 + 4,   2,          140,     60);      
    }
  }

  //Callback for the descriptorComboBox
  void descriptorChanged(Action& action)
  {
    descriptorIndex = (DESCRIPTOR)descriptorComboBox->getCurSel();
    detectedImage -> detect(descriptorIndex);
  }

  void openFile(const char* filename)
  {
    try {
      originalImage   -> loadImage(filename);
      detectedImage  -> loadImage(filename);
      const char* fname = strrchr(filename, '\\');
      if (fname) {
        fname++;
      }
      imageFile = fname;
      updateCaption();

      savedImageFile = "";
      
      detectedImage->detect(descriptorIndex);
      
    } catch (Exception& ex) {
      caught(ex);
    }
  }
  
  void dropFiles(Action& action)
  {
    char fileName[MAX_PATH] = { 0 };
    DropFiles drop((HDROP)action.getWParam());
    //fileName[0] = ZERO;
    int num = drop.queryFile(0, fileName, CountOf(fileName));
    if(num > 0) {
      if (filedlg.isImageFileName(fileName)) {
        openFile(fileName);
        bringUp();
      } else {        
        bringUp(); //Activate and raise this view
        showErrorDialog("Invalid image filename", fileName,  MB_OK);
      }
    }    
  }

  void confirm(Action& action)
  {
    int rc = MessageBox(NULL, "Are you sure to close this window?", "Confirmation", 
                MB_OKCANCEL|MB_ICONEXCLAMATION);
    if (rc == IDOK) {
      exit(action);
    }
  }
  
public:
  MainView(OpenCVApplication& applet, const char* name, Args& args)
  :OpenCVApplicationView(applet, name, args)
  {
    try {
      imageFile = "..\\images\\Pedestrian.png";
      
      int  imageLoadingFlag = CV_LOAD_IMAGE_COLOR;
      Args ar;
      ar.set(XmNimageFileName, imageFile);
      ar.set(XmNimageLoadingFlag, imageLoadingFlag);
      originalImage = new OriginalImageView(this,  "", ar); //You don't need to specify windows name parameter 
      originalImage -> addCallback(XmNdropCallback, this,
        (Callback)&MainView::dropFiles, NULL);
      
      ar.reset();
      ar.set(XmNimageFileName, imageFile);
      ar.set(XmNimageLoadingFlag, imageLoadingFlag);
      detectedImage = new DetectedImageView(this, "", ar); //You don't need to specify windows name parameter

      ar.reset();
      ar.set(XmNstyle, CBS_DROPDOWNLIST);
      descriptorComboBox   = new LabeledComboBox(this, "HOG Descriptor", ar);
    
      int count = 0;
      const Pair<char*, DESCRIPTOR>* descriptors = getDescriptorList(count);

      for (int i =0; i<count; i++) {
        descriptorComboBox->addString(descriptors[i].first);
      }
      descriptorIndex  = USER_DEFINED;
      descriptorComboBox -> setCurSel((int)descriptorIndex);
      descriptorComboBox -> addCallback(XmNselChangeCallback, this,
        (Callback)&MainView::descriptorChanged, NULL);
      
      detectedImage -> detect(descriptorIndex);
    
      updateCaption();  

      ar.reset();
      ar.set(XmNfilter, FileDialog::getImageFilesFilter());
      filedlg.create(this, "OpenFile", ar);
      
    } catch (Exception& ex) {
      caught(ex);
    }
  }

  ~MainView()
  {
  }

  void open(Action& action)
  {
    Args ar;
    
    char dir[MAX_PATH] = { 0 };
    if (restoreFileFolder(dir, CountOf(dir))) {
      ar.set(XmNdirectory, dir);
      filedlg.setValues(ar);
    }
    
    try {    
      if(filedlg.open()) {
    
        const char* filename = filedlg.getFileName();
        saveFileFolder(filename);

        openFile(filename);
      }
    } catch (Exception& ex) {
      caught(ex);
    }
  }

  void save(Action& action)
  {
    try {
      if (!savedImageFile.isEmpty()) {
        detectedImage->writeImage(savedImageFile); 
      } else {
        saveAs(action);
      }
    } catch (Exception& ex) {
      caught(ex);
    }
  }  

  void saveAs(Action& action)
  {
    Args ar;
    
    char dir[MAX_PATH] = { 0 };
    if (restoreFileFolder(dir, CountOf(dir))) {
      ar.set(XmNdirectory, dir);
      filedlg.setValues(ar);
    }
    
    try {    
      if(filedlg.save()) {
    
        const char* filename = filedlg.getFileName();
        saveFileFolder(filename);
        
        detectedImage->writeImage(filename);
        savedImageFile = filename;
      }
    } catch (Exception& ex) {
      caught(ex);
    }
  }  
};
}

//
void main(int argc, char** argv) 
{
  try {
    ModuleFileName module(argv[0]);
    
    const char*  name = module.getAppName();
        
    OpenCVApplication applet(name, argc, argv);

    Args args;
    args.set(XmNwidth,  900);
    args.set(XmNheight, 400);  
    MainView view(applet, name, args);
    view.realize();

    applet.run();
    
  } catch (SOL::Exception& ex) {
    caught(ex);
  }
}


Last modified: 15 Apr. 2018

Copyright (c) 2018 Antillia.com ALL RIGHTS RESERVED.