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.