Move objdetect HaarCascadeClassifier and HOGDescriptor to contrib xobjdetect (#25198)
* Move objdetect parts to contrib * Move objdetect parts to contrib * Minor fixes.
@ -1036,7 +1036,7 @@ ocv_register_modules()
|
||||
add_subdirectory(doc)
|
||||
|
||||
# various data that is used by cv libraries and/or demo applications.
|
||||
add_subdirectory(data)
|
||||
# add_subdirectory(data)
|
||||
|
||||
# extra applications
|
||||
if(BUILD_opencv_apps)
|
||||
|
@ -53,8 +53,6 @@ macro(ocv_add_app directory)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
ocv_add_app(annotation)
|
||||
ocv_add_app(visualisation)
|
||||
ocv_add_app(interactive-calibration)
|
||||
ocv_add_app(version)
|
||||
ocv_add_app(model-diagnostics)
|
||||
|
@ -1,3 +0,0 @@
|
||||
ocv_add_application(opencv_annotation
|
||||
MODULES opencv_core opencv_highgui opencv_imgproc opencv_imgcodecs opencv_videoio
|
||||
SRCS opencv_annotation.cpp)
|
@ -1,317 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*****************************************************************************************************
|
||||
USAGE:
|
||||
./opencv_annotation -images <folder location> -annotations <output file>
|
||||
|
||||
Created by: Puttemans Steven - February 2015
|
||||
Adapted by: Puttemans Steven - April 2016 - Vectorize the process to enable better processing
|
||||
+ early leave and store by pressing an ESC key
|
||||
+ enable delete `d` button, to remove last annotation
|
||||
*****************************************************************************************************/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// Function prototypes
|
||||
void on_mouse(int, int, int, int, void*);
|
||||
vector<Rect> get_annotations(Mat);
|
||||
|
||||
// Public parameters
|
||||
Mat image;
|
||||
int roi_x0 = 0, roi_y0 = 0, roi_x1 = 0, roi_y1 = 0, num_of_rec = 0;
|
||||
bool start_draw = false, stop = false;
|
||||
|
||||
// Window name for visualisation purposes
|
||||
const string window_name = "OpenCV Based Annotation Tool";
|
||||
|
||||
// FUNCTION : Mouse response for selecting objects in images
|
||||
// If left button is clicked, start drawing a rectangle as long as mouse moves
|
||||
// Stop drawing once a new left click is detected by the on_mouse function
|
||||
void on_mouse(int event, int x, int y, int , void * )
|
||||
{
|
||||
// Action when left button is clicked
|
||||
if(event == EVENT_LBUTTONDOWN)
|
||||
{
|
||||
if(!start_draw)
|
||||
{
|
||||
roi_x0 = x;
|
||||
roi_y0 = y;
|
||||
start_draw = true;
|
||||
} else {
|
||||
roi_x1 = x;
|
||||
roi_y1 = y;
|
||||
start_draw = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Action when mouse is moving and drawing is enabled
|
||||
if((event == EVENT_MOUSEMOVE) && start_draw)
|
||||
{
|
||||
// Redraw bounding box for annotation
|
||||
Mat current_view;
|
||||
image.copyTo(current_view);
|
||||
rectangle(current_view, Point(roi_x0,roi_y0), Point(x,y), Scalar(0,0,255));
|
||||
imshow(window_name, current_view);
|
||||
}
|
||||
}
|
||||
|
||||
// FUNCTION : returns a vector of Rect objects given an image containing positive object instances
|
||||
vector<Rect> get_annotations(Mat input_image)
|
||||
{
|
||||
vector<Rect> current_annotations;
|
||||
|
||||
// Make it possible to exit the annotation process
|
||||
stop = false;
|
||||
|
||||
// Init window interface and couple mouse actions
|
||||
namedWindow(window_name, WINDOW_AUTOSIZE);
|
||||
setMouseCallback(window_name, on_mouse);
|
||||
|
||||
image = input_image;
|
||||
imshow(window_name, image);
|
||||
int key_pressed = 0;
|
||||
|
||||
do
|
||||
{
|
||||
// Get a temporary image clone
|
||||
Mat temp_image = input_image.clone();
|
||||
Rect currentRect(0, 0, 0, 0);
|
||||
|
||||
// Keys for processing
|
||||
// You need to select one for confirming a selection and one to continue to the next image
|
||||
// Based on the universal ASCII code of the keystroke: http://www.asciitable.com/
|
||||
// c = 99 add rectangle to current image
|
||||
// n = 110 save added rectangles and show next image
|
||||
// d = 100 delete the last annotation made
|
||||
// <ESC> = 27 exit program
|
||||
key_pressed = 0xFF & waitKey(0);
|
||||
switch( key_pressed )
|
||||
{
|
||||
case 27:
|
||||
stop = true;
|
||||
break;
|
||||
case 99:
|
||||
// Draw initiated from top left corner
|
||||
if(roi_x0<roi_x1 && roi_y0<roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x0;
|
||||
currentRect.y = roi_y0;
|
||||
currentRect.width = roi_x1-roi_x0;
|
||||
currentRect.height = roi_y1-roi_y0;
|
||||
}
|
||||
// Draw initiated from bottom right corner
|
||||
if(roi_x0>roi_x1 && roi_y0>roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x1;
|
||||
currentRect.y = roi_y1;
|
||||
currentRect.width = roi_x0-roi_x1;
|
||||
currentRect.height = roi_y0-roi_y1;
|
||||
}
|
||||
// Draw initiated from top right corner
|
||||
if(roi_x0>roi_x1 && roi_y0<roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x1;
|
||||
currentRect.y = roi_y0;
|
||||
currentRect.width = roi_x0-roi_x1;
|
||||
currentRect.height = roi_y1-roi_y0;
|
||||
}
|
||||
// Draw initiated from bottom left corner
|
||||
if(roi_x0<roi_x1 && roi_y0>roi_y1)
|
||||
{
|
||||
currentRect.x = roi_x0;
|
||||
currentRect.y = roi_y1;
|
||||
currentRect.width = roi_x1-roi_x0;
|
||||
currentRect.height = roi_y0-roi_y1;
|
||||
}
|
||||
// Draw the rectangle on the canvas
|
||||
// Add the rectangle to the vector of annotations
|
||||
current_annotations.push_back(currentRect);
|
||||
break;
|
||||
case 100:
|
||||
// Remove the last annotation
|
||||
if(current_annotations.size() > 0){
|
||||
current_annotations.pop_back();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Default case --> do nothing at all
|
||||
// Other keystrokes can simply be ignored
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if escape has been pressed
|
||||
if(stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw all the current rectangles onto the top image and make sure that the global image is linked
|
||||
for(int i=0; i < (int)current_annotations.size(); i++){
|
||||
rectangle(temp_image, current_annotations[i], Scalar(0,255,0), 1);
|
||||
}
|
||||
image = temp_image;
|
||||
|
||||
// Force an explicit redraw of the canvas --> necessary to visualize delete correctly
|
||||
imshow(window_name, image);
|
||||
}
|
||||
// Continue as long as the next image key has not been pressed
|
||||
while(key_pressed != 110);
|
||||
|
||||
// Close down the window
|
||||
destroyWindow(window_name);
|
||||
|
||||
// Return the data
|
||||
return current_annotations;
|
||||
}
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
// Use the cmdlineparser to process input arguments
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | show this message }"
|
||||
"{ images i | | (required) path to image folder [example - /data/testimages/] }"
|
||||
"{ annotations a | | (required) path to annotations txt file [example - /data/annotations.txt] }"
|
||||
"{ maxWindowHeight m | -1 | (optional) images larger in height than this value will be scaled down }"
|
||||
"{ resizeFactor r | 2 | (optional) factor for scaling down [default = half the size] }"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return 0;
|
||||
}
|
||||
string image_folder(parser.get<string>("images"));
|
||||
string annotations_file(parser.get<string>("annotations"));
|
||||
if (image_folder.empty() || annotations_file.empty()){
|
||||
parser.printMessage();
|
||||
cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int resizeFactor = parser.get<int>("resizeFactor");
|
||||
int const maxWindowHeight = parser.get<int>("maxWindowHeight") > 0 ? parser.get<int>("maxWindowHeight") : -1;
|
||||
|
||||
// Start by processing the data
|
||||
// Return the image filenames inside the image folder
|
||||
map< String, vector<Rect> > annotations;
|
||||
vector<String> filenames;
|
||||
String folder(image_folder);
|
||||
glob(folder, filenames);
|
||||
|
||||
// Add key tips on how to use the software when running it
|
||||
cout << "* mark rectangles with the left mouse button," << endl;
|
||||
cout << "* press 'c' to accept a selection," << endl;
|
||||
cout << "* press 'd' to delete the latest selection," << endl;
|
||||
cout << "* press 'n' to proceed with next image," << endl;
|
||||
cout << "* press 'esc' to stop." << endl;
|
||||
|
||||
// Loop through each image stored in the images folder
|
||||
// Create and temporarily store the annotations
|
||||
// At the end write everything to the annotations file
|
||||
for (size_t i = 0; i < filenames.size(); i++){
|
||||
// Read in an image
|
||||
Mat current_image = imread(filenames[i]);
|
||||
bool const resize_bool = (maxWindowHeight > 0) && (current_image.rows > maxWindowHeight);
|
||||
|
||||
// Check if the image is actually read - avoid other files in the folder, because glob() takes them all
|
||||
// If not then simply skip this iteration
|
||||
if(current_image.empty()){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(resize_bool){
|
||||
resize(current_image, current_image, Size(current_image.cols/resizeFactor, current_image.rows/resizeFactor), 0, 0, INTER_LINEAR_EXACT);
|
||||
}
|
||||
|
||||
// Perform annotations & store the result inside the vectorized structure
|
||||
// If the image was resized before, then resize the found annotations back to original dimensions
|
||||
vector<Rect> current_annotations = get_annotations(current_image);
|
||||
if(resize_bool){
|
||||
for(int j =0; j < (int)current_annotations.size(); j++){
|
||||
current_annotations[j].x = current_annotations[j].x * resizeFactor;
|
||||
current_annotations[j].y = current_annotations[j].y * resizeFactor;
|
||||
current_annotations[j].width = current_annotations[j].width * resizeFactor;
|
||||
current_annotations[j].height = current_annotations[j].height * resizeFactor;
|
||||
}
|
||||
}
|
||||
annotations[filenames[i]] = current_annotations;
|
||||
|
||||
// Check if the ESC key was hit, then exit earlier then expected
|
||||
if(stop){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// When all data is processed, store the data gathered inside the proper file
|
||||
// This now even gets called when the ESC button was hit to store preliminary results
|
||||
ofstream output(annotations_file.c_str());
|
||||
if ( !output.is_open() ){
|
||||
cerr << "The path for the output file contains an error and could not be opened. Please check again!" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Store the annotations, write to the output file
|
||||
for(map<String, vector<Rect> >::iterator it = annotations.begin(); it != annotations.end(); it++){
|
||||
vector<Rect> &anno = it->second;
|
||||
output << it->first << " " << anno.size();
|
||||
for(size_t j=0; j < anno.size(); j++){
|
||||
Rect temp = anno[j];
|
||||
output << " " << temp.x << " " << temp.y << " " << temp.width << " " << temp.height;
|
||||
}
|
||||
output << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
ocv_add_application(opencv_visualisation
|
||||
MODULES opencv_core opencv_highgui opencv_imgproc opencv_videoio opencv_imgcodecs
|
||||
SRCS opencv_visualisation.cpp)
|
@ -1,378 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*****************************************************************************************************
|
||||
|
||||
Software for visualising cascade classifier models trained by OpenCV and to get a better
|
||||
understanding of the used features.
|
||||
|
||||
USAGE:
|
||||
./opencv_visualisation --model=<model.xml> --image=<ref.png> --data=<video output folder>
|
||||
|
||||
Created by: Puttemans Steven - April 2016
|
||||
*****************************************************************************************************/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
struct rect_data{
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
float weight;
|
||||
};
|
||||
|
||||
static void printLimits(){
|
||||
cerr << "Limits of the current interface:" << endl;
|
||||
cerr << " - Only handles cascade classifier models, trained with the opencv_traincascade tool, containing stumps as decision trees [default settings]." << endl;
|
||||
cerr << " - The image provided needs to be a sample window with the original model dimensions, passed to the --image parameter." << endl;
|
||||
cerr << " - ONLY handles HAAR and LBP features." << endl;
|
||||
}
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ help h usage ? | | show this message }"
|
||||
"{ image i | | (required) path to reference image }"
|
||||
"{ model m | | (required) path to cascade xml file }"
|
||||
"{ data d | | (optional) path to video output folder }"
|
||||
"{ ext | avi | (optional) output video file extension e.g. avi (default) or mp4 }"
|
||||
"{ fourcc | XVID | (optional) output video file's 4-character codec e.g. XVID (default) or H264 }"
|
||||
"{ fps | 15 | (optional) output video file's frames-per-second rate }"
|
||||
);
|
||||
// Read in the input arguments
|
||||
if (parser.has("help")){
|
||||
parser.printMessage();
|
||||
printLimits();
|
||||
return 0;
|
||||
}
|
||||
string model(parser.get<string>("model"));
|
||||
string output_folder(parser.get<string>("data"));
|
||||
string image_ref = (parser.get<string>("image"));
|
||||
string fourcc = (parser.get<string>("fourcc"));
|
||||
int fps = parser.get<int>("fps");
|
||||
if (model.empty() || image_ref.empty() || fourcc.size()!=4 || fps<1){
|
||||
parser.printMessage();
|
||||
printLimits();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Value for timing
|
||||
// You can increase this to have a better visualisation during the generation
|
||||
int timing = 1;
|
||||
|
||||
// Value for cols of storing elements
|
||||
int cols_prefered = 5;
|
||||
|
||||
// Open the XML model
|
||||
FileStorage fs;
|
||||
bool model_ok = fs.open(model, FileStorage::READ);
|
||||
if (!model_ok){
|
||||
cerr << "the cascade file '" << model << "' could not be loaded." << endl;
|
||||
return -1;
|
||||
}
|
||||
// Get a the required information
|
||||
// First decide which feature type we are using
|
||||
FileNode cascade = fs["cascade"];
|
||||
string feature_type = cascade["featureType"];
|
||||
bool haar = false, lbp = false;
|
||||
if (feature_type.compare("HAAR") == 0){
|
||||
haar = true;
|
||||
}
|
||||
if (feature_type.compare("LBP") == 0){
|
||||
lbp = true;
|
||||
}
|
||||
if ( feature_type.compare("HAAR") != 0 && feature_type.compare("LBP")){
|
||||
cerr << "The model is not an HAAR or LBP feature based model!" << endl;
|
||||
cerr << "Please select a model that can be visualized by the software." << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We make a visualisation mask - which increases the window to make it at least a bit more visible
|
||||
int resize_factor = 10;
|
||||
int resize_storage_factor = 10;
|
||||
Mat reference_image = imread(image_ref, IMREAD_GRAYSCALE );
|
||||
if (reference_image.empty()){
|
||||
cerr << "the reference image '" << image_ref << "'' could not be loaded." << endl;
|
||||
return -1;
|
||||
}
|
||||
Mat visualization;
|
||||
resize(reference_image, visualization, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), 0, 0, INTER_LINEAR_EXACT);
|
||||
|
||||
// First recover for each stage the number of weak features and their index
|
||||
// Important since it is NOT sequential when using LBP features
|
||||
vector< vector<int> > stage_features;
|
||||
FileNode stages = cascade["stages"];
|
||||
FileNodeIterator it_stages = stages.begin(), it_stages_end = stages.end();
|
||||
int idx = 0;
|
||||
for( ; it_stages != it_stages_end; it_stages++, idx++ ){
|
||||
vector<int> current_feature_indexes;
|
||||
FileNode weak_classifiers = (*it_stages)["weakClassifiers"];
|
||||
FileNodeIterator it_weak = weak_classifiers.begin(), it_weak_end = weak_classifiers.end();
|
||||
vector<int> values;
|
||||
for(int idy = 0; it_weak != it_weak_end; it_weak++, idy++ ){
|
||||
(*it_weak)["internalNodes"] >> values;
|
||||
current_feature_indexes.push_back( (int)values[2] );
|
||||
}
|
||||
stage_features.push_back(current_feature_indexes);
|
||||
}
|
||||
|
||||
// If the output option has been chosen than we will store a combined image plane for
|
||||
// each stage, containing all weak classifiers for that stage.
|
||||
bool draw_planes = false;
|
||||
stringstream output_video;
|
||||
output_video << output_folder << "model_visualization." << parser.get<string>("ext");
|
||||
VideoWriter result_video;
|
||||
if( output_folder.compare("") != 0 ){
|
||||
draw_planes = true;
|
||||
result_video.open(output_video.str(), VideoWriter::fourcc(fourcc[0],fourcc[1],fourcc[2],fourcc[3]), fps, visualization.size(), false);
|
||||
if (!result_video.isOpened()){
|
||||
cerr << "the output video '" << output_video.str() << "' could not be opened."
|
||||
<< " fourcc=" << fourcc
|
||||
<< " fps=" << fps
|
||||
<< " frameSize=" << visualization.size()
|
||||
<< endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(haar){
|
||||
// Grab the corresponding features dimensions and weights
|
||||
FileNode features = cascade["features"];
|
||||
vector< vector< rect_data > > feature_data;
|
||||
FileNodeIterator it_features = features.begin(), it_features_end = features.end();
|
||||
for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
|
||||
vector< rect_data > current_feature_rectangles;
|
||||
FileNode rectangles = (*it_features)["rects"];
|
||||
int nrects = (int)rectangles.size();
|
||||
for(int k = 0; k < nrects; k++){
|
||||
rect_data current_data;
|
||||
FileNode single_rect = rectangles[k];
|
||||
current_data.x = (int)single_rect[0];
|
||||
current_data.y = (int)single_rect[1];
|
||||
current_data.w = (int)single_rect[2];
|
||||
current_data.h = (int)single_rect[3];
|
||||
current_data.weight = (float)single_rect[4];
|
||||
current_feature_rectangles.push_back(current_data);
|
||||
}
|
||||
feature_data.push_back(current_feature_rectangles);
|
||||
}
|
||||
|
||||
// Loop over each possible feature on its index, visualise on the mask and wait a bit,
|
||||
// then continue to the next feature.
|
||||
// If visualisations should be stored then do the in between calculations
|
||||
Mat image_plane;
|
||||
Mat metadata = Mat::zeros(150, 1000, CV_8UC1);
|
||||
vector< rect_data > current_rects;
|
||||
for(int sid = 0; sid < (int)stage_features.size(); sid ++){
|
||||
if(draw_planes){
|
||||
int features_nmbr = (int)stage_features[sid].size();
|
||||
int cols = cols_prefered;
|
||||
int rows = features_nmbr / cols;
|
||||
if( (features_nmbr % cols) > 0){
|
||||
rows++;
|
||||
}
|
||||
image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);
|
||||
}
|
||||
for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
|
||||
stringstream meta1, meta2;
|
||||
meta1 << "Stage " << sid << " / Feature " << fid;
|
||||
meta2 << "Rectangles: ";
|
||||
Mat temp_window = visualization.clone();
|
||||
Mat temp_metadata = metadata.clone();
|
||||
int current_feature_index = stage_features[sid][fid];
|
||||
current_rects = feature_data[current_feature_index];
|
||||
Mat single_feature = reference_image.clone();
|
||||
resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);
|
||||
for(int i = 0; i < (int)current_rects.size(); i++){
|
||||
rect_data local = current_rects[i];
|
||||
if(draw_planes){
|
||||
if(local.weight >= 0){
|
||||
rectangle(single_feature, Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), Scalar(0), FILLED);
|
||||
}else{
|
||||
rectangle(single_feature, Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), Scalar(255), FILLED);
|
||||
}
|
||||
}
|
||||
Rect part(local.x * resize_factor, local.y * resize_factor, local.w * resize_factor, local.h * resize_factor);
|
||||
meta2 << part << " (w " << local.weight << ") ";
|
||||
if(local.weight >= 0){
|
||||
rectangle(temp_window, part, Scalar(0), FILLED);
|
||||
}else{
|
||||
rectangle(temp_window, part, Scalar(255), FILLED);
|
||||
}
|
||||
}
|
||||
imshow("features", temp_window);
|
||||
putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
result_video.write(temp_window);
|
||||
// Copy the feature image if needed
|
||||
if(draw_planes){
|
||||
single_feature.copyTo(image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
|
||||
}
|
||||
putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
imshow("metadata", temp_metadata);
|
||||
waitKey(timing);
|
||||
}
|
||||
//Store the stage image if needed
|
||||
if(draw_planes){
|
||||
stringstream save_location;
|
||||
save_location << output_folder << "stage_" << sid << ".png";
|
||||
imwrite(save_location.str(), image_plane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(lbp){
|
||||
// Grab the corresponding features dimensions and weights
|
||||
FileNode features = cascade["features"];
|
||||
vector<Rect> feature_data;
|
||||
FileNodeIterator it_features = features.begin(), it_features_end = features.end();
|
||||
for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
|
||||
FileNode rectangle = (*it_features)["rect"];
|
||||
Rect current_feature ((int)rectangle[0], (int)rectangle[1], (int)rectangle[2], (int)rectangle[3]);
|
||||
feature_data.push_back(current_feature);
|
||||
}
|
||||
|
||||
// Loop over each possible feature on its index, visualise on the mask and wait a bit,
|
||||
// then continue to the next feature.
|
||||
Mat image_plane;
|
||||
Mat metadata = Mat::zeros(150, 1000, CV_8UC1);
|
||||
for(int sid = 0; sid < (int)stage_features.size(); sid ++){
|
||||
if(draw_planes){
|
||||
int features_nmbr = (int)stage_features[sid].size();
|
||||
int cols = cols_prefered;
|
||||
int rows = features_nmbr / cols;
|
||||
if( (features_nmbr % cols) > 0){
|
||||
rows++;
|
||||
}
|
||||
image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);
|
||||
}
|
||||
for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
|
||||
stringstream meta1, meta2;
|
||||
meta1 << "Stage " << sid << " / Feature " << fid;
|
||||
meta2 << "Rectangle: ";
|
||||
Mat temp_window = visualization.clone();
|
||||
Mat temp_metadata = metadata.clone();
|
||||
int current_feature_index = stage_features[sid][fid];
|
||||
Rect current_rect = feature_data[current_feature_index];
|
||||
Mat single_feature = reference_image.clone();
|
||||
resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);
|
||||
|
||||
// VISUALISATION
|
||||
// The rectangle is the top left one of a 3x3 block LBP constructor
|
||||
Rect resized(current_rect.x * resize_factor, current_rect.y * resize_factor, current_rect.width * resize_factor, current_rect.height * resize_factor);
|
||||
meta2 << resized;
|
||||
// Top left
|
||||
rectangle(temp_window, resized, Scalar(255), 1);
|
||||
// Top middle
|
||||
rectangle(temp_window, Rect(resized.x + resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);
|
||||
// Top right
|
||||
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);
|
||||
// Middle left
|
||||
rectangle(temp_window, Rect(resized.x, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Middle middle
|
||||
rectangle(temp_window, Rect(resized.x + resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), FILLED);
|
||||
// Middle right
|
||||
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Bottom left
|
||||
rectangle(temp_window, Rect(resized.x, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Bottom middle
|
||||
rectangle(temp_window, Rect(resized.x + resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
// Bottom right
|
||||
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
|
||||
|
||||
if(draw_planes){
|
||||
Rect resized_inner(current_rect.x * resize_storage_factor, current_rect.y * resize_storage_factor, current_rect.width * resize_storage_factor, current_rect.height * resize_storage_factor);
|
||||
// Top left
|
||||
rectangle(single_feature, resized_inner, Scalar(255), 1);
|
||||
// Top middle
|
||||
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Top right
|
||||
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Middle left
|
||||
rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Middle middle
|
||||
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), FILLED);
|
||||
// Middle right
|
||||
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Bottom left
|
||||
rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Bottom middle
|
||||
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
// Bottom right
|
||||
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
|
||||
|
||||
single_feature.copyTo(image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
|
||||
}
|
||||
|
||||
putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
imshow("metadata", temp_metadata);
|
||||
imshow("features", temp_window);
|
||||
putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
|
||||
result_video.write(temp_window);
|
||||
|
||||
waitKey(timing);
|
||||
}
|
||||
|
||||
//Store the stage image if needed
|
||||
if(draw_planes){
|
||||
stringstream save_location;
|
||||
save_location << output_folder << "stage_" << sid << ".png";
|
||||
imwrite(save_location.str(), image_plane);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
file(GLOB HAAR_CASCADES haarcascades/*.xml)
|
||||
file(GLOB LBP_CASCADES lbpcascades/*.xml)
|
||||
|
||||
install(FILES ${HAAR_CASCADES} DESTINATION ${OPENCV_OTHER_INSTALL_PATH}/haarcascades COMPONENT libs)
|
||||
install(FILES ${LBP_CASCADES} DESTINATION ${OPENCV_OTHER_INSTALL_PATH}/lbpcascades COMPONENT libs)
|
||||
|
||||
if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH)
|
||||
install(DIRECTORY "${OPENCV_TEST_DATA_PATH}/" DESTINATION "${OPENCV_TEST_DATA_INSTALL_PATH}" COMPONENT "tests")
|
||||
endif()
|
@ -1,7 +0,0 @@
|
||||
This folder contains various data that is used by cv libraries and/or demo applications.
|
||||
----------------------------------------------------------------------------------------
|
||||
|
||||
haarcascades - the folder contains trained classifiers for detecting objects
|
||||
of a particular type, e.g. faces (frontal, profile), pedestrians etc.
|
||||
Some of the classifiers have a special license - please,
|
||||
look into the files for details.
|
@ -255,10 +255,10 @@ if(DOXYGEN_FOUND)
|
||||
endif()
|
||||
|
||||
# copy haar cascade files
|
||||
set(haar_cascade_files "")
|
||||
set(data_harrcascades_path "${OpenCV_SOURCE_DIR}/data/haarcascades/")
|
||||
list(APPEND js_tutorials_assets_deps "${data_harrcascades_path}/haarcascade_frontalface_default.xml" "${data_harrcascades_path}/haarcascade_eye.xml")
|
||||
list(APPEND js_assets "${data_harrcascades_path}/haarcascade_frontalface_default.xml" "${data_harrcascades_path}/haarcascade_eye.xml")
|
||||
# set(haar_cascade_files "")
|
||||
# set(data_harrcascades_path "${OpenCV_SOURCE_DIR}/data/haarcascades/")
|
||||
# list(APPEND js_tutorials_assets_deps "${data_harrcascades_path}/haarcascade_frontalface_default.xml" "${data_harrcascades_path}/haarcascade_eye.xml")
|
||||
# list(APPEND js_assets "${data_harrcascades_path}/haarcascade_frontalface_default.xml" "${data_harrcascades_path}/haarcascade_eye.xml")
|
||||
|
||||
foreach(f ${js_assets})
|
||||
get_filename_component(fname "${f}" NAME)
|
||||
|
@ -1,107 +0,0 @@
|
||||
Face Detection using Haar Cascades {#tutorial_js_face_detection}
|
||||
==================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- learn the basics of face detection using Haar Feature-based Cascade Classifiers
|
||||
- extend the same for eye detection etc.
|
||||
|
||||
Basics
|
||||
------
|
||||
|
||||
Object Detection using Haar feature-based cascade classifiers is an effective method proposed by Paul Viola and Michael Jones in the 2001 paper, "Rapid Object Detection using a
|
||||
Boosted Cascade of Simple Features". It is a machine learning based approach in which a cascade
|
||||
function is trained from a lot of positive and negative images. It is then used to detect objects in
|
||||
other images.
|
||||
|
||||
Here we will work with face detection. Initially, the algorithm needs a lot of positive images
|
||||
(images of faces) and negative images (images without faces) to train the classifier. Then we need
|
||||
to extract features from it. For this, Haar features shown in below image are used. They are just
|
||||
like our convolutional kernel. Each feature is a single value obtained by subtracting the sum of pixels
|
||||
under the white rectangle from the sum of pixels under the black rectangle.
|
||||
|
||||

|
||||
|
||||
Now all possible sizes and locations of each kernel are used to calculate plenty of features. For each
|
||||
feature calculation, we need to find the sum of the pixels under the white and black rectangles. To solve this,
|
||||
they introduced the integral images. It simplifies calculation of the sum of the pixels, how large may be
|
||||
the number of pixels, to an operation involving just four pixels.
|
||||
|
||||
But among all these features we calculated, most of them are irrelevant. For example, consider the
|
||||
image below. Top row shows two good features. The first feature selected seems to focus on the
|
||||
property that the region of the eyes is often darker than the region of the nose and cheeks. The
|
||||
second feature selected relies on the property that the eyes are darker than the bridge of the nose.
|
||||
But the same windows applying on cheeks or any other place is irrelevant. So how do we select the
|
||||
best features out of 160000+ features? It is achieved by **Adaboost**.
|
||||
|
||||

|
||||
|
||||
For this, we apply each and every feature on all the training images. For each feature, it finds the
|
||||
best threshold which will classify the faces to positive and negative. But obviously, there will be
|
||||
errors or misclassifications. We select the features with minimum error rate, which means they are
|
||||
the features that best classifies the face and non-face images. (The process is not as simple as
|
||||
this. Each image is given an equal weight in the beginning. After each classification, weights of
|
||||
misclassified images are increased. Then again same process is done. New error rates are calculated.
|
||||
Also new weights. The process is continued until required accuracy or error rate is achieved or
|
||||
required number of features are found).
|
||||
|
||||
Final classifier is a weighted sum of these weak classifiers. It is called weak because it alone
|
||||
can't classify the image, but together with others forms a strong classifier. The paper says even
|
||||
200 features provide detection with 95% accuracy. Their final setup had around 6000 features.
|
||||
(Imagine a reduction from 160000+ features to 6000 features. That is a big gain).
|
||||
|
||||
So now you take an image. Take each 24x24 window. Apply 6000 features to it. Check if it is face or
|
||||
not. Wow.. Wow.. Isn't it a little inefficient and time consuming? Yes, it is. Authors have a good
|
||||
solution for that.
|
||||
|
||||
In an image, most of the image region is non-face region. So it is a better idea to have a simple
|
||||
method to check if a window is not a face region. If it is not, discard it in a single shot. Don't
|
||||
process it again. Instead focus on region where there can be a face. This way, we can find more time
|
||||
to check a possible face region.
|
||||
|
||||
For this they introduced the concept of **Cascade of Classifiers**. Instead of applying all the 6000
|
||||
features on a window, group the features into different stages of classifiers and apply one-by-one.
|
||||
(Normally first few stages will contain very less number of features). If a window fails the first
|
||||
stage, discard it. We don't consider remaining features on it. If it passes, apply the second stage
|
||||
of features and continue the process. The window which passes all stages is a face region. How is
|
||||
the plan !!!
|
||||
|
||||
Authors' detector had 6000+ features with 38 stages with 1, 10, 25, 25 and 50 features in first five
|
||||
stages. (Two features in the above image is actually obtained as the best two features from
|
||||
Adaboost). According to authors, on an average, 10 features out of 6000+ are evaluated per
|
||||
sub-window.
|
||||
|
||||
So this is a simple intuitive explanation of how Viola-Jones face detection works. Read paper for
|
||||
more details.
|
||||
|
||||
Haar-cascade Detection in OpenCV
|
||||
--------------------------------
|
||||
|
||||
Here we will deal with detection. OpenCV already contains many pre-trained classifiers for face,
|
||||
eyes, smile etc. Those XML files are stored in opencv/data/haarcascades/ folder. Let's create a face
|
||||
and eye detector with OpenCV.
|
||||
|
||||
We use the function: **detectMultiScale (image, objects, scaleFactor = 1.1, minNeighbors = 3, flags = 0, minSize = new cv.Size(0, 0), maxSize = new cv.Size(0, 0))**
|
||||
|
||||
@param image matrix of the type CV_8U containing an image where objects are detected.
|
||||
@param objects vector of rectangles where each rectangle contains the detected object. The rectangles may be partially outside the original image.
|
||||
@param scaleFactor parameter specifying how much the image size is reduced at each image scale.
|
||||
@param minNeighbors parameter specifying how many neighbors each candidate rectangle should have to retain it.
|
||||
@param flags parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade.
|
||||
@param minSize minimum possible object size. Objects smaller than this are ignored.
|
||||
@param maxSize maximum possible object size. Objects larger than this are ignored. If maxSize == minSize model is evaluated on single scale.
|
||||
|
||||
@note Don't forget to delete CascadeClassifier and RectVector!
|
||||
|
||||
Try it
|
||||
------
|
||||
|
||||
Try this demo using the code above. Canvas elements named haarCascadeDetectionCanvasInput and haarCascadeDetectionCanvasOutput have been prepared. Choose an image and
|
||||
click `Try it` to see the result. You can change the code in the textbox to investigate more.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_face_detection.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
@ -1,15 +0,0 @@
|
||||
Face Detection in Video Capture {#tutorial_js_face_detection_camera}
|
||||
==================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- learn how to detect faces in video capture.
|
||||
|
||||
@note If you don't know how to capture video from camera, please review @ref tutorial_js_video_display.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_face_detection_camera.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
@ -1,11 +0,0 @@
|
||||
Object Detection {#tutorial_js_table_of_contents_objdetect}
|
||||
================
|
||||
|
||||
- @subpage tutorial_js_face_detection
|
||||
|
||||
Face detection
|
||||
using haar-cascades
|
||||
|
||||
- @subpage tutorial_js_face_detection_camera
|
||||
|
||||
Face Detection in Video Capture
|
@ -22,11 +22,6 @@ OpenCV.js Tutorials {#tutorial_js_root}
|
||||
In this section you
|
||||
will learn different techniques to work with videos like object tracking etc.
|
||||
|
||||
- @subpage tutorial_js_table_of_contents_objdetect
|
||||
|
||||
In this section you
|
||||
will object detection techniques like face detection etc.
|
||||
|
||||
- @subpage tutorial_js_table_of_contents_dnn
|
||||
|
||||
These tutorials show how to use dnn module in JavaScript
|
||||
|
@ -623,15 +623,6 @@
|
||||
volume = {5},
|
||||
pages = {1530-1536}
|
||||
}
|
||||
@inproceedings{Lienhart02,
|
||||
author = {Lienhart, Rainer and Maydt, Jochen},
|
||||
title = {An extended set of haar-like features for rapid object detection},
|
||||
booktitle = {Image Processing. 2002. Proceedings. 2002 International Conference on},
|
||||
year = {2002},
|
||||
pages = {I--900},
|
||||
volume = {1},
|
||||
publisher = {IEEE}
|
||||
}
|
||||
@article{Lowe04,
|
||||
author = {Lowe, David G.},
|
||||
title = {Distinctive Image Features from Scale-Invariant Keypoints},
|
||||
@ -1042,25 +1033,6 @@
|
||||
number = {3},
|
||||
publisher = {ACM}
|
||||
}
|
||||
@inproceedings{Viola01,
|
||||
author = {Viola, Paul and Jones, Michael J.},
|
||||
title = {Rapid object detection using a boosted cascade of simple features},
|
||||
booktitle = {Computer Vision and Pattern Recognition, 2001. CVPR 2001. Proceedings of the 2001 IEEE Computer Society Conference on},
|
||||
year = {2001},
|
||||
pages = {I--511},
|
||||
volume = {1},
|
||||
publisher = {IEEE}
|
||||
}
|
||||
@article{Viola04,
|
||||
author = {Viola, Paul and Jones, Michael J.},
|
||||
title = {Robust real-time face detection},
|
||||
journal = {International Journal of Computer Vision},
|
||||
year = {2004},
|
||||
volume = {57},
|
||||
number = {2},
|
||||
pages = {137--154},
|
||||
publisher = {Kluwer Academic Publishers}
|
||||
}
|
||||
@inproceedings{WJ10,
|
||||
author = {Xu, Wei and Mulligan, Jane},
|
||||
title = {Performance evaluation of color correction approaches for automatic multi-view image and video stitching},
|
||||
@ -1159,14 +1131,6 @@
|
||||
year = {2013},
|
||||
publisher = {Springer}
|
||||
}
|
||||
@incollection{Liao2007,
|
||||
title = {Learning multi-scale block local binary patterns for face recognition},
|
||||
author = {Liao, Shengcai and Zhu, Xiangxin and Lei, Zhen and Zhang, Lun and Li, Stan Z},
|
||||
booktitle = {Advances in Biometrics},
|
||||
pages = {828--837},
|
||||
year = {2007},
|
||||
publisher = {Springer}
|
||||
}
|
||||
@incollection{nister2008linear,
|
||||
title = {Linear time maximally stable extremal regions},
|
||||
author = {Nist{\'e}r, David and Stew{\'e}nius, Henrik},
|
||||
|
@ -1,4 +0,0 @@
|
||||
Face Detection using Haar Cascades {#tutorial_py_face_detection}
|
||||
==================================
|
||||
|
||||
Tutorial content has been moved: @ref tutorial_cascade_classifier
|
@ -48,7 +48,7 @@ OpenCV-Python Tutorials {#tutorial_py_root}
|
||||
- @ref tutorial_table_of_content_objdetect
|
||||
|
||||
In this section you
|
||||
will learn object detection techniques like face detection etc.
|
||||
will learn object detection techniques.
|
||||
|
||||
- @subpage tutorial_py_table_of_contents_bindings
|
||||
|
||||
|
@ -259,10 +259,10 @@ Next, create the directory `src/main/resources` and download this Lena image int
|
||||
Make sure it's called `"lena.png"`. Items in the resources directory are available to the Java
|
||||
application at runtime.
|
||||
|
||||
Next, copy `lbpcascade_frontalface.xml` from `opencv/data/lbpcascades/` into the `resources`
|
||||
Next, copy `lbpcascade_frontalface.xml` from `opencv_contrib/modules/xobjdetect/data/lbpcascades/` into the `resources`
|
||||
directory:
|
||||
@code{.bash}
|
||||
cp <opencv_dir>/data/lbpcascades/lbpcascade_frontalface.xml src/main/resources/
|
||||
cp <xobjdetect_dir>/data/lbpcascades/lbpcascade_frontalface.xml src/main/resources/
|
||||
@endcode
|
||||
Now modify src/main/java/HelloOpenCV.java so it contains the following Java code:
|
||||
@code{.java}
|
||||
@ -273,7 +273,7 @@ import org.opencv.core.Point;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.objdetect.CascadeClassifier;
|
||||
import org.opencv.xobjdetect.CascadeClassifier;
|
||||
|
||||
//
|
||||
// Detects faces in an image, draws boxes around them, and writes the results
|
||||
|
@ -2,6 +2,8 @@ Detection of ArUco boards {#tutorial_aruco_board_detection}
|
||||
=========================
|
||||
|
||||
@prev_tutorial{tutorial_aruco_detection}
|
||||
@next_tutorial{tutorial_barcode_detect_and_decode}
|
||||
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
|
@ -3,8 +3,7 @@ Barcode Recognition {#tutorial_barcode_detect_and_decode}
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_cascade_classifier}
|
||||
@next_tutorial{tutorial_introduction_to_pca}
|
||||
@prev_tutorial{tutorial_aruco_board_detection}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@ -3,3 +3,4 @@ Object Detection (objdetect module) {#tutorial_table_of_content_objdetect}
|
||||
|
||||
- @subpage tutorial_aruco_detection
|
||||
- @subpage tutorial_aruco_board_detection
|
||||
- @subpage tutorial_barcode_detect_and_decode
|
||||
|
@ -1,148 +0,0 @@
|
||||
Cascade Classifier {#tutorial_cascade_classifier}
|
||||
==================
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_optical_flow}
|
||||
@next_tutorial{tutorial_barcode_detect_and_decode}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Ana Huamán |
|
||||
| Compatibility | OpenCV >= 3.0 |
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial,
|
||||
|
||||
- We will learn how the Haar cascade object detection works.
|
||||
- We will see the basics of face detection and eye detection using the Haar Feature-based Cascade Classifiers
|
||||
- We will use the @ref cv::CascadeClassifier class to detect objects in a video stream. Particularly, we
|
||||
will use the functions:
|
||||
- @ref cv::CascadeClassifier::load to load a .xml classifier file. It can be either a Haar or a LBP classifier
|
||||
- @ref cv::CascadeClassifier::detectMultiScale to perform the detection.
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
Object Detection using Haar feature-based cascade classifiers is an effective object detection
|
||||
method proposed by Paul Viola and Michael Jones in their paper, "Rapid Object Detection using a
|
||||
Boosted Cascade of Simple Features" in 2001. It is a machine learning based approach where a cascade
|
||||
function is trained from a lot of positive and negative images. It is then used to detect objects in
|
||||
other images.
|
||||
|
||||
Here we will work with face detection. Initially, the algorithm needs a lot of positive images
|
||||
(images of faces) and negative images (images without faces) to train the classifier. Then we need
|
||||
to extract features from it. For this, Haar features shown in the below image are used. They are just
|
||||
like our convolutional kernel. Each feature is a single value obtained by subtracting sum of pixels
|
||||
under the white rectangle from sum of pixels under the black rectangle.
|
||||
|
||||

|
||||
|
||||
Now, all possible sizes and locations of each kernel are used to calculate lots of features. (Just
|
||||
imagine how much computation it needs? Even a 24x24 window results over 160000 features). For each
|
||||
feature calculation, we need to find the sum of the pixels under white and black rectangles. To solve
|
||||
this, they introduced the integral image. However large your image, it reduces the calculations for a
|
||||
given pixel to an operation involving just four pixels. Nice, isn't it? It makes things super-fast.
|
||||
|
||||
But among all these features we calculated, most of them are irrelevant. For example, consider the
|
||||
image below. The top row shows two good features. The first feature selected seems to focus on the
|
||||
property that the region of the eyes is often darker than the region of the nose and cheeks. The
|
||||
second feature selected relies on the property that the eyes are darker than the bridge of the nose.
|
||||
But the same windows applied to cheeks or any other place is irrelevant. So how do we select the
|
||||
best features out of 160000+ features? It is achieved by **Adaboost**.
|
||||
|
||||

|
||||
|
||||
For this, we apply each and every feature on all the training images. For each feature, it finds the
|
||||
best threshold which will classify the faces to positive and negative. Obviously, there will be
|
||||
errors or misclassifications. We select the features with minimum error rate, which means they are
|
||||
the features that most accurately classify the face and non-face images. (The process is not as simple as
|
||||
this. Each image is given an equal weight in the beginning. After each classification, weights of
|
||||
misclassified images are increased. Then the same process is done. New error rates are calculated.
|
||||
Also new weights. The process is continued until the required accuracy or error rate is achieved or
|
||||
the required number of features are found).
|
||||
|
||||
The final classifier is a weighted sum of these weak classifiers. It is called weak because it alone
|
||||
can't classify the image, but together with others forms a strong classifier. The paper says even
|
||||
200 features provide detection with 95% accuracy. Their final setup had around 6000 features.
|
||||
(Imagine a reduction from 160000+ features to 6000 features. That is a big gain).
|
||||
|
||||
So now you take an image. Take each 24x24 window. Apply 6000 features to it. Check if it is face or
|
||||
not. Wow.. Isn't it a little inefficient and time consuming? Yes, it is. The authors have a good
|
||||
solution for that.
|
||||
|
||||
In an image, most of the image is non-face region. So it is a better idea to have a simple
|
||||
method to check if a window is not a face region. If it is not, discard it in a single shot, and don't
|
||||
process it again. Instead, focus on regions where there can be a face. This way, we spend more time
|
||||
checking possible face regions.
|
||||
|
||||
For this they introduced the concept of **Cascade of Classifiers**. Instead of applying all 6000
|
||||
features on a window, the features are grouped into different stages of classifiers and applied one-by-one.
|
||||
(Normally the first few stages will contain very many fewer features). If a window fails the first
|
||||
stage, discard it. We don't consider the remaining features on it. If it passes, apply the second stage
|
||||
of features and continue the process. The window which passes all stages is a face region. How is
|
||||
that plan!
|
||||
|
||||
The authors' detector had 6000+ features with 38 stages with 1, 10, 25, 25 and 50 features in the first five
|
||||
stages. (The two features in the above image are actually obtained as the best two features from
|
||||
Adaboost). According to the authors, on average 10 features out of 6000+ are evaluated per
|
||||
sub-window.
|
||||
|
||||
So this is a simple intuitive explanation of how Viola-Jones face detection works. Read the paper for
|
||||
more details or check out the references in the Additional Resources section.
|
||||
|
||||
Haar-cascade Detection in OpenCV
|
||||
--------------------------------
|
||||
OpenCV provides pretrained models that can be read using the @ref cv::CascadeClassifier::load method.
|
||||
These models are located in the data folder in the OpenCV installation or can be found [here](https://github.com/opencv/opencv/tree/5.x/data).
|
||||
|
||||
The following code example will use pretrained Haar cascade models to detect faces and eyes in an image.
|
||||
First, a @ref cv::CascadeClassifier is created and the necessary XML file is loaded using the @ref cv::CascadeClassifier::load method.
|
||||
Afterwards, the detection is done using the @ref cv::CascadeClassifier::detectMultiScale method, which returns boundary rectangles for the detected faces or eyes.
|
||||
|
||||
@add_toggle_cpp
|
||||
This tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/objectDetection/objectDetection.cpp)
|
||||
@include samples/cpp/tutorial_code/objectDetection/objectDetection.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
This tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/5.x/samples/java/tutorial_code/objectDetection/cascade_classifier/ObjectDetectionDemo.java)
|
||||
@include samples/java/tutorial_code/objectDetection/cascade_classifier/ObjectDetectionDemo.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
This tutorial code's is shown lines below. You can also download it from
|
||||
[here](https://github.com/opencv/opencv/tree/5.x/samples/python/tutorial_code/objectDetection/cascade_classifier/objectDetection.py)
|
||||
@include samples/python/tutorial_code/objectDetection/cascade_classifier/objectDetection.py
|
||||
@end_toggle
|
||||
|
||||
Result
|
||||
------
|
||||
|
||||
-# Here is the result of running the code above and using as input the video stream of a built-in
|
||||
webcam:
|
||||
|
||||

|
||||
|
||||
Be sure the program will find the path of files *haarcascade_frontalface_alt.xml* and
|
||||
*haarcascade_eye_tree_eyeglasses.xml*. They are located in
|
||||
*opencv/data/haarcascades*
|
||||
|
||||
-# This is the result of using the file *lbpcascade_frontalface.xml* (LBP trained) for the face
|
||||
detection. For the eyes we keep using the file used in the tutorial.
|
||||
|
||||

|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
-# Paul Viola and Michael J. Jones. Robust real-time face detection. International Journal of Computer Vision, 57(2):137–154, 2004. @cite Viola04
|
||||
-# Rainer Lienhart and Jochen Maydt. An extended set of haar-like features for rapid object detection. In Image Processing. 2002. Proceedings. 2002 International Conference on, volume 1, pages I–900. IEEE, 2002. @cite Lienhart02
|
||||
-# Video Lecture on [Face Detection and Tracking](https://www.youtube.com/watch?v=WfdYYNamHZ8)
|
||||
-# An interesting interview regarding Face Detection by [Adam
|
||||
Harvey](https://web.archive.org/web/20171204220159/http://www.makematics.com/research/viola-jones/)
|
||||
-# [OpenCV Face Detection: Visualized](https://vimeo.com/12774628) on Vimeo by Adam Harvey
|
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 278 KiB |
@ -3,7 +3,7 @@ Introduction to Principal Component Analysis (PCA) {#tutorial_introduction_to_pc
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_barcode_detect_and_decode}
|
||||
@prev_tutorial{tutorial_optical_flow}
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
|
@ -4,7 +4,7 @@ Optical Flow {#tutorial_optical_flow}
|
||||
@tableofcontents
|
||||
|
||||
@prev_tutorial{tutorial_meanshift}
|
||||
@next_tutorial{tutorial_cascade_classifier}
|
||||
@next_tutorial{tutorial_introduction_to_pca}
|
||||
|
||||
Goal
|
||||
----
|
||||
|
@ -1,4 +1,4 @@
|
||||
Other tutorials (objdetect, photo, stitching, video) {#tutorial_table_of_content_other}
|
||||
Other tutorials (photo, stitching, video) {#tutorial_table_of_content_other}
|
||||
========================================================
|
||||
|
||||
- photo. @subpage tutorial_hdr_imaging
|
||||
@ -6,6 +6,4 @@ Other tutorials (objdetect, photo, stitching, video) {#tutorial_table_of_content
|
||||
- video. @subpage tutorial_background_subtraction
|
||||
- video. @subpage tutorial_meanshift
|
||||
- video. @subpage tutorial_optical_flow
|
||||
- objdetect. @subpage tutorial_cascade_classifier
|
||||
- objdetect. @subpage tutorial_barcode_detect_and_decode
|
||||
- ml. @subpage tutorial_introduction_to_pca
|
||||
|
@ -10,7 +10,7 @@ OpenCV Tutorials {#tutorial_root}
|
||||
- @subpage tutorial_table_of_content_features2d - feature detectors, descriptors and matching framework
|
||||
- @subpage tutorial_table_of_content_dnn - infer neural networks using built-in _dnn_ module
|
||||
- @subpage tutorial_table_of_content_gapi - graph-based approach to computer vision algorithms building
|
||||
- @subpage tutorial_table_of_content_other - other modules (objdetect, stitching, video, photo)
|
||||
- @subpage tutorial_table_of_content_other - other modules (stitching, video, photo)
|
||||
- @subpage tutorial_table_of_content_ios - running OpenCV on an iDevice
|
||||
- @subpage tutorial_table_of_content_3d - 3d objects processing and visualisation
|
||||
@cond CUDA_MODULES
|
||||
|
@ -76,9 +76,6 @@
|
||||
#ifdef HAVE_OPENCV_IMGPROC
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_OPENCV_ML
|
||||
#include "opencv2/ml.hpp"
|
||||
#endif
|
||||
#ifdef HAVE_OPENCV_OBJDETECT
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#endif
|
||||
|
@ -71,94 +71,9 @@
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
// The environment is Node.js
|
||||
var cv = require('./opencv.js'); // eslint-disable-line no-var
|
||||
cv.FS_createLazyFile('/', 'haarcascade_frontalface_default.xml', // eslint-disable-line new-cap
|
||||
'haarcascade_frontalface_default.xml', true, false);
|
||||
}
|
||||
|
||||
QUnit.module('Object Detection', {});
|
||||
QUnit.test('Cascade classification', function(assert) {
|
||||
// Group rectangle
|
||||
{
|
||||
let rectList = new cv.RectVector();
|
||||
let weights = new cv.IntVector();
|
||||
let groupThreshold = 1;
|
||||
const eps = 0.2;
|
||||
|
||||
let rect1 = new cv.Rect(1, 2, 3, 4);
|
||||
let rect2 = new cv.Rect(1, 4, 2, 3);
|
||||
|
||||
rectList.push_back(rect1);
|
||||
rectList.push_back(rect2);
|
||||
|
||||
cv.groupRectangles(rectList, weights, groupThreshold, eps);
|
||||
|
||||
|
||||
rectList.delete();
|
||||
weights.delete();
|
||||
}
|
||||
|
||||
// CascadeClassifier
|
||||
{
|
||||
let classifier = new cv.CascadeClassifier();
|
||||
const modelPath = '/haarcascade_frontalface_default.xml';
|
||||
|
||||
assert.equal(classifier.empty(), true);
|
||||
|
||||
|
||||
classifier.load(modelPath);
|
||||
assert.equal(classifier.empty(), false);
|
||||
|
||||
let image = cv.Mat.eye({height: 10, width: 10}, cv.CV_8UC3);
|
||||
let objects = new cv.RectVector();
|
||||
let numDetections = new cv.IntVector();
|
||||
const scaleFactor = 1.1;
|
||||
const minNeighbors = 3;
|
||||
const flags = 0;
|
||||
const minSize = {height: 0, width: 0};
|
||||
const maxSize = {height: 10, width: 10};
|
||||
|
||||
classifier.detectMultiScale2(image, objects, numDetections, scaleFactor,
|
||||
minNeighbors, flags, minSize, maxSize);
|
||||
|
||||
// test default parameters
|
||||
classifier.detectMultiScale2(image, objects, numDetections, scaleFactor,
|
||||
minNeighbors, flags, minSize);
|
||||
classifier.detectMultiScale2(image, objects, numDetections, scaleFactor,
|
||||
minNeighbors, flags);
|
||||
classifier.detectMultiScale2(image, objects, numDetections, scaleFactor,
|
||||
minNeighbors);
|
||||
classifier.detectMultiScale2(image, objects, numDetections, scaleFactor);
|
||||
|
||||
classifier.delete();
|
||||
objects.delete();
|
||||
numDetections.delete();
|
||||
}
|
||||
|
||||
// HOGDescriptor
|
||||
{
|
||||
let hog = new cv.HOGDescriptor();
|
||||
let mat = new cv.Mat({height: 10, width: 10}, cv.CV_8UC1);
|
||||
let descriptors = new cv.FloatVector();
|
||||
let locations = new cv.PointVector();
|
||||
|
||||
|
||||
assert.equal(hog.winSize.height, 128);
|
||||
assert.equal(hog.winSize.width, 64);
|
||||
assert.equal(hog.nbins, 9);
|
||||
assert.equal(hog.derivAperture, 1);
|
||||
assert.equal(hog.winSigma, -1);
|
||||
assert.equal(hog.histogramNormType, 0);
|
||||
assert.equal(hog.nlevels, 64);
|
||||
|
||||
hog.nlevels = 32;
|
||||
assert.equal(hog.nlevels, 32);
|
||||
|
||||
hog.delete();
|
||||
mat.delete();
|
||||
descriptors.delete();
|
||||
locations.delete();
|
||||
}
|
||||
});
|
||||
QUnit.test('QR code detect and decode', function (assert) {
|
||||
{
|
||||
const detector = new cv.QRCodeDetector();
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@ -52,57 +52,6 @@
|
||||
@defgroup objdetect Object Detection
|
||||
|
||||
@{
|
||||
@defgroup objdetect_cascade_classifier Cascade Classifier for Object Detection
|
||||
|
||||
The object detector described below has been initially proposed by Paul Viola @cite Viola01 and
|
||||
improved by Rainer Lienhart @cite Lienhart02 .
|
||||
|
||||
First, a classifier (namely a *cascade of boosted classifiers working with haar-like features*) is
|
||||
trained with a few hundred sample views of a particular object (i.e., a face or a car), called
|
||||
positive examples, that are scaled to the same size (say, 20x20), and negative examples - arbitrary
|
||||
images of the same size.
|
||||
|
||||
After a classifier is trained, it can be applied to a region of interest (of the same size as used
|
||||
during the training) in an input image. The classifier outputs a "1" if the region is likely to show
|
||||
the object (i.e., face/car), and "0" otherwise. To search for the object in the whole image one can
|
||||
move the search window across the image and check every location using the classifier. The
|
||||
classifier is designed so that it can be easily "resized" in order to be able to find the objects of
|
||||
interest at different sizes, which is more efficient than resizing the image itself. So, to find an
|
||||
object of an unknown size in the image the scan procedure should be done several times at different
|
||||
scales.
|
||||
|
||||
The word "cascade" in the classifier name means that the resultant classifier consists of several
|
||||
simpler classifiers (*stages*) that are applied subsequently to a region of interest until at some
|
||||
stage the candidate is rejected or all the stages are passed. The word "boosted" means that the
|
||||
classifiers at every stage of the cascade are complex themselves and they are built out of basic
|
||||
classifiers using one of four different boosting techniques (weighted voting). Currently Discrete
|
||||
Adaboost, Real Adaboost, Gentle Adaboost and Logitboost are supported. The basic classifiers are
|
||||
decision-tree classifiers with at least 2 leaves. Haar-like features are the input to the basic
|
||||
classifiers, and are calculated as described below. The current algorithm uses the following
|
||||
Haar-like features:
|
||||
|
||||

|
||||
|
||||
The feature used in a particular classifier is specified by its shape (1a, 2b etc.), position within
|
||||
the region of interest and the scale (this scale is not the same as the scale used at the detection
|
||||
stage, though these two scales are multiplied). For example, in the case of the third line feature
|
||||
(2c) the response is calculated as the difference between the sum of image pixels under the
|
||||
rectangle covering the whole feature (including the two white stripes and the black stripe in the
|
||||
middle) and the sum of the image pixels under the black stripe multiplied by 3 in order to
|
||||
compensate for the differences in the size of areas. The sums of pixel values over a rectangular
|
||||
regions are calculated rapidly using integral images (see below and the integral description).
|
||||
|
||||
Check @ref tutorial_cascade_classifier "the corresponding tutorial" for more details.
|
||||
|
||||
The following reference is for the detection part only. There is a separate application called
|
||||
opencv_traincascade that can train a cascade of boosted classifiers from a set of samples.
|
||||
|
||||
@note In the new C++ interface it is also possible to use LBP (local binary pattern) features in
|
||||
addition to Haar-like features. .. [Viola01] Paul Viola and Michael J. Jones. Rapid Object Detection
|
||||
using a Boosted Cascade of Simple Features. IEEE CVPR, 2001. The paper is available online at
|
||||
<https://github.com/SvHey/thesis/blob/master/Literature/ObjectDetection/violaJones_CVPR2001.pdf>
|
||||
|
||||
@defgroup objdetect_hog HOG (Histogram of Oriented Gradients) descriptor and object detector
|
||||
@defgroup objdetect_barcode Barcode detection and decoding
|
||||
@defgroup objdetect_qrcode QRCode detection and encoding
|
||||
@defgroup objdetect_dnn_face DNN-based face detection and recognition
|
||||
@ -133,564 +82,8 @@
|
||||
@}
|
||||
*/
|
||||
|
||||
typedef struct CvHaarClassifierCascade CvHaarClassifierCascade;
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
//! @addtogroup objdetect_common
|
||||
//! @{
|
||||
|
||||
///////////////////////////// Object Detection ////////////////////////////
|
||||
|
||||
/** @brief This class is used for grouping object candidates detected by Cascade Classifier, HOG etc.
|
||||
|
||||
instance of the class is to be passed to cv::partition
|
||||
*/
|
||||
class CV_EXPORTS SimilarRects
|
||||
{
|
||||
public:
|
||||
SimilarRects(double _eps) : eps(_eps) {}
|
||||
inline bool operator()(const Rect& r1, const Rect& r2) const
|
||||
{
|
||||
double delta = eps * ((std::min)(r1.width, r2.width) + (std::min)(r1.height, r2.height)) * 0.5;
|
||||
return std::abs(r1.x - r2.x) <= delta &&
|
||||
std::abs(r1.y - r2.y) <= delta &&
|
||||
std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&
|
||||
std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;
|
||||
}
|
||||
double eps;
|
||||
};
|
||||
|
||||
/** @brief Groups the object candidate rectangles.
|
||||
|
||||
@param rectList Input/output vector of rectangles. Output vector includes retained and grouped
|
||||
rectangles. (The Python list is not modified in place.)
|
||||
@param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a
|
||||
group of rectangles to retain it.
|
||||
@param eps Relative difference between sides of the rectangles to merge them into a group.
|
||||
|
||||
The function is a wrapper for the generic function partition . It clusters all the input rectangles
|
||||
using the rectangle equivalence criteria that combines rectangles with similar sizes and similar
|
||||
locations. The similarity is defined by eps. When eps=0 , no clustering is done at all. If
|
||||
\f$\texttt{eps}\rightarrow +\inf\f$ , all the rectangles are put in one cluster. Then, the small
|
||||
clusters containing less than or equal to groupThreshold rectangles are rejected. In each other
|
||||
cluster, the average rectangle is computed and put into the output rectangle list.
|
||||
*/
|
||||
CV_EXPORTS void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps = 0.2);
|
||||
/** @overload */
|
||||
CV_EXPORTS_W void groupRectangles(CV_IN_OUT std::vector<Rect>& rectList, CV_OUT std::vector<int>& weights,
|
||||
int groupThreshold, double eps = 0.2);
|
||||
/** @overload */
|
||||
CV_EXPORTS void groupRectangles(std::vector<Rect>& rectList, int groupThreshold,
|
||||
double eps, std::vector<int>* weights, std::vector<double>* levelWeights );
|
||||
/** @overload */
|
||||
CV_EXPORTS void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& rejectLevels,
|
||||
std::vector<double>& levelWeights, int groupThreshold, double eps = 0.2);
|
||||
/** @overload */
|
||||
CV_EXPORTS void groupRectangles_meanshift(std::vector<Rect>& rectList, std::vector<double>& foundWeights,
|
||||
std::vector<double>& foundScales,
|
||||
double detectThreshold = 0.0, Size winDetSize = Size(64, 128));
|
||||
//! @}
|
||||
|
||||
//! @addtogroup objdetect_cascade_classifier
|
||||
//! @{
|
||||
|
||||
template<> struct DefaultDeleter<CvHaarClassifierCascade>{ CV_EXPORTS void operator ()(CvHaarClassifierCascade* obj) const; };
|
||||
|
||||
enum { CASCADE_DO_CANNY_PRUNING = 1,
|
||||
CASCADE_SCALE_IMAGE = 2,
|
||||
CASCADE_FIND_BIGGEST_OBJECT = 4,
|
||||
CASCADE_DO_ROUGH_SEARCH = 8
|
||||
};
|
||||
|
||||
class CV_EXPORTS_W BaseCascadeClassifier : public Algorithm
|
||||
{
|
||||
public:
|
||||
virtual ~BaseCascadeClassifier();
|
||||
virtual bool empty() const CV_OVERRIDE = 0;
|
||||
virtual bool load( const String& filename ) = 0;
|
||||
virtual void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
double scaleFactor,
|
||||
int minNeighbors, int flags,
|
||||
Size minSize, Size maxSize ) = 0;
|
||||
|
||||
virtual void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
CV_OUT std::vector<int>& numDetections,
|
||||
double scaleFactor,
|
||||
int minNeighbors, int flags,
|
||||
Size minSize, Size maxSize ) = 0;
|
||||
|
||||
virtual void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
CV_OUT std::vector<int>& rejectLevels,
|
||||
CV_OUT std::vector<double>& levelWeights,
|
||||
double scaleFactor,
|
||||
int minNeighbors, int flags,
|
||||
Size minSize, Size maxSize,
|
||||
bool outputRejectLevels ) = 0;
|
||||
|
||||
virtual bool isOldFormatCascade() const = 0;
|
||||
virtual Size getOriginalWindowSize() const = 0;
|
||||
virtual int getFeatureType() const = 0;
|
||||
virtual void* getOldCascade() = 0;
|
||||
|
||||
class CV_EXPORTS MaskGenerator
|
||||
{
|
||||
public:
|
||||
virtual ~MaskGenerator() {}
|
||||
virtual Mat generateMask(const Mat& src)=0;
|
||||
virtual void initializeMask(const Mat& /*src*/) { }
|
||||
};
|
||||
virtual void setMaskGenerator(const Ptr<MaskGenerator>& maskGenerator) = 0;
|
||||
virtual Ptr<MaskGenerator> getMaskGenerator() = 0;
|
||||
};
|
||||
|
||||
/** @example samples/cpp/facedetect.cpp
|
||||
This program demonstrates usage of the Cascade classifier class
|
||||
\image html Cascade_Classifier_Tutorial_Result_Haar.jpg "Sample screenshot" width=321 height=254
|
||||
*/
|
||||
/** @brief Cascade classifier class for object detection.
|
||||
*/
|
||||
class CV_EXPORTS_W CascadeClassifier
|
||||
{
|
||||
public:
|
||||
CV_WRAP CascadeClassifier();
|
||||
/** @brief Loads a classifier from a file.
|
||||
|
||||
@param filename Name of the file from which the classifier is loaded.
|
||||
*/
|
||||
CV_WRAP CascadeClassifier(const String& filename);
|
||||
~CascadeClassifier();
|
||||
/** @brief Checks whether the classifier has been loaded.
|
||||
*/
|
||||
CV_WRAP bool empty() const;
|
||||
/** @brief Loads a classifier from a file.
|
||||
|
||||
@param filename Name of the file from which the classifier is loaded. The file may contain an old
|
||||
HAAR classifier trained by the haartraining application or a new cascade classifier trained by the
|
||||
traincascade application.
|
||||
*/
|
||||
CV_WRAP bool load( const String& filename );
|
||||
/** @brief Reads a classifier from a FileStorage node.
|
||||
|
||||
@note The file may contain a new cascade classifier (trained by the traincascade application) only.
|
||||
*/
|
||||
CV_WRAP bool read( const FileNode& node );
|
||||
|
||||
/** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list
|
||||
of rectangles.
|
||||
|
||||
@param image Matrix of the type CV_8U containing an image where objects are detected.
|
||||
@param objects Vector of rectangles where each rectangle contains the detected object, the
|
||||
rectangles may be partially outside the original image.
|
||||
@param scaleFactor Parameter specifying how much the image size is reduced at each image scale.
|
||||
@param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have
|
||||
to retain it.
|
||||
@param flags Parameter with the same meaning for an old cascade as in the function
|
||||
cvHaarDetectObjects. It is not used for a new cascade.
|
||||
@param minSize Minimum possible object size. Objects smaller than that are ignored.
|
||||
@param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale.
|
||||
*/
|
||||
CV_WRAP void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
double scaleFactor = 1.1,
|
||||
int minNeighbors = 3, int flags = 0,
|
||||
Size minSize = Size(),
|
||||
Size maxSize = Size() );
|
||||
|
||||
/** @overload
|
||||
@param image Matrix of the type CV_8U containing an image where objects are detected.
|
||||
@param objects Vector of rectangles where each rectangle contains the detected object, the
|
||||
rectangles may be partially outside the original image.
|
||||
@param numDetections Vector of detection numbers for the corresponding objects. An object's number
|
||||
of detections is the number of neighboring positively classified rectangles that were joined
|
||||
together to form the object.
|
||||
@param scaleFactor Parameter specifying how much the image size is reduced at each image scale.
|
||||
@param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have
|
||||
to retain it.
|
||||
@param flags Parameter with the same meaning for an old cascade as in the function
|
||||
cvHaarDetectObjects. It is not used for a new cascade.
|
||||
@param minSize Minimum possible object size. Objects smaller than that are ignored.
|
||||
@param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale.
|
||||
*/
|
||||
CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
CV_OUT std::vector<int>& numDetections,
|
||||
double scaleFactor=1.1,
|
||||
int minNeighbors=3, int flags=0,
|
||||
Size minSize=Size(),
|
||||
Size maxSize=Size() );
|
||||
|
||||
/** @overload
|
||||
This function allows you to retrieve the final stage decision certainty of classification.
|
||||
For this, one needs to set `outputRejectLevels` on true and provide the `rejectLevels` and `levelWeights` parameter.
|
||||
For each resulting detection, `levelWeights` will then contain the certainty of classification at the final stage.
|
||||
This value can then be used to separate strong from weaker classifications.
|
||||
|
||||
A code sample on how to use it efficiently can be found below:
|
||||
@code
|
||||
Mat img;
|
||||
vector<double> weights;
|
||||
vector<int> levels;
|
||||
vector<Rect> detections;
|
||||
CascadeClassifier model("/path/to/your/model.xml");
|
||||
model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true);
|
||||
cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl;
|
||||
@endcode
|
||||
*/
|
||||
CV_WRAP_AS(detectMultiScale3) void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
CV_OUT std::vector<int>& rejectLevels,
|
||||
CV_OUT std::vector<double>& levelWeights,
|
||||
double scaleFactor = 1.1,
|
||||
int minNeighbors = 3, int flags = 0,
|
||||
Size minSize = Size(),
|
||||
Size maxSize = Size(),
|
||||
bool outputRejectLevels = false );
|
||||
|
||||
CV_WRAP bool isOldFormatCascade() const;
|
||||
CV_WRAP Size getOriginalWindowSize() const;
|
||||
CV_WRAP int getFeatureType() const;
|
||||
void* getOldCascade();
|
||||
|
||||
CV_WRAP static bool convert(const String& oldcascade, const String& newcascade);
|
||||
|
||||
void setMaskGenerator(const Ptr<BaseCascadeClassifier::MaskGenerator>& maskGenerator);
|
||||
Ptr<BaseCascadeClassifier::MaskGenerator> getMaskGenerator();
|
||||
|
||||
Ptr<BaseCascadeClassifier> cc;
|
||||
};
|
||||
|
||||
CV_EXPORTS Ptr<BaseCascadeClassifier::MaskGenerator> createFaceDetectionMaskGenerator();
|
||||
//! @}
|
||||
|
||||
//! @addtogroup objdetect_hog
|
||||
//! @{
|
||||
//////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector //////////////
|
||||
|
||||
//! struct for detection region of interest (ROI)
|
||||
struct DetectionROI
|
||||
{
|
||||
//! scale(size) of the bounding box
|
||||
double scale;
|
||||
//! set of requested locations to be evaluated
|
||||
std::vector<cv::Point> locations;
|
||||
//! vector that will contain confidence values for each location
|
||||
std::vector<double> confidences;
|
||||
};
|
||||
|
||||
/**@brief Implementation of HOG (Histogram of Oriented Gradients) descriptor and object detector.
|
||||
|
||||
the HOG descriptor algorithm introduced by Navneet Dalal and Bill Triggs @cite Dalal2005 .
|
||||
|
||||
useful links:
|
||||
|
||||
https://hal.inria.fr/inria-00548512/document/
|
||||
|
||||
https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients
|
||||
|
||||
https://software.intel.com/en-us/ipp-dev-reference-histogram-of-oriented-gradients-hog-descriptor
|
||||
|
||||
http://www.learnopencv.com/histogram-of-oriented-gradients
|
||||
|
||||
http://www.learnopencv.com/handwritten-digits-classification-an-opencv-c-python-tutorial
|
||||
|
||||
*/
|
||||
class CV_EXPORTS_W HOGDescriptor
|
||||
{
|
||||
public:
|
||||
enum HistogramNormType { L2Hys = 0 //!< Default histogramNormType
|
||||
};
|
||||
enum { DEFAULT_NLEVELS = 64 //!< Default nlevels value.
|
||||
};
|
||||
enum DescriptorStorageFormat { DESCR_FORMAT_COL_BY_COL, DESCR_FORMAT_ROW_BY_ROW };
|
||||
|
||||
/**@brief Creates the HOG descriptor and detector with default parameters.
|
||||
|
||||
@param _winSize sets winSize with given value.
|
||||
@param _blockSize sets blockSize with given value.
|
||||
@param _blockStride sets blockStride with given value.
|
||||
@param _cellSize sets cellSize with given value.
|
||||
@param _nbins sets nbins with given value.
|
||||
@param _derivAperture sets derivAperture with given value.
|
||||
@param _winSigma sets winSigma with given value.
|
||||
@param _histogramNormType sets histogramNormType with given value.
|
||||
@param _L2HysThreshold sets L2HysThreshold with given value.
|
||||
@param _gammaCorrection sets gammaCorrection with given value.
|
||||
@param _nlevels sets nlevels with given value.
|
||||
@param _signedGradient sets signedGradient with given value.
|
||||
*/
|
||||
CV_WRAP HOGDescriptor(Size _winSize = Size(64, 128), Size _blockSize = Size(16, 16), Size _blockStride = Size(8, 8),
|
||||
Size _cellSize = Size(8, 8), int _nbins = 9, int _derivAperture = 1, double _winSigma = -1,
|
||||
HOGDescriptor::HistogramNormType _histogramNormType = HOGDescriptor::L2Hys,
|
||||
double _L2HysThreshold = 0.2, bool _gammaCorrection = true,
|
||||
int _nlevels = HOGDescriptor::DEFAULT_NLEVELS, bool _signedGradient = false)
|
||||
: winSize(_winSize), blockSize(_blockSize), blockStride(_blockStride), cellSize(_cellSize),
|
||||
nbins(_nbins), derivAperture(_derivAperture), winSigma(_winSigma),
|
||||
histogramNormType(_histogramNormType), L2HysThreshold(_L2HysThreshold),
|
||||
gammaCorrection(_gammaCorrection), free_coef(-1.f), nlevels(_nlevels), signedGradient(_signedGradient)
|
||||
{}
|
||||
|
||||
/** @overload
|
||||
|
||||
Creates the HOG descriptor and detector and loads HOGDescriptor parameters and coefficients for the linear SVM classifier from a file.
|
||||
@param filename The file name containing HOGDescriptor properties and coefficients for the linear SVM classifier.
|
||||
*/
|
||||
CV_WRAP HOGDescriptor(const String& filename)
|
||||
{
|
||||
load(filename);
|
||||
}
|
||||
|
||||
/** @overload
|
||||
@param d the HOGDescriptor which cloned to create a new one.
|
||||
*/
|
||||
HOGDescriptor(const HOGDescriptor& d)
|
||||
{
|
||||
d.copyTo(*this);
|
||||
}
|
||||
|
||||
/**@brief Default destructor.
|
||||
*/
|
||||
virtual ~HOGDescriptor() {}
|
||||
|
||||
/**@brief Returns the number of coefficients required for the classification.
|
||||
*/
|
||||
CV_WRAP size_t getDescriptorSize() const;
|
||||
|
||||
/** @brief Checks if detector size equal to descriptor size.
|
||||
*/
|
||||
CV_WRAP bool checkDetectorSize() const;
|
||||
|
||||
/** @brief Returns winSigma value
|
||||
*/
|
||||
CV_WRAP double getWinSigma() const;
|
||||
|
||||
/**@example samples/cpp/peopledetect.cpp
|
||||
*/
|
||||
/**@brief Sets coefficients for the linear SVM classifier.
|
||||
@param svmdetector coefficients for the linear SVM classifier.
|
||||
*/
|
||||
CV_WRAP virtual void setSVMDetector(InputArray svmdetector);
|
||||
|
||||
/** @brief Reads HOGDescriptor parameters and coefficients for the linear SVM classifier from a file node.
|
||||
@param fn File node
|
||||
*/
|
||||
virtual bool read(FileNode& fn);
|
||||
|
||||
/** @brief Stores HOGDescriptor parameters and coefficients for the linear SVM classifier in a file storage.
|
||||
@param fs File storage
|
||||
@param objname Object name
|
||||
*/
|
||||
virtual void write(FileStorage& fs, const String& objname) const;
|
||||
|
||||
/** @brief loads HOGDescriptor parameters and coefficients for the linear SVM classifier from a file
|
||||
@param filename Name of the file to read.
|
||||
@param objname The optional name of the node to read (if empty, the first top-level node will be used).
|
||||
*/
|
||||
CV_WRAP virtual bool load(const String& filename, const String& objname = String());
|
||||
|
||||
/** @brief saves HOGDescriptor parameters and coefficients for the linear SVM classifier to a file
|
||||
@param filename File name
|
||||
@param objname Object name
|
||||
*/
|
||||
CV_WRAP virtual void save(const String& filename, const String& objname = String()) const;
|
||||
|
||||
/** @brief clones the HOGDescriptor
|
||||
@param c cloned HOGDescriptor
|
||||
*/
|
||||
virtual void copyTo(HOGDescriptor& c) const;
|
||||
|
||||
/** @brief Computes HOG descriptors of given image.
|
||||
@param img Matrix of the type CV_8U containing an image where HOG features will be calculated.
|
||||
@param descriptors Matrix of the type CV_32F
|
||||
@param winStride Window stride. It must be a multiple of block stride.
|
||||
@param padding Padding
|
||||
@param locations Vector of Point
|
||||
*/
|
||||
CV_WRAP virtual void compute(InputArray img,
|
||||
CV_OUT std::vector<float>& descriptors,
|
||||
Size winStride = Size(), Size padding = Size(),
|
||||
const std::vector<Point>& locations = std::vector<Point>()) const;
|
||||
|
||||
/** @brief Performs object detection without a multi-scale window.
|
||||
@param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
|
||||
@param foundLocations Vector of point where each point contains left-top corner point of detected object boundaries.
|
||||
@param weights Vector that will contain confidence values for each detected object.
|
||||
@param hitThreshold Threshold for the distance between features and SVM classifying plane.
|
||||
Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient).
|
||||
But if the free coefficient is omitted (which is allowed), you can specify it manually here.
|
||||
@param winStride Window stride. It must be a multiple of block stride.
|
||||
@param padding Padding
|
||||
@param searchLocations Vector of Point includes set of requested locations to be evaluated.
|
||||
*/
|
||||
CV_WRAP virtual void detect(InputArray img, CV_OUT std::vector<Point>& foundLocations,
|
||||
CV_OUT std::vector<double>& weights,
|
||||
double hitThreshold = 0, Size winStride = Size(),
|
||||
Size padding = Size(),
|
||||
const std::vector<Point>& searchLocations = std::vector<Point>()) const;
|
||||
|
||||
/** @brief Performs object detection without a multi-scale window.
|
||||
@param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
|
||||
@param foundLocations Vector of point where each point contains left-top corner point of detected object boundaries.
|
||||
@param hitThreshold Threshold for the distance between features and SVM classifying plane.
|
||||
Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient).
|
||||
But if the free coefficient is omitted (which is allowed), you can specify it manually here.
|
||||
@param winStride Window stride. It must be a multiple of block stride.
|
||||
@param padding Padding
|
||||
@param searchLocations Vector of Point includes locations to search.
|
||||
*/
|
||||
virtual void detect(InputArray img, CV_OUT std::vector<Point>& foundLocations,
|
||||
double hitThreshold = 0, Size winStride = Size(),
|
||||
Size padding = Size(),
|
||||
const std::vector<Point>& searchLocations=std::vector<Point>()) const;
|
||||
|
||||
/** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list
|
||||
of rectangles.
|
||||
@param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
|
||||
@param foundLocations Vector of rectangles where each rectangle contains the detected object.
|
||||
@param foundWeights Vector that will contain confidence values for each detected object.
|
||||
@param hitThreshold Threshold for the distance between features and SVM classifying plane.
|
||||
Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient).
|
||||
But if the free coefficient is omitted (which is allowed), you can specify it manually here.
|
||||
@param winStride Window stride. It must be a multiple of block stride.
|
||||
@param padding Padding
|
||||
@param scale Coefficient of the detection window increase.
|
||||
@param groupThreshold Coefficient to regulate the similarity threshold. When detected, some objects can be covered
|
||||
by many rectangles. 0 means not to perform grouping.
|
||||
@param useMeanshiftGrouping indicates grouping algorithm
|
||||
*/
|
||||
CV_WRAP virtual void detectMultiScale(InputArray img, CV_OUT std::vector<Rect>& foundLocations,
|
||||
CV_OUT std::vector<double>& foundWeights, double hitThreshold = 0,
|
||||
Size winStride = Size(), Size padding = Size(), double scale = 1.05,
|
||||
double groupThreshold = 2.0, bool useMeanshiftGrouping = false) const;
|
||||
|
||||
/** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list
|
||||
of rectangles.
|
||||
@param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
|
||||
@param foundLocations Vector of rectangles where each rectangle contains the detected object.
|
||||
@param hitThreshold Threshold for the distance between features and SVM classifying plane.
|
||||
Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient).
|
||||
But if the free coefficient is omitted (which is allowed), you can specify it manually here.
|
||||
@param winStride Window stride. It must be a multiple of block stride.
|
||||
@param padding Padding
|
||||
@param scale Coefficient of the detection window increase.
|
||||
@param groupThreshold Coefficient to regulate the similarity threshold. When detected, some objects can be covered
|
||||
by many rectangles. 0 means not to perform grouping.
|
||||
@param useMeanshiftGrouping indicates grouping algorithm
|
||||
*/
|
||||
virtual void detectMultiScale(InputArray img, CV_OUT std::vector<Rect>& foundLocations,
|
||||
double hitThreshold = 0, Size winStride = Size(),
|
||||
Size padding = Size(), double scale = 1.05,
|
||||
double groupThreshold = 2.0, bool useMeanshiftGrouping = false) const;
|
||||
|
||||
/** @brief Computes gradients and quantized gradient orientations.
|
||||
@param img Matrix contains the image to be computed
|
||||
@param grad Matrix of type CV_32FC2 contains computed gradients
|
||||
@param angleOfs Matrix of type CV_8UC2 contains quantized gradient orientations
|
||||
@param paddingTL Padding from top-left
|
||||
@param paddingBR Padding from bottom-right
|
||||
*/
|
||||
CV_WRAP virtual void computeGradient(InputArray img, InputOutputArray grad, InputOutputArray angleOfs,
|
||||
Size paddingTL = Size(), Size paddingBR = Size()) const;
|
||||
|
||||
/** @brief Returns coefficients of the classifier trained for people detection (for 64x128 windows).
|
||||
*/
|
||||
CV_WRAP static std::vector<float> getDefaultPeopleDetector();
|
||||
|
||||
/**@example samples/tapi/hog.cpp
|
||||
*/
|
||||
/** @brief Returns coefficients of the classifier trained for people detection (for 48x96 windows).
|
||||
*/
|
||||
CV_WRAP static std::vector<float> getDaimlerPeopleDetector();
|
||||
|
||||
//! Detection window size. Align to block size and block stride. Default value is Size(64,128).
|
||||
CV_PROP Size winSize;
|
||||
|
||||
//! Block size in pixels. Align to cell size. Default value is Size(16,16).
|
||||
CV_PROP Size blockSize;
|
||||
|
||||
//! Block stride. It must be a multiple of cell size. Default value is Size(8,8).
|
||||
CV_PROP Size blockStride;
|
||||
|
||||
//! Cell size. Default value is Size(8,8).
|
||||
CV_PROP Size cellSize;
|
||||
|
||||
//! Number of bins used in the calculation of histogram of gradients. Default value is 9.
|
||||
CV_PROP int nbins;
|
||||
|
||||
//! not documented
|
||||
CV_PROP int derivAperture;
|
||||
|
||||
//! Gaussian smoothing window parameter.
|
||||
CV_PROP double winSigma;
|
||||
|
||||
//! histogramNormType
|
||||
CV_PROP HOGDescriptor::HistogramNormType histogramNormType;
|
||||
|
||||
//! L2-Hys normalization method shrinkage.
|
||||
CV_PROP double L2HysThreshold;
|
||||
|
||||
//! Flag to specify whether the gamma correction preprocessing is required or not.
|
||||
CV_PROP bool gammaCorrection;
|
||||
|
||||
//! coefficients for the linear SVM classifier.
|
||||
CV_PROP std::vector<float> svmDetector;
|
||||
|
||||
//! coefficients for the linear SVM classifier used when OpenCL is enabled
|
||||
UMat oclSvmDetector;
|
||||
|
||||
//! not documented
|
||||
float free_coef;
|
||||
|
||||
//! Maximum number of detection window increases. Default value is 64
|
||||
CV_PROP int nlevels;
|
||||
|
||||
//! Indicates signed gradient will be used or not
|
||||
CV_PROP bool signedGradient;
|
||||
|
||||
/** @brief evaluate specified ROI and return confidence value for each location
|
||||
@param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
|
||||
@param locations Vector of Point
|
||||
@param foundLocations Vector of Point where each Point is detected object's top-left point.
|
||||
@param confidences confidences
|
||||
@param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually
|
||||
it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if
|
||||
the free coefficient is omitted (which is allowed), you can specify it manually here
|
||||
@param winStride winStride
|
||||
@param padding padding
|
||||
*/
|
||||
virtual void detectROI(InputArray img, const std::vector<cv::Point> &locations,
|
||||
CV_OUT std::vector<cv::Point>& foundLocations, CV_OUT std::vector<double>& confidences,
|
||||
double hitThreshold = 0, cv::Size winStride = Size(),
|
||||
cv::Size padding = Size()) const;
|
||||
|
||||
/** @brief evaluate specified ROI and return confidence value for each location in multiple scales
|
||||
@param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected.
|
||||
@param foundLocations Vector of rectangles where each rectangle contains the detected object.
|
||||
@param locations Vector of DetectionROI
|
||||
@param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified
|
||||
in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here.
|
||||
@param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.
|
||||
*/
|
||||
virtual void detectMultiScaleROI(InputArray img,
|
||||
CV_OUT std::vector<cv::Rect>& foundLocations,
|
||||
std::vector<DetectionROI>& locations,
|
||||
double hitThreshold = 0,
|
||||
int groupThreshold = 0) const;
|
||||
|
||||
/** @brief Groups the object candidate rectangles.
|
||||
@param rectList Input/output vector of rectangles. Output vector includes retained and grouped rectangles. (The Python list is not modified in place.)
|
||||
@param weights Input/output vector of weights of rectangles. Output vector includes weights of retained and grouped rectangles. (The Python list is not modified in place.)
|
||||
@param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.
|
||||
@param eps Relative difference between sides of the rectangles to merge them into a group.
|
||||
*/
|
||||
void groupRectangles(std::vector<cv::Rect>& rectList, std::vector<double>& weights, int groupThreshold, double eps) const;
|
||||
};
|
||||
//! @}
|
||||
|
||||
//! @addtogroup objdetect_qrcode
|
||||
//! @{
|
||||
|
||||
@ -854,7 +247,6 @@ public:
|
||||
//! @}
|
||||
}
|
||||
|
||||
#include "opencv2/objdetect/detection_based_tracker.hpp"
|
||||
#include "opencv2/objdetect/face.hpp"
|
||||
#include "opencv2/objdetect/charuco_detector.hpp"
|
||||
#include "opencv2/objdetect/barcode.hpp"
|
||||
|
@ -1,222 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_OBJDETECT_DBT_HPP
|
||||
#define OPENCV_OBJDETECT_DBT_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
//! @addtogroup objdetect_cascade_classifier
|
||||
//! @{
|
||||
|
||||
class CV_EXPORTS DetectionBasedTracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Parameters
|
||||
{
|
||||
int maxTrackLifetime;
|
||||
int minDetectionPeriod; //the minimal time between run of the big object detector (on the whole frame) in ms (1000 mean 1 sec), default=0
|
||||
|
||||
Parameters();
|
||||
};
|
||||
|
||||
class IDetector
|
||||
{
|
||||
public:
|
||||
IDetector():
|
||||
minObjSize(96, 96),
|
||||
maxObjSize(INT_MAX, INT_MAX),
|
||||
minNeighbours(2),
|
||||
scaleFactor(1.1f)
|
||||
{}
|
||||
|
||||
virtual void detect(const cv::Mat& image, std::vector<cv::Rect>& objects) = 0;
|
||||
|
||||
void setMinObjectSize(const cv::Size& min)
|
||||
{
|
||||
minObjSize = min;
|
||||
}
|
||||
void setMaxObjectSize(const cv::Size& max)
|
||||
{
|
||||
maxObjSize = max;
|
||||
}
|
||||
cv::Size getMinObjectSize() const
|
||||
{
|
||||
return minObjSize;
|
||||
}
|
||||
cv::Size getMaxObjectSize() const
|
||||
{
|
||||
return maxObjSize;
|
||||
}
|
||||
float getScaleFactor()
|
||||
{
|
||||
return scaleFactor;
|
||||
}
|
||||
void setScaleFactor(float value)
|
||||
{
|
||||
scaleFactor = value;
|
||||
}
|
||||
int getMinNeighbours()
|
||||
{
|
||||
return minNeighbours;
|
||||
}
|
||||
void setMinNeighbours(int value)
|
||||
{
|
||||
minNeighbours = value;
|
||||
}
|
||||
virtual ~IDetector() {}
|
||||
|
||||
protected:
|
||||
cv::Size minObjSize;
|
||||
cv::Size maxObjSize;
|
||||
int minNeighbours;
|
||||
float scaleFactor;
|
||||
};
|
||||
|
||||
DetectionBasedTracker(cv::Ptr<IDetector> mainDetector, cv::Ptr<IDetector> trackingDetector, const Parameters& params);
|
||||
virtual ~DetectionBasedTracker();
|
||||
|
||||
virtual bool run();
|
||||
virtual void stop();
|
||||
virtual void resetTracking();
|
||||
|
||||
virtual void process(const cv::Mat& imageGray);
|
||||
|
||||
bool setParameters(const Parameters& params);
|
||||
const Parameters& getParameters() const;
|
||||
|
||||
|
||||
typedef std::pair<cv::Rect, int> Object;
|
||||
virtual void getObjects(std::vector<cv::Rect>& result) const;
|
||||
virtual void getObjects(std::vector<Object>& result) const;
|
||||
|
||||
enum ObjectStatus
|
||||
{
|
||||
DETECTED_NOT_SHOWN_YET,
|
||||
DETECTED,
|
||||
DETECTED_TEMPORARY_LOST,
|
||||
WRONG_OBJECT
|
||||
};
|
||||
struct ExtObject
|
||||
{
|
||||
int id;
|
||||
cv::Rect location;
|
||||
ObjectStatus status;
|
||||
ExtObject(int _id, cv::Rect _location, ObjectStatus _status)
|
||||
:id(_id), location(_location), status(_status)
|
||||
{
|
||||
}
|
||||
};
|
||||
virtual void getObjects(std::vector<ExtObject>& result) const;
|
||||
|
||||
|
||||
virtual int addObject(const cv::Rect& location); //returns id of the new object
|
||||
|
||||
protected:
|
||||
class SeparateDetectionWork;
|
||||
cv::Ptr<SeparateDetectionWork> separateDetectionWork;
|
||||
friend void* workcycleObjectDetectorFunction(void* p);
|
||||
|
||||
struct InnerParameters
|
||||
{
|
||||
int numLastPositionsToTrack;
|
||||
int numStepsToWaitBeforeFirstShow;
|
||||
int numStepsToTrackWithoutDetectingIfObjectHasNotBeenShown;
|
||||
int numStepsToShowWithoutDetecting;
|
||||
|
||||
float coeffTrackingWindowSize;
|
||||
float coeffObjectSizeToTrack;
|
||||
float coeffObjectSpeedUsingInPrediction;
|
||||
|
||||
InnerParameters();
|
||||
};
|
||||
Parameters parameters;
|
||||
InnerParameters innerParameters;
|
||||
|
||||
struct TrackedObject
|
||||
{
|
||||
typedef std::vector<cv::Rect> PositionsVector;
|
||||
|
||||
PositionsVector lastPositions;
|
||||
|
||||
int numDetectedFrames;
|
||||
int numFramesNotDetected;
|
||||
int id;
|
||||
|
||||
TrackedObject(const cv::Rect& rect):numDetectedFrames(1), numFramesNotDetected(0)
|
||||
{
|
||||
lastPositions.push_back(rect);
|
||||
id=getNextId();
|
||||
}
|
||||
|
||||
static int getNextId()
|
||||
{
|
||||
static int _id=0;
|
||||
return _id++;
|
||||
}
|
||||
};
|
||||
|
||||
int numTrackedSteps;
|
||||
std::vector<TrackedObject> trackedObjects;
|
||||
|
||||
std::vector<float> weightsPositionsSmoothing;
|
||||
std::vector<float> weightsSizesSmoothing;
|
||||
|
||||
cv::Ptr<IDetector> cascadeForTracking;
|
||||
|
||||
void updateTrackedObjects(const std::vector<cv::Rect>& detectedObjects);
|
||||
cv::Rect calcTrackedObjectPositionToShow(int i) const;
|
||||
cv::Rect calcTrackedObjectPositionToShow(int i, ObjectStatus& status) const;
|
||||
void detectInRegion(const cv::Mat& img, const cv::Rect& r, std::vector<cv::Rect>& detectedObjectsInRegions);
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
} //end of cv namespace
|
||||
|
||||
#endif
|
@ -1,104 +0,0 @@
|
||||
package org.opencv.test.objdetect;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfRect;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.objdetect.CascadeClassifier;
|
||||
import org.opencv.objdetect.Objdetect;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
import org.opencv.test.OpenCVTestRunner;
|
||||
|
||||
public class CascadeClassifierTest extends OpenCVTestCase {
|
||||
|
||||
private CascadeClassifier cc;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
cc = null;
|
||||
}
|
||||
|
||||
public void testCascadeClassifier() {
|
||||
cc = new CascadeClassifier();
|
||||
assertNotNull(cc);
|
||||
}
|
||||
|
||||
public void testCascadeClassifierString() {
|
||||
cc = new CascadeClassifier(OpenCVTestRunner.LBPCASCADE_FRONTALFACE_PATH);
|
||||
assertNotNull(cc);
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRect() {
|
||||
CascadeClassifier cc = new CascadeClassifier(OpenCVTestRunner.LBPCASCADE_FRONTALFACE_PATH);
|
||||
MatOfRect faces = new MatOfRect();
|
||||
|
||||
Mat greyLena = new Mat();
|
||||
Imgproc.cvtColor(rgbLena, greyLena, Imgproc.COLOR_RGB2GRAY);
|
||||
Imgproc.equalizeHist(greyLena, greyLena);
|
||||
|
||||
cc.detectMultiScale(greyLena, faces, 1.1, 3, Objdetect.CASCADE_SCALE_IMAGE, new Size(30, 30), new Size());
|
||||
assertEquals(1, faces.total());
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleIntIntSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleIntIntSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDoubleDoubleInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDoubleDoubleIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDoubleDoubleIntIntSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDoubleDoubleIntIntSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfIntegerListOfDoubleDoubleIntIntSizeSizeBoolean() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testEmpty() {
|
||||
cc = new CascadeClassifier();
|
||||
assertTrue(cc.empty());
|
||||
}
|
||||
|
||||
public void testLoad() {
|
||||
cc = new CascadeClassifier();
|
||||
cc.load(OpenCVTestRunner.LBPCASCADE_FRONTALFACE_PATH);
|
||||
assertFalse(cc.empty());
|
||||
}
|
||||
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
package org.opencv.test.objdetect;
|
||||
|
||||
import org.opencv.objdetect.HOGDescriptor;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
public class HOGDescriptorTest extends OpenCVTestCase {
|
||||
|
||||
public void testCheckDetectorSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeGradientMatMatMat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeGradientMatMatMatSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeGradientMatMatMatSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeMatListOfFloat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeMatListOfFloatSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeMatListOfFloatSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeMatListOfFloatSizeSizeListOfPoint() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPoint() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointDoubleSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointDoubleSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointDoubleSizeSizeListOfPoint() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointListOfDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointListOfDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointListOfDoubleDoubleSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointListOfDoubleDoubleSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMatListOfPointListOfDoubleDoubleSizeSizeListOfPoint() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRect() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleSizeSizeDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleSizeSizeDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectDoubleSizeSizeDoubleDoubleBoolean() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDoubleDoubleSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDoubleDoubleSizeSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDoubleDoubleSizeSizeDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDoubleDoubleSizeSizeDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testDetectMultiScaleMatListOfRectListOfDoubleDoubleSizeSizeDoubleDoubleBoolean() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_blockSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_blockStride() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_cellSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_derivAperture() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_gammaCorrection() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_histogramNormType() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_L2HysThreshold() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_nbins() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_nlevels() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_svmDetector() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_winSigma() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_winSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGetDaimlerPeopleDetector() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGetDefaultPeopleDetector() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGetDescriptorSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGetWinSigma() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptor() {
|
||||
HOGDescriptor hog = new HOGDescriptor();
|
||||
|
||||
assertNotNull(hog);
|
||||
assertEquals(HOGDescriptor.DEFAULT_NLEVELS, hog.get_nlevels());
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeIntIntDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeIntIntDoubleInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeIntIntDoubleIntDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeIntIntDoubleIntDoubleBoolean() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorSizeSizeSizeSizeIntIntDoubleIntDoubleBooleanInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testHOGDescriptorString() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testLoadString() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testLoadStringString() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSaveString() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSaveStringString() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSetSVMDetector() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package org.opencv.test.objdetect;
|
||||
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
public class ObjdetectTest extends OpenCVTestCase {
|
||||
|
||||
public void testGroupRectanglesListOfRectListOfIntegerInt() {
|
||||
fail("Not yet implemented");
|
||||
/*
|
||||
final int NUM = 10;
|
||||
MatOfRect rects = new MatOfRect();
|
||||
rects.alloc(NUM);
|
||||
|
||||
for (int i = 0; i < NUM; i++)
|
||||
rects.put(i, 0, 10, 10, 20, 20);
|
||||
|
||||
int groupThreshold = 1;
|
||||
Objdetect.groupRectangles(rects, null, groupThreshold);//TODO: second parameter should not be null
|
||||
assertEquals(1, rects.total());
|
||||
*/
|
||||
}
|
||||
|
||||
public void testGroupRectanglesListOfRectListOfIntegerIntDouble() {
|
||||
fail("Not yet implemented");
|
||||
/*
|
||||
final int NUM = 10;
|
||||
MatOfRect rects = new MatOfRect();
|
||||
rects.alloc(NUM);
|
||||
|
||||
for (int i = 0; i < NUM; i++)
|
||||
rects.put(i, 0, 10, 10, 20, 20);
|
||||
|
||||
for (int i = 0; i < NUM; i++)
|
||||
rects.put(i, 0, 10, 10, 25, 25);
|
||||
|
||||
int groupThreshold = 1;
|
||||
double eps = 0.2;
|
||||
Objdetect.groupRectangles(rects, null, groupThreshold, eps);//TODO: second parameter should not be null
|
||||
assertEquals(2, rects.size());
|
||||
*/
|
||||
}
|
||||
}
|
@ -4,7 +4,4 @@
|
||||
|
||||
typedef QRCodeEncoder::Params QRCodeEncoder_Params;
|
||||
|
||||
typedef HOGDescriptor::HistogramNormType HOGDescriptor_HistogramNormType;
|
||||
typedef HOGDescriptor::DescriptorStorageFormat HOGDescriptor_DescriptorStorageFormat;
|
||||
|
||||
#endif
|
||||
|
@ -1,92 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
face detection using haar cascades
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
def detect(img, cascade):
|
||||
rects = cascade.detectMultiScale(img, scaleFactor=1.275, minNeighbors=4, minSize=(30, 30),
|
||||
flags=cv.CASCADE_SCALE_IMAGE)
|
||||
if len(rects) == 0:
|
||||
return []
|
||||
rects[:,2:] += rects[:,:2]
|
||||
return rects
|
||||
|
||||
from tests_common import NewOpenCVTests, intersectionRate
|
||||
|
||||
class facedetect_test(NewOpenCVTests):
|
||||
|
||||
def test_facedetect(self):
|
||||
cascade_fn = self.repoPath + '/data/haarcascades/haarcascade_frontalface_alt.xml'
|
||||
nested_fn = self.repoPath + '/data/haarcascades/haarcascade_eye.xml'
|
||||
|
||||
cascade = cv.CascadeClassifier(cascade_fn)
|
||||
nested = cv.CascadeClassifier(nested_fn)
|
||||
|
||||
samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png']
|
||||
|
||||
faces = []
|
||||
eyes = []
|
||||
|
||||
testFaces = [
|
||||
#lena
|
||||
[[218, 200, 389, 371],
|
||||
[ 244, 240, 294, 290],
|
||||
[ 309, 246, 352, 289]],
|
||||
|
||||
#lisa
|
||||
[[167, 119, 307, 259],
|
||||
[188, 153, 229, 194],
|
||||
[236, 153, 277, 194]]
|
||||
]
|
||||
|
||||
for sample in samples:
|
||||
|
||||
img = self.get_sample( sample)
|
||||
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
|
||||
gray = cv.GaussianBlur(gray, (5, 5), 0)
|
||||
|
||||
rects = detect(gray, cascade)
|
||||
faces.append(rects)
|
||||
|
||||
if not nested.empty():
|
||||
for x1, y1, x2, y2 in rects:
|
||||
roi = gray[y1:y2, x1:x2]
|
||||
subrects = detect(roi.copy(), nested)
|
||||
|
||||
for rect in subrects:
|
||||
rect[0] += x1
|
||||
rect[2] += x1
|
||||
rect[1] += y1
|
||||
rect[3] += y1
|
||||
|
||||
eyes.append(subrects)
|
||||
|
||||
faces_matches = 0
|
||||
eyes_matches = 0
|
||||
|
||||
eps = 0.8
|
||||
|
||||
for i in range(len(faces)):
|
||||
for j in range(len(testFaces)):
|
||||
if intersectionRate(faces[i][0], testFaces[j][0]) > eps:
|
||||
faces_matches += 1
|
||||
#check eyes
|
||||
if len(eyes[i]) == 2:
|
||||
if intersectionRate(eyes[i][0], testFaces[j][1]) > eps and intersectionRate(eyes[i][1] , testFaces[j][2]) > eps:
|
||||
eyes_matches += 1
|
||||
elif intersectionRate(eyes[i][1], testFaces[j][1]) > eps and intersectionRate(eyes[i][0], testFaces[j][2]) > eps:
|
||||
eyes_matches += 1
|
||||
|
||||
self.assertEqual(faces_matches, 2)
|
||||
self.assertEqual(eyes_matches, 2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
example to detect upright people in images using HOG features
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
|
||||
def inside(r, q):
|
||||
rx, ry, rw, rh = r
|
||||
qx, qy, qw, qh = q
|
||||
return rx > qx and ry > qy and rx + rw < qx + qw and ry + rh < qy + qh
|
||||
|
||||
from tests_common import NewOpenCVTests, intersectionRate
|
||||
|
||||
class peopledetect_test(NewOpenCVTests):
|
||||
def test_peopledetect(self):
|
||||
|
||||
hog = cv.HOGDescriptor( (48, 96) )
|
||||
hog.setSVMDetector( cv.HOGDescriptor_getDaimlerPeopleDetector() )
|
||||
|
||||
dirPath = 'samples/data/'
|
||||
samples = ['basketball1.png', 'basketball2.png']
|
||||
|
||||
testPeople = [
|
||||
[[23, 76, 164, 477], [440, 22, 637, 478]],
|
||||
[[23, 76, 164, 477], [440, 22, 637, 478]]
|
||||
]
|
||||
|
||||
eps = 0.5
|
||||
|
||||
for sample in samples:
|
||||
|
||||
img = self.get_sample(dirPath + sample, 0)
|
||||
|
||||
found, _w = hog.detectMultiScale(img, winStride=(8,8), padding=(32,32), scale=1.05)
|
||||
found_filtered = []
|
||||
for ri, r in enumerate(found):
|
||||
for qi, q in enumerate(found):
|
||||
if ri != qi and inside(r, q):
|
||||
break
|
||||
else:
|
||||
found_filtered.append(r)
|
||||
|
||||
matches = 0
|
||||
|
||||
for i in range(len(found_filtered)):
|
||||
for j in range(len(testPeople)):
|
||||
|
||||
found_rect = (found_filtered[i][0], found_filtered[i][1],
|
||||
found_filtered[i][0] + found_filtered[i][2],
|
||||
found_filtered[i][1] + found_filtered[i][3])
|
||||
|
||||
if intersectionRate(found_rect, testPeople[j][0]) > eps or intersectionRate(found_rect, testPeople[j][1]) > eps:
|
||||
matches += 1
|
||||
|
||||
self.assertGreater(matches, 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
@ -1,61 +0,0 @@
|
||||
#include "../perf_precomp.hpp"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
|
||||
typedef tuple<std::string, std::string, int> Cascade_Image_MinSize_t;
|
||||
typedef perf::TestBaseWithParam<Cascade_Image_MinSize_t> Cascade_Image_MinSize;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
OCL_PERF_TEST_P(Cascade_Image_MinSize, CascadeClassifier,
|
||||
testing::Combine(
|
||||
testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml"),
|
||||
string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt2.xml"),
|
||||
string("cv/cascadeandhog/cascades/lbpcascade_frontalface.xml") ),
|
||||
testing::Values( string("cv/shared/lena.png"),
|
||||
string("cv/cascadeandhog/images/bttf301.png"),
|
||||
string("cv/cascadeandhog/images/class57.png") ),
|
||||
testing::Values(30, 64, 90) ) )
|
||||
{
|
||||
const string cascadePath = get<0>(GetParam());
|
||||
const string imagePath = get<1>(GetParam());
|
||||
int min_size = get<2>(GetParam());
|
||||
Size minSize(min_size, min_size);
|
||||
|
||||
CascadeClassifier cc( getDataPath(cascadePath) );
|
||||
if (cc.empty())
|
||||
FAIL() << "Can't load cascade file: " << getDataPath(cascadePath);
|
||||
|
||||
Mat img = imread(getDataPath(imagePath), IMREAD_GRAYSCALE);
|
||||
if (img.empty())
|
||||
FAIL() << "Can't load source image: " << getDataPath(imagePath);
|
||||
|
||||
vector<Rect> faces;
|
||||
|
||||
equalizeHist(img, img);
|
||||
declare.in(img).time(60);
|
||||
|
||||
UMat uimg = img.getUMat(ACCESS_READ);
|
||||
|
||||
while(next())
|
||||
{
|
||||
faces.clear();
|
||||
cvtest::ocl::perf::safeFinish();
|
||||
|
||||
startTimer();
|
||||
cc.detectMultiScale(uimg, faces, 1.1, 3, 0, minSize);
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
sort(faces.begin(), faces.end(), comparators::RectLess());
|
||||
SANITY_CHECK(faces, min_size/5);
|
||||
}
|
||||
|
||||
#endif //HAVE_OPENCL
|
||||
|
||||
} // namespace
|
@ -1,93 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Fangfang Bai, fangfang@multicorewareinc.com
|
||||
// Jin Ma, jin@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
///////////// HOG////////////////////////
|
||||
|
||||
struct RectLess
|
||||
{
|
||||
bool operator()(const cv::Rect& a,
|
||||
const cv::Rect& b) const
|
||||
{
|
||||
if (a.x != b.x)
|
||||
return a.x < b.x;
|
||||
else if (a.y != b.y)
|
||||
return a.y < b.y;
|
||||
else if (a.width != b.width)
|
||||
return a.width < b.width;
|
||||
else
|
||||
return a.height < b.height;
|
||||
}
|
||||
};
|
||||
|
||||
OCL_PERF_TEST(HOGFixture, HOG)
|
||||
{
|
||||
UMat src;
|
||||
imread(getDataPath("gpu/hog/road.png"), cv::IMREAD_GRAYSCALE).copyTo(src);
|
||||
ASSERT_FALSE(src.empty());
|
||||
|
||||
vector<cv::Rect> found_locations;
|
||||
declare.in(src);
|
||||
|
||||
HOGDescriptor hog;
|
||||
hog.setSVMDetector(hog.getDefaultPeopleDetector());
|
||||
|
||||
OCL_TEST_CYCLE() hog.detectMultiScale(src, found_locations);
|
||||
|
||||
std::sort(found_locations.begin(), found_locations.end(), RectLess());
|
||||
SANITY_CHECK(found_locations, 3);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,656 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
void clipObjects(Size sz, std::vector<Rect>& objects,
|
||||
std::vector<int>* a, std::vector<double>* b);
|
||||
|
||||
class FeatureEvaluator
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
HAAR = 0,
|
||||
LBP = 1,
|
||||
HOG = 2
|
||||
};
|
||||
|
||||
struct ScaleData
|
||||
{
|
||||
ScaleData() { scale = 0.f; layer_ofs = ystep = 0; }
|
||||
Size getWorkingSize(Size winSize) const
|
||||
{
|
||||
return Size(std::max(szi.width - winSize.width, 0),
|
||||
std::max(szi.height - winSize.height, 0));
|
||||
}
|
||||
|
||||
float scale;
|
||||
Size szi;
|
||||
int layer_ofs, ystep;
|
||||
};
|
||||
|
||||
virtual ~FeatureEvaluator();
|
||||
|
||||
virtual bool read(const FileNode& node, Size origWinSize);
|
||||
virtual Ptr<FeatureEvaluator> clone() const;
|
||||
virtual int getFeatureType() const;
|
||||
int getNumChannels() const { return nchannels; }
|
||||
|
||||
virtual bool setImage(InputArray img, const std::vector<float>& scales);
|
||||
virtual bool setWindow(Point p, int scaleIdx);
|
||||
const ScaleData& getScaleData(int scaleIdx) const
|
||||
{
|
||||
CV_Assert( 0 <= scaleIdx && scaleIdx < (int)scaleData->size());
|
||||
return scaleData->at(scaleIdx);
|
||||
}
|
||||
virtual void getUMats(std::vector<UMat>& bufs);
|
||||
virtual void getMats();
|
||||
|
||||
Size getLocalSize() const { return localSize; }
|
||||
Size getLocalBufSize() const { return lbufSize; }
|
||||
|
||||
virtual float calcOrd(int featureIdx) const;
|
||||
virtual int calcCat(int featureIdx) const;
|
||||
|
||||
static Ptr<FeatureEvaluator> create(int type);
|
||||
|
||||
protected:
|
||||
enum { SBUF_VALID=1, USBUF_VALID=2 };
|
||||
int sbufFlag;
|
||||
|
||||
bool updateScaleData( Size imgsz, const std::vector<float>& _scales );
|
||||
virtual void computeChannels( int, InputArray ) {}
|
||||
virtual void computeOptFeatures() {}
|
||||
|
||||
Size origWinSize, sbufSize, localSize, lbufSize;
|
||||
int nchannels;
|
||||
Mat sbuf, rbuf;
|
||||
UMat urbuf, usbuf, ufbuf, uscaleData;
|
||||
|
||||
Ptr<std::vector<ScaleData> > scaleData;
|
||||
};
|
||||
|
||||
|
||||
class CascadeClassifierImpl CV_FINAL : public BaseCascadeClassifier
|
||||
{
|
||||
public:
|
||||
CascadeClassifierImpl();
|
||||
virtual ~CascadeClassifierImpl() CV_OVERRIDE;
|
||||
|
||||
bool empty() const CV_OVERRIDE;
|
||||
bool load( const String& filename ) CV_OVERRIDE;
|
||||
void read( const FileNode& node ) CV_OVERRIDE;
|
||||
bool read_( const FileNode& node );
|
||||
void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
double scaleFactor = 1.1,
|
||||
int minNeighbors = 3, int flags = 0,
|
||||
Size minSize = Size(),
|
||||
Size maxSize = Size() ) CV_OVERRIDE;
|
||||
|
||||
void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
CV_OUT std::vector<int>& numDetections,
|
||||
double scaleFactor=1.1,
|
||||
int minNeighbors=3, int flags=0,
|
||||
Size minSize=Size(),
|
||||
Size maxSize=Size() ) CV_OVERRIDE;
|
||||
|
||||
void detectMultiScale( InputArray image,
|
||||
CV_OUT std::vector<Rect>& objects,
|
||||
CV_OUT std::vector<int>& rejectLevels,
|
||||
CV_OUT std::vector<double>& levelWeights,
|
||||
double scaleFactor = 1.1,
|
||||
int minNeighbors = 3, int flags = 0,
|
||||
Size minSize = Size(),
|
||||
Size maxSize = Size(),
|
||||
bool outputRejectLevels = false ) CV_OVERRIDE;
|
||||
|
||||
|
||||
bool isOldFormatCascade() const CV_OVERRIDE;
|
||||
Size getOriginalWindowSize() const CV_OVERRIDE;
|
||||
int getFeatureType() const CV_OVERRIDE;
|
||||
void* getOldCascade() CV_OVERRIDE;
|
||||
|
||||
void setMaskGenerator(const Ptr<MaskGenerator>& maskGenerator) CV_OVERRIDE;
|
||||
Ptr<MaskGenerator> getMaskGenerator() CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
enum { SUM_ALIGN = 64 };
|
||||
|
||||
bool detectSingleScale( InputArray image, Size processingRectSize,
|
||||
int yStep, double factor, std::vector<Rect>& candidates,
|
||||
std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
|
||||
Size sumSize0, bool outputRejectLevels = false );
|
||||
#ifdef HAVE_OPENCL
|
||||
bool ocl_detectMultiScaleNoGrouping( const std::vector<float>& scales,
|
||||
std::vector<Rect>& candidates );
|
||||
#endif
|
||||
void detectMultiScaleNoGrouping( InputArray image, std::vector<Rect>& candidates,
|
||||
std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
|
||||
double scaleFactor, Size minObjectSize, Size maxObjectSize,
|
||||
bool outputRejectLevels = false );
|
||||
|
||||
enum { MAX_FACES = 10000 };
|
||||
enum { BOOST = 0 };
|
||||
enum { DO_CANNY_PRUNING = CASCADE_DO_CANNY_PRUNING,
|
||||
SCALE_IMAGE = CASCADE_SCALE_IMAGE,
|
||||
FIND_BIGGEST_OBJECT = CASCADE_FIND_BIGGEST_OBJECT,
|
||||
DO_ROUGH_SEARCH = CASCADE_DO_ROUGH_SEARCH
|
||||
};
|
||||
|
||||
friend class CascadeClassifierInvoker;
|
||||
friend class SparseCascadeClassifierInvoker;
|
||||
|
||||
template<class FEval>
|
||||
friend int predictOrdered( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
|
||||
|
||||
template<class FEval>
|
||||
friend int predictCategorical( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
|
||||
|
||||
template<class FEval>
|
||||
friend int predictOrderedStump( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
|
||||
|
||||
template<class FEval>
|
||||
friend int predictCategoricalStump( CascadeClassifierImpl& cascade, Ptr<FeatureEvaluator> &featureEvaluator, double& weight);
|
||||
|
||||
int runAt( Ptr<FeatureEvaluator>& feval, Point pt, int scaleIdx, double& weight );
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
struct DTreeNode
|
||||
{
|
||||
int featureIdx;
|
||||
float threshold; // for ordered features only
|
||||
int left;
|
||||
int right;
|
||||
};
|
||||
|
||||
struct DTree
|
||||
{
|
||||
int nodeCount;
|
||||
};
|
||||
|
||||
struct Stage
|
||||
{
|
||||
int first;
|
||||
int ntrees;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
struct Stump
|
||||
{
|
||||
Stump() : featureIdx(0), threshold(0), left(0), right(0) { }
|
||||
Stump(int _featureIdx, float _threshold, float _left, float _right)
|
||||
: featureIdx(_featureIdx), threshold(_threshold), left(_left), right(_right) {}
|
||||
|
||||
int featureIdx;
|
||||
float threshold;
|
||||
float left;
|
||||
float right;
|
||||
};
|
||||
|
||||
Data();
|
||||
|
||||
bool read(const FileNode &node);
|
||||
|
||||
int stageType;
|
||||
int featureType;
|
||||
int ncategories;
|
||||
int minNodesPerTree, maxNodesPerTree;
|
||||
Size origWinSize;
|
||||
|
||||
std::vector<Stage> stages;
|
||||
std::vector<DTree> classifiers;
|
||||
std::vector<DTreeNode> nodes;
|
||||
std::vector<float> leaves;
|
||||
std::vector<int> subsets;
|
||||
std::vector<Stump> stumps;
|
||||
};
|
||||
|
||||
Data data;
|
||||
Ptr<FeatureEvaluator> featureEvaluator;
|
||||
Ptr<CvHaarClassifierCascade> oldCascade;
|
||||
|
||||
Ptr<MaskGenerator> maskGenerator;
|
||||
UMat ugrayImage;
|
||||
UMat ufacepos, ustages, unodes, uleaves, usubsets;
|
||||
#ifdef HAVE_OPENCL
|
||||
ocl::Kernel haarKernel, lbpKernel;
|
||||
bool tryOpenCL;
|
||||
#endif
|
||||
|
||||
Mutex mtx;
|
||||
};
|
||||
|
||||
#define CC_CASCADE_PARAMS "cascadeParams"
|
||||
#define CC_STAGE_TYPE "stageType"
|
||||
#define CC_FEATURE_TYPE "featureType"
|
||||
#define CC_HEIGHT "height"
|
||||
#define CC_WIDTH "width"
|
||||
|
||||
#define CC_STAGE_NUM "stageNum"
|
||||
#define CC_STAGES "stages"
|
||||
#define CC_STAGE_PARAMS "stageParams"
|
||||
|
||||
#define CC_BOOST "BOOST"
|
||||
#define CC_MAX_DEPTH "maxDepth"
|
||||
#define CC_WEAK_COUNT "maxWeakCount"
|
||||
#define CC_STAGE_THRESHOLD "stageThreshold"
|
||||
#define CC_WEAK_CLASSIFIERS "weakClassifiers"
|
||||
#define CC_INTERNAL_NODES "internalNodes"
|
||||
#define CC_LEAF_VALUES "leafValues"
|
||||
|
||||
#define CC_FEATURES "features"
|
||||
#define CC_FEATURE_PARAMS "featureParams"
|
||||
#define CC_MAX_CAT_COUNT "maxCatCount"
|
||||
|
||||
#define CC_HAAR "HAAR"
|
||||
#define CC_RECTS "rects"
|
||||
#define CC_TILTED "tilted"
|
||||
|
||||
#define CC_LBP "LBP"
|
||||
#define CC_RECT "rect"
|
||||
|
||||
#define CC_HOG "HOG"
|
||||
|
||||
#define CV_SUM_PTRS( p0, p1, p2, p3, sum, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = sum + (rect).x + (step) * (rect).y, \
|
||||
/* (x + w, y) */ \
|
||||
(p1) = sum + (rect).x + (rect).width + (step) * (rect).y, \
|
||||
/* (x, y + h) */ \
|
||||
(p2) = sum + (rect).x + (step) * ((rect).y + (rect).height), \
|
||||
/* (x + w, y + h) */ \
|
||||
(p3) = sum + (rect).x + (rect).width + (step) * ((rect).y + (rect).height)
|
||||
|
||||
#define CV_TILTED_PTRS( p0, p1, p2, p3, tilted, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = tilted + (rect).x + (step) * (rect).y, \
|
||||
/* (x - h, y + h) */ \
|
||||
(p1) = tilted + (rect).x - (rect).height + (step) * ((rect).y + (rect).height), \
|
||||
/* (x + w, y + w) */ \
|
||||
(p2) = tilted + (rect).x + (rect).width + (step) * ((rect).y + (rect).width), \
|
||||
/* (x + w - h, y + w + h) */ \
|
||||
(p3) = tilted + (rect).x + (rect).width - (rect).height \
|
||||
+ (step) * ((rect).y + (rect).width + (rect).height)
|
||||
|
||||
#define CALC_SUM_(p0, p1, p2, p3, offset) \
|
||||
((p0)[offset] - (p1)[offset] - (p2)[offset] + (p3)[offset])
|
||||
|
||||
#define CALC_SUM(rect,offset) CALC_SUM_((rect)[0], (rect)[1], (rect)[2], (rect)[3], offset)
|
||||
|
||||
#define CV_SUM_OFS( p0, p1, p2, p3, sum, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = sum + (rect).x + (step) * (rect).y, \
|
||||
/* (x + w, y) */ \
|
||||
(p1) = sum + (rect).x + (rect).width + (step) * (rect).y, \
|
||||
/* (x, y + h) */ \
|
||||
(p2) = sum + (rect).x + (step) * ((rect).y + (rect).height), \
|
||||
/* (x + w, y + h) */ \
|
||||
(p3) = sum + (rect).x + (rect).width + (step) * ((rect).y + (rect).height)
|
||||
|
||||
#define CV_TILTED_OFS( p0, p1, p2, p3, tilted, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = tilted + (rect).x + (step) * (rect).y, \
|
||||
/* (x - h, y + h) */ \
|
||||
(p1) = tilted + (rect).x - (rect).height + (step) * ((rect).y + (rect).height), \
|
||||
/* (x + w, y + w) */ \
|
||||
(p2) = tilted + (rect).x + (rect).width + (step) * ((rect).y + (rect).width), \
|
||||
/* (x + w - h, y + w + h) */ \
|
||||
(p3) = tilted + (rect).x + (rect).width - (rect).height \
|
||||
+ (step) * ((rect).y + (rect).width + (rect).height)
|
||||
|
||||
#define CALC_SUM_OFS_(p0, p1, p2, p3, ptr) \
|
||||
((ptr)[p0] - (ptr)[p1] - (ptr)[p2] + (ptr)[p3])
|
||||
|
||||
#define CALC_SUM_OFS(rect, ptr) CALC_SUM_OFS_((rect)[0], (rect)[1], (rect)[2], (rect)[3], ptr)
|
||||
|
||||
//---------------------------------------------- HaarEvaluator ---------------------------------------
|
||||
class HaarEvaluator CV_FINAL : public FeatureEvaluator
|
||||
{
|
||||
public:
|
||||
struct Feature
|
||||
{
|
||||
Feature();
|
||||
bool read(const FileNode& node, const Size& origWinSize);
|
||||
|
||||
bool tilted;
|
||||
|
||||
enum { RECT_NUM = 3 };
|
||||
struct RectWeigth
|
||||
{
|
||||
Rect r;
|
||||
float weight;
|
||||
} rect[RECT_NUM];
|
||||
};
|
||||
|
||||
struct OptFeature
|
||||
{
|
||||
OptFeature();
|
||||
|
||||
enum { RECT_NUM = Feature::RECT_NUM };
|
||||
float calc( const int* pwin ) const;
|
||||
void setOffsets( const Feature& _f, int step, int tofs );
|
||||
|
||||
int ofs[RECT_NUM][4];
|
||||
float weight[4];
|
||||
};
|
||||
|
||||
HaarEvaluator();
|
||||
virtual ~HaarEvaluator() CV_OVERRIDE;
|
||||
|
||||
virtual bool read( const FileNode& node, Size origWinSize) CV_OVERRIDE;
|
||||
virtual Ptr<FeatureEvaluator> clone() const CV_OVERRIDE;
|
||||
virtual int getFeatureType() const CV_OVERRIDE { return FeatureEvaluator::HAAR; }
|
||||
|
||||
virtual bool setWindow(Point p, int scaleIdx) CV_OVERRIDE;
|
||||
Rect getNormRect() const;
|
||||
int getSquaresOffset() const;
|
||||
|
||||
float operator()(int featureIdx) const
|
||||
{ return optfeaturesPtr[featureIdx].calc(pwin) * varianceNormFactor; }
|
||||
virtual float calcOrd(int featureIdx) const CV_OVERRIDE
|
||||
{ return (*this)(featureIdx); }
|
||||
|
||||
protected:
|
||||
virtual void computeChannels( int i, InputArray img ) CV_OVERRIDE;
|
||||
virtual void computeOptFeatures() CV_OVERRIDE;
|
||||
|
||||
Ptr<std::vector<Feature> > features;
|
||||
Ptr<std::vector<OptFeature> > optfeatures;
|
||||
Ptr<std::vector<OptFeature> > optfeatures_lbuf;
|
||||
bool hasTiltedFeatures;
|
||||
|
||||
int tofs, sqofs;
|
||||
Vec4i nofs;
|
||||
Rect normrect;
|
||||
const int* pwin;
|
||||
OptFeature* optfeaturesPtr; // optimization
|
||||
float varianceNormFactor;
|
||||
};
|
||||
|
||||
inline HaarEvaluator::Feature :: Feature()
|
||||
{
|
||||
tilted = false;
|
||||
rect[0].r = rect[1].r = rect[2].r = Rect();
|
||||
rect[0].weight = rect[1].weight = rect[2].weight = 0;
|
||||
}
|
||||
|
||||
inline HaarEvaluator::OptFeature :: OptFeature()
|
||||
{
|
||||
weight[0] = weight[1] = weight[2] = 0.f;
|
||||
|
||||
ofs[0][0] = ofs[0][1] = ofs[0][2] = ofs[0][3] =
|
||||
ofs[1][0] = ofs[1][1] = ofs[1][2] = ofs[1][3] =
|
||||
ofs[2][0] = ofs[2][1] = ofs[2][2] = ofs[2][3] = 0;
|
||||
}
|
||||
|
||||
inline float HaarEvaluator::OptFeature :: calc( const int* ptr ) const
|
||||
{
|
||||
float ret = weight[0] * CALC_SUM_OFS(ofs[0], ptr) +
|
||||
weight[1] * CALC_SUM_OFS(ofs[1], ptr);
|
||||
|
||||
if( weight[2] != 0.0f )
|
||||
ret += weight[2] * CALC_SUM_OFS(ofs[2], ptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//---------------------------------------------- LBPEvaluator -------------------------------------
|
||||
|
||||
class LBPEvaluator CV_FINAL : public FeatureEvaluator
|
||||
{
|
||||
public:
|
||||
struct Feature
|
||||
{
|
||||
Feature();
|
||||
Feature( int x, int y, int _block_w, int _block_h ) :
|
||||
rect(x, y, _block_w, _block_h) {}
|
||||
|
||||
bool read(const FileNode& node, const Size& origWinSize);
|
||||
|
||||
Rect rect; // weight and height for block
|
||||
};
|
||||
|
||||
struct OptFeature
|
||||
{
|
||||
OptFeature();
|
||||
|
||||
int calc( const int* pwin ) const;
|
||||
void setOffsets( const Feature& _f, int step );
|
||||
int ofs[16];
|
||||
};
|
||||
|
||||
LBPEvaluator();
|
||||
virtual ~LBPEvaluator() CV_OVERRIDE;
|
||||
|
||||
virtual bool read( const FileNode& node, Size origWinSize ) CV_OVERRIDE;
|
||||
virtual Ptr<FeatureEvaluator> clone() const CV_OVERRIDE;
|
||||
virtual int getFeatureType() const CV_OVERRIDE { return FeatureEvaluator::LBP; }
|
||||
|
||||
virtual bool setWindow(Point p, int scaleIdx) CV_OVERRIDE;
|
||||
|
||||
int operator()(int featureIdx) const
|
||||
{ return optfeaturesPtr[featureIdx].calc(pwin); }
|
||||
virtual int calcCat(int featureIdx) const CV_OVERRIDE
|
||||
{ return (*this)(featureIdx); }
|
||||
protected:
|
||||
virtual void computeChannels( int i, InputArray img ) CV_OVERRIDE;
|
||||
virtual void computeOptFeatures() CV_OVERRIDE;
|
||||
|
||||
Ptr<std::vector<Feature> > features;
|
||||
Ptr<std::vector<OptFeature> > optfeatures;
|
||||
Ptr<std::vector<OptFeature> > optfeatures_lbuf;
|
||||
OptFeature* optfeaturesPtr; // optimization
|
||||
|
||||
const int* pwin;
|
||||
};
|
||||
|
||||
|
||||
inline LBPEvaluator::Feature :: Feature()
|
||||
{
|
||||
rect = Rect();
|
||||
}
|
||||
|
||||
inline LBPEvaluator::OptFeature :: OptFeature()
|
||||
{
|
||||
for( int i = 0; i < 16; i++ )
|
||||
ofs[i] = 0;
|
||||
}
|
||||
|
||||
inline int LBPEvaluator::OptFeature :: calc( const int* p ) const
|
||||
{
|
||||
int cval = CALC_SUM_OFS_( ofs[5], ofs[6], ofs[9], ofs[10], p );
|
||||
|
||||
return (CALC_SUM_OFS_( ofs[0], ofs[1], ofs[4], ofs[5], p ) >= cval ? 128 : 0) | // 0
|
||||
(CALC_SUM_OFS_( ofs[1], ofs[2], ofs[5], ofs[6], p ) >= cval ? 64 : 0) | // 1
|
||||
(CALC_SUM_OFS_( ofs[2], ofs[3], ofs[6], ofs[7], p ) >= cval ? 32 : 0) | // 2
|
||||
(CALC_SUM_OFS_( ofs[6], ofs[7], ofs[10], ofs[11], p ) >= cval ? 16 : 0) | // 5
|
||||
(CALC_SUM_OFS_( ofs[10], ofs[11], ofs[14], ofs[15], p ) >= cval ? 8 : 0)| // 8
|
||||
(CALC_SUM_OFS_( ofs[9], ofs[10], ofs[13], ofs[14], p ) >= cval ? 4 : 0)| // 7
|
||||
(CALC_SUM_OFS_( ofs[8], ofs[9], ofs[12], ofs[13], p ) >= cval ? 2 : 0)| // 6
|
||||
(CALC_SUM_OFS_( ofs[4], ofs[5], ofs[8], ofs[9], p ) >= cval ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------- predictor functions -------------------------------------
|
||||
|
||||
template<class FEval>
|
||||
inline int predictOrdered( CascadeClassifierImpl& cascade,
|
||||
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int nstages = (int)cascade.data.stages.size();
|
||||
int nodeOfs = 0, leafOfs = 0;
|
||||
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
|
||||
float* cascadeLeaves = &cascade.data.leaves[0];
|
||||
CascadeClassifierImpl::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
|
||||
CascadeClassifierImpl::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];
|
||||
CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
|
||||
|
||||
for( int si = 0; si < nstages; si++ )
|
||||
{
|
||||
CascadeClassifierImpl::Data::Stage& stage = cascadeStages[si];
|
||||
int wi, ntrees = stage.ntrees;
|
||||
sum = 0;
|
||||
|
||||
for( wi = 0; wi < ntrees; wi++ )
|
||||
{
|
||||
CascadeClassifierImpl::Data::DTree& weak = cascadeWeaks[stage.first + wi];
|
||||
int idx = 0, root = nodeOfs;
|
||||
|
||||
do
|
||||
{
|
||||
CascadeClassifierImpl::Data::DTreeNode& node = cascadeNodes[root + idx];
|
||||
double val = featureEvaluator(node.featureIdx);
|
||||
idx = val < node.threshold ? node.left : node.right;
|
||||
}
|
||||
while( idx > 0 );
|
||||
sum += cascadeLeaves[leafOfs - idx];
|
||||
nodeOfs += weak.nodeCount;
|
||||
leafOfs += weak.nodeCount + 1;
|
||||
}
|
||||
if( sum < stage.threshold )
|
||||
return -si;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<class FEval>
|
||||
inline int predictCategorical( CascadeClassifierImpl& cascade,
|
||||
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int nstages = (int)cascade.data.stages.size();
|
||||
int nodeOfs = 0, leafOfs = 0;
|
||||
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
|
||||
size_t subsetSize = (cascade.data.ncategories + 31)/32;
|
||||
int* cascadeSubsets = &cascade.data.subsets[0];
|
||||
float* cascadeLeaves = &cascade.data.leaves[0];
|
||||
CascadeClassifierImpl::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
|
||||
CascadeClassifierImpl::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];
|
||||
CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
|
||||
|
||||
for(int si = 0; si < nstages; si++ )
|
||||
{
|
||||
CascadeClassifierImpl::Data::Stage& stage = cascadeStages[si];
|
||||
int wi, ntrees = stage.ntrees;
|
||||
sum = 0;
|
||||
|
||||
for( wi = 0; wi < ntrees; wi++ )
|
||||
{
|
||||
CascadeClassifierImpl::Data::DTree& weak = cascadeWeaks[stage.first + wi];
|
||||
int idx = 0, root = nodeOfs;
|
||||
do
|
||||
{
|
||||
CascadeClassifierImpl::Data::DTreeNode& node = cascadeNodes[root + idx];
|
||||
int c = featureEvaluator(node.featureIdx);
|
||||
const int* subset = &cascadeSubsets[(root + idx)*subsetSize];
|
||||
idx = (subset[c>>5] & (1 << (c & 31))) ? node.left : node.right;
|
||||
}
|
||||
while( idx > 0 );
|
||||
sum += cascadeLeaves[leafOfs - idx];
|
||||
nodeOfs += weak.nodeCount;
|
||||
leafOfs += weak.nodeCount + 1;
|
||||
}
|
||||
if( sum < stage.threshold )
|
||||
return -si;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<class FEval>
|
||||
inline int predictOrderedStump( CascadeClassifierImpl& cascade,
|
||||
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(!cascade.data.stumps.empty());
|
||||
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
|
||||
const CascadeClassifierImpl::Data::Stump* cascadeStumps = &cascade.data.stumps[0];
|
||||
const CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
|
||||
|
||||
int nstages = (int)cascade.data.stages.size();
|
||||
double tmp = 0;
|
||||
|
||||
for( int stageIdx = 0; stageIdx < nstages; stageIdx++ )
|
||||
{
|
||||
const CascadeClassifierImpl::Data::Stage& stage = cascadeStages[stageIdx];
|
||||
tmp = 0;
|
||||
|
||||
int ntrees = stage.ntrees;
|
||||
for( int i = 0; i < ntrees; i++ )
|
||||
{
|
||||
const CascadeClassifierImpl::Data::Stump& stump = cascadeStumps[i];
|
||||
double value = featureEvaluator(stump.featureIdx);
|
||||
tmp += value < stump.threshold ? stump.left : stump.right;
|
||||
}
|
||||
|
||||
if( tmp < stage.threshold )
|
||||
{
|
||||
sum = (double)tmp;
|
||||
return -stageIdx;
|
||||
}
|
||||
cascadeStumps += ntrees;
|
||||
}
|
||||
|
||||
sum = (double)tmp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<class FEval>
|
||||
inline int predictCategoricalStump( CascadeClassifierImpl& cascade,
|
||||
Ptr<FeatureEvaluator> &_featureEvaluator, double& sum )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(!cascade.data.stumps.empty());
|
||||
int nstages = (int)cascade.data.stages.size();
|
||||
FEval& featureEvaluator = (FEval&)*_featureEvaluator;
|
||||
size_t subsetSize = (cascade.data.ncategories + 31)/32;
|
||||
const int* cascadeSubsets = &cascade.data.subsets[0];
|
||||
const CascadeClassifierImpl::Data::Stump* cascadeStumps = &cascade.data.stumps[0];
|
||||
const CascadeClassifierImpl::Data::Stage* cascadeStages = &cascade.data.stages[0];
|
||||
|
||||
double tmp = 0;
|
||||
for( int si = 0; si < nstages; si++ )
|
||||
{
|
||||
const CascadeClassifierImpl::Data::Stage& stage = cascadeStages[si];
|
||||
int wi, ntrees = stage.ntrees;
|
||||
tmp = 0;
|
||||
|
||||
for( wi = 0; wi < ntrees; wi++ )
|
||||
{
|
||||
const CascadeClassifierImpl::Data::Stump& stump = cascadeStumps[wi];
|
||||
int c = featureEvaluator(stump.featureIdx);
|
||||
const int* subset = &cascadeSubsets[wi*subsetSize];
|
||||
tmp += (subset[c>>5] & (1 << (c & 31))) ? stump.left : stump.right;
|
||||
}
|
||||
|
||||
if( tmp < stage.threshold )
|
||||
{
|
||||
sum = tmp;
|
||||
return -si;
|
||||
}
|
||||
|
||||
cascadeStumps += ntrees;
|
||||
cascadeSubsets += ntrees*subsetSize;
|
||||
}
|
||||
|
||||
sum = (double)tmp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace haar_cvt
|
||||
{
|
||||
bool convert(const FileNode& oldcascade_root, FileStorage& newfs);
|
||||
}
|
||||
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, Itseez Inc, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
/* Haar features calculation */
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "cascadedetect.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
/* field names */
|
||||
|
||||
#define ICV_HAAR_SIZE_NAME "size"
|
||||
#define ICV_HAAR_STAGES_NAME "stages"
|
||||
#define ICV_HAAR_TREES_NAME "trees"
|
||||
#define ICV_HAAR_FEATURE_NAME "feature"
|
||||
#define ICV_HAAR_RECTS_NAME "rects"
|
||||
#define ICV_HAAR_TILTED_NAME "tilted"
|
||||
#define ICV_HAAR_THRESHOLD_NAME "threshold"
|
||||
#define ICV_HAAR_LEFT_NODE_NAME "left_node"
|
||||
#define ICV_HAAR_LEFT_VAL_NAME "left_val"
|
||||
#define ICV_HAAR_RIGHT_NODE_NAME "right_node"
|
||||
#define ICV_HAAR_RIGHT_VAL_NAME "right_val"
|
||||
#define ICV_HAAR_STAGE_THRESHOLD_NAME "stage_threshold"
|
||||
#define ICV_HAAR_PARENT_NAME "parent"
|
||||
#define ICV_HAAR_NEXT_NAME "next"
|
||||
|
||||
namespace haar_cvt
|
||||
{
|
||||
|
||||
struct HaarFeature
|
||||
{
|
||||
enum { RECT_NUM = 3 };
|
||||
|
||||
HaarFeature()
|
||||
{
|
||||
tilted = false;
|
||||
for( int i = 0; i < RECT_NUM; i++ )
|
||||
{
|
||||
rect[i].r = Rect(0,0,0,0);
|
||||
rect[i].weight = 0.f;
|
||||
}
|
||||
}
|
||||
bool tilted;
|
||||
struct
|
||||
{
|
||||
Rect r;
|
||||
float weight;
|
||||
} rect[RECT_NUM];
|
||||
};
|
||||
|
||||
struct HaarClassifierNode
|
||||
{
|
||||
HaarClassifierNode()
|
||||
{
|
||||
f = left = right = 0;
|
||||
threshold = 0.f;
|
||||
}
|
||||
int f, left, right;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
struct HaarClassifier
|
||||
{
|
||||
std::vector<HaarClassifierNode> nodes;
|
||||
std::vector<float> leaves;
|
||||
};
|
||||
|
||||
struct HaarStageClassifier
|
||||
{
|
||||
HaarStageClassifier() : threshold(0) {}
|
||||
|
||||
double threshold;
|
||||
std::vector<HaarClassifier> weaks;
|
||||
};
|
||||
|
||||
bool convert(const FileNode& oldroot, FileStorage& newfs)
|
||||
{
|
||||
FileNode sznode = oldroot[ICV_HAAR_SIZE_NAME];
|
||||
if( sznode.empty() )
|
||||
return false;
|
||||
Size cascadesize;
|
||||
cascadesize.width = (int)sznode[0];
|
||||
cascadesize.height = (int)sznode[1];
|
||||
std::vector<HaarFeature> features;
|
||||
|
||||
int i, j, k, n;
|
||||
|
||||
FileNode stages_seq = oldroot[ICV_HAAR_STAGES_NAME];
|
||||
int nstages = (int)stages_seq.size();
|
||||
std::vector<HaarStageClassifier> stages(nstages);
|
||||
|
||||
for( i = 0; i < nstages; i++ )
|
||||
{
|
||||
FileNode stagenode = stages_seq[i];
|
||||
HaarStageClassifier& stage = stages[i];
|
||||
stage.threshold = (double)stagenode[ICV_HAAR_STAGE_THRESHOLD_NAME];
|
||||
FileNode weaks_seq = stagenode[ICV_HAAR_TREES_NAME];
|
||||
int nweaks = (int)weaks_seq.size();
|
||||
stage.weaks.resize(nweaks);
|
||||
|
||||
for( j = 0; j < nweaks; j++ )
|
||||
{
|
||||
HaarClassifier& weak = stage.weaks[j];
|
||||
FileNode weaknode = weaks_seq[j];
|
||||
int nnodes = (int)weaknode.size();
|
||||
|
||||
for( n = 0; n < nnodes; n++ )
|
||||
{
|
||||
FileNode nnode = weaknode[n];
|
||||
FileNode fnode = nnode[ICV_HAAR_FEATURE_NAME];
|
||||
HaarFeature f;
|
||||
HaarClassifierNode node;
|
||||
node.f = (int)features.size();
|
||||
f.tilted = (int)fnode[ICV_HAAR_TILTED_NAME] != 0;
|
||||
FileNode rects_seq = fnode[ICV_HAAR_RECTS_NAME];
|
||||
int nrects = (int)rects_seq.size();
|
||||
|
||||
for( k = 0; k < nrects; k++ )
|
||||
{
|
||||
FileNode rnode = rects_seq[k];
|
||||
f.rect[k].r.x = (int)rnode[0];
|
||||
f.rect[k].r.y = (int)rnode[1];
|
||||
f.rect[k].r.width = (int)rnode[2];
|
||||
f.rect[k].r.height = (int)rnode[3];
|
||||
f.rect[k].weight = (float)rnode[4];
|
||||
}
|
||||
features.push_back(f);
|
||||
node.threshold = nnode[ICV_HAAR_THRESHOLD_NAME];
|
||||
FileNode leftValNode = nnode[ICV_HAAR_LEFT_VAL_NAME];
|
||||
if( !leftValNode.empty() )
|
||||
{
|
||||
node.left = -(int)weak.leaves.size();
|
||||
weak.leaves.push_back((float)leftValNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.left = (int)nnode[ICV_HAAR_LEFT_NODE_NAME];
|
||||
}
|
||||
FileNode rightValNode = nnode[ICV_HAAR_RIGHT_VAL_NAME];
|
||||
if( !rightValNode.empty() )
|
||||
{
|
||||
node.right = -(int)weak.leaves.size();
|
||||
weak.leaves.push_back((float)rightValNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.right = (int)nnode[ICV_HAAR_RIGHT_NODE_NAME];
|
||||
}
|
||||
weak.nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int maxWeakCount = 0, nfeatures = (int)features.size();
|
||||
for( i = 0; i < nstages; i++ )
|
||||
maxWeakCount = std::max(maxWeakCount, (int)stages[i].weaks.size());
|
||||
|
||||
newfs << "cascade" << "{:opencv-cascade-classifier"
|
||||
<< "stageType" << "BOOST"
|
||||
<< "featureType" << "HAAR"
|
||||
<< "width" << cascadesize.width
|
||||
<< "height" << cascadesize.height
|
||||
<< "stageParams" << "{"
|
||||
<< "maxWeakCount" << (int)maxWeakCount
|
||||
<< "}"
|
||||
<< "featureParams" << "{"
|
||||
<< "maxCatCount" << 0
|
||||
<< "}"
|
||||
<< "stageNum" << (int)nstages
|
||||
<< "stages" << "[";
|
||||
|
||||
for( i = 0; i < nstages; i++ )
|
||||
{
|
||||
int nweaks = (int)stages[i].weaks.size();
|
||||
newfs << "{" << "maxWeakCount" << (int)nweaks
|
||||
<< "stageThreshold" << stages[i].threshold
|
||||
<< "weakClassifiers" << "[";
|
||||
for( j = 0; j < nweaks; j++ )
|
||||
{
|
||||
const HaarClassifier& c = stages[i].weaks[j];
|
||||
newfs << "{" << "internalNodes" << "[:";
|
||||
int nnodes = (int)c.nodes.size(), nleaves = (int)c.leaves.size();
|
||||
for( k = 0; k < nnodes; k++ )
|
||||
newfs << c.nodes[k].left << c.nodes[k].right
|
||||
<< c.nodes[k].f << c.nodes[k].threshold;
|
||||
newfs << "]" << "leafValues" << "[:";
|
||||
for( k = 0; k < nleaves; k++ )
|
||||
newfs << c.leaves[k];
|
||||
newfs << "]" << "}";
|
||||
}
|
||||
newfs << "]" << "}";
|
||||
}
|
||||
|
||||
newfs << "]"
|
||||
<< "features" << "[";
|
||||
|
||||
for( i = 0; i < nfeatures; i++ )
|
||||
{
|
||||
const HaarFeature& f = features[i];
|
||||
newfs << "{" << "rects" << "[";
|
||||
for( j = 0; j < HaarFeature::RECT_NUM; j++ )
|
||||
{
|
||||
if( j >= 2 && fabs(f.rect[j].weight) < FLT_EPSILON )
|
||||
break;
|
||||
newfs << "[:" << f.rect[j].r.x << f.rect[j].r.y <<
|
||||
f.rect[j].r.width << f.rect[j].r.height << f.rect[j].weight << "]";
|
||||
}
|
||||
newfs << "]";
|
||||
if( f.tilted )
|
||||
newfs << "tilted" << 1;
|
||||
newfs << "}";
|
||||
}
|
||||
|
||||
newfs << "]" << "}";
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CascadeClassifier::convert(const String& oldcascade, const String& newcascade)
|
||||
{
|
||||
FileStorage oldfs(oldcascade, FileStorage::READ);
|
||||
FileStorage newfs(newcascade, FileStorage::WRITE);
|
||||
if( !oldfs.isOpened() || !newfs.isOpened() )
|
||||
return false;
|
||||
FileNode oldroot = oldfs.getFirstTopLevelNode();
|
||||
|
||||
bool ok = haar_cvt::convert(oldroot, newfs);
|
||||
if( !ok && newcascade.size() > 0 )
|
||||
remove(newcascade.c_str());
|
||||
return ok;
|
||||
}
|
||||
|
||||
}
|
@ -1,885 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "opencv2/core/utility.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
#undef DEBUGLOGS
|
||||
#define DEBUGLOGS 1
|
||||
#endif
|
||||
|
||||
#ifndef DEBUGLOGS
|
||||
#define DEBUGLOGS 0
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#define LOG_TAG "OBJECT_DETECTOR"
|
||||
#define LOGD0(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGI0(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGW0(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGE0(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOGD0(_str, ...) (printf(_str , ## __VA_ARGS__), printf("\n"), fflush(stdout))
|
||||
#define LOGI0(_str, ...) (printf(_str , ## __VA_ARGS__), printf("\n"), fflush(stdout))
|
||||
#define LOGW0(_str, ...) (printf(_str , ## __VA_ARGS__), printf("\n"), fflush(stdout))
|
||||
#define LOGE0(_str, ...) (printf(_str , ## __VA_ARGS__), printf("\n"), fflush(stdout))
|
||||
#endif //__ANDROID__
|
||||
|
||||
#if DEBUGLOGS
|
||||
#define LOGD(_str, ...) LOGD0(_str , ## __VA_ARGS__)
|
||||
#define LOGI(_str, ...) LOGI0(_str , ## __VA_ARGS__)
|
||||
#define LOGW(_str, ...) LOGW0(_str , ## __VA_ARGS__)
|
||||
#define LOGE(_str, ...) LOGE0(_str , ## __VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...)
|
||||
#define LOGI(...)
|
||||
#define LOGW(...)
|
||||
#define LOGE(...)
|
||||
#endif //DEBUGLOGS
|
||||
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static inline cv::Point2f centerRect(const cv::Rect& r)
|
||||
{
|
||||
return cv::Point2f(r.x+((float)r.width)/2, r.y+((float)r.height)/2);
|
||||
}
|
||||
|
||||
static inline cv::Rect scale_rect(const cv::Rect& r, float scale)
|
||||
{
|
||||
cv::Point2f m=centerRect(r);
|
||||
float width = r.width * scale;
|
||||
float height = r.height * scale;
|
||||
int x=cvRound(m.x - width/2);
|
||||
int y=cvRound(m.y - height/2);
|
||||
|
||||
return cv::Rect(x, y, cvRound(width), cvRound(height));
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
void* workcycleObjectDetectorFunction(void* p);
|
||||
}
|
||||
|
||||
class cv::DetectionBasedTracker::SeparateDetectionWork
|
||||
{
|
||||
public:
|
||||
SeparateDetectionWork(cv::DetectionBasedTracker& _detectionBasedTracker, cv::Ptr<DetectionBasedTracker::IDetector> _detector,
|
||||
const cv::DetectionBasedTracker::Parameters& params);
|
||||
virtual ~SeparateDetectionWork();
|
||||
bool communicateWithDetectingThread(const Mat& imageGray, std::vector<Rect>& rectsWhereRegions);
|
||||
bool run();
|
||||
void stop();
|
||||
void resetTracking();
|
||||
|
||||
inline bool isWorking()
|
||||
{
|
||||
return (stateThread==STATE_THREAD_WORKING_SLEEPING) || (stateThread==STATE_THREAD_WORKING_WITH_IMAGE);
|
||||
}
|
||||
void setParameters(const cv::DetectionBasedTracker::Parameters& params)
|
||||
{
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
parameters = params;
|
||||
}
|
||||
|
||||
inline void init()
|
||||
{
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
stateThread = STATE_THREAD_STOPPED;
|
||||
isObjectDetectingReady = false;
|
||||
shouldObjectDetectingResultsBeForgot = false;
|
||||
objectDetectorThreadStartStop.notify_one();
|
||||
}
|
||||
protected:
|
||||
|
||||
DetectionBasedTracker& detectionBasedTracker;
|
||||
cv::Ptr<DetectionBasedTracker::IDetector> cascadeInThread;
|
||||
std::thread second_workthread;
|
||||
std::mutex mtx;
|
||||
std::condition_variable objectDetectorRun;
|
||||
std::condition_variable objectDetectorThreadStartStop;
|
||||
std::vector<cv::Rect> resultDetect;
|
||||
volatile bool isObjectDetectingReady;
|
||||
volatile bool shouldObjectDetectingResultsBeForgot;
|
||||
|
||||
enum StateSeparatedThread {
|
||||
STATE_THREAD_STOPPED=0,
|
||||
STATE_THREAD_WORKING_SLEEPING,
|
||||
STATE_THREAD_WORKING_WITH_IMAGE,
|
||||
STATE_THREAD_WORKING,
|
||||
STATE_THREAD_STOPPING
|
||||
};
|
||||
volatile StateSeparatedThread stateThread;
|
||||
|
||||
cv::Mat imageSeparateDetecting;
|
||||
|
||||
void workcycleObjectDetector();
|
||||
friend void* workcycleObjectDetectorFunction(void* p);
|
||||
|
||||
long long timeWhenDetectingThreadStartedWork;
|
||||
cv::DetectionBasedTracker::Parameters parameters;
|
||||
};
|
||||
|
||||
cv::DetectionBasedTracker::SeparateDetectionWork::SeparateDetectionWork(DetectionBasedTracker& _detectionBasedTracker, cv::Ptr<DetectionBasedTracker::IDetector> _detector,
|
||||
const cv::DetectionBasedTracker::Parameters& params)
|
||||
:detectionBasedTracker(_detectionBasedTracker),
|
||||
cascadeInThread(),
|
||||
isObjectDetectingReady(false),
|
||||
shouldObjectDetectingResultsBeForgot(false),
|
||||
stateThread(STATE_THREAD_STOPPED),
|
||||
timeWhenDetectingThreadStartedWork(-1),
|
||||
parameters(params)
|
||||
{
|
||||
CV_Assert(_detector);
|
||||
|
||||
cascadeInThread = _detector;
|
||||
}
|
||||
|
||||
cv::DetectionBasedTracker::SeparateDetectionWork::~SeparateDetectionWork()
|
||||
{
|
||||
if(stateThread!=STATE_THREAD_STOPPED) {
|
||||
LOGE("\n\n\nATTENTION!!! dangerous algorithm error: destructor DetectionBasedTracker::DetectionBasedTracker::~SeparateDetectionWork is called before stopping the workthread");
|
||||
}
|
||||
second_workthread.join();
|
||||
}
|
||||
bool cv::DetectionBasedTracker::SeparateDetectionWork::run()
|
||||
{
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::run() --- start");
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
// unlocked when leaving scope
|
||||
if (stateThread != STATE_THREAD_STOPPED) {
|
||||
LOGE("DetectionBasedTracker::SeparateDetectionWork::run is called while the previous run is not stopped");
|
||||
return false;
|
||||
}
|
||||
stateThread=STATE_THREAD_WORKING_SLEEPING;
|
||||
second_workthread = std::thread(workcycleObjectDetectorFunction, (void*)this); //TODO: add attributes?
|
||||
objectDetectorThreadStartStop.wait(mtx_lock);
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::run --- end");
|
||||
return true;
|
||||
}
|
||||
|
||||
#define CATCH_ALL_AND_LOG(_block) \
|
||||
try { \
|
||||
_block; \
|
||||
} \
|
||||
catch(const cv::Exception& e) { \
|
||||
LOGE0("\n %s: ERROR: OpenCV Exception caught: \n'%s'\n\n", CV_Func, e.what()); \
|
||||
} catch(const std::exception& e) { \
|
||||
LOGE0("\n %s: ERROR: Exception caught: \n'%s'\n\n", CV_Func, e.what()); \
|
||||
} catch(...) { \
|
||||
LOGE0("\n %s: ERROR: UNKNOWN Exception caught\n\n", CV_Func); \
|
||||
}
|
||||
|
||||
void* cv::workcycleObjectDetectorFunction(void* p)
|
||||
{
|
||||
CATCH_ALL_AND_LOG({ ((cv::DetectionBasedTracker::SeparateDetectionWork*)p)->workcycleObjectDetector(); });
|
||||
try{
|
||||
((cv::DetectionBasedTracker::SeparateDetectionWork*)p)->init();
|
||||
} catch(...) {
|
||||
LOGE0("DetectionBasedTracker: workcycleObjectDetectorFunction: ERROR concerning pointer, received as the function parameter");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector()
|
||||
{
|
||||
static double freq = getTickFrequency();
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- start");
|
||||
std::vector<Rect> objects;
|
||||
|
||||
CV_Assert(stateThread==STATE_THREAD_WORKING_SLEEPING);
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
{
|
||||
objectDetectorThreadStartStop.notify_one();
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- before waiting");
|
||||
CV_Assert(stateThread==STATE_THREAD_WORKING_SLEEPING);
|
||||
objectDetectorRun.wait(mtx_lock);
|
||||
if (isWorking()) {
|
||||
stateThread=STATE_THREAD_WORKING_WITH_IMAGE;
|
||||
}
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- after waiting");
|
||||
}
|
||||
mtx_lock.unlock();
|
||||
|
||||
bool isFirstStep=true;
|
||||
|
||||
isObjectDetectingReady=false;
|
||||
|
||||
while(isWorking())
|
||||
{
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- next step");
|
||||
|
||||
if (! isFirstStep) {
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- before waiting");
|
||||
CV_Assert(stateThread==STATE_THREAD_WORKING_SLEEPING);
|
||||
mtx_lock.lock();
|
||||
if (!isWorking()) {//it is a rare case, but may cause a crash
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- go out from the workcycle from inner part of lock just before waiting");
|
||||
mtx_lock.unlock();
|
||||
break;
|
||||
}
|
||||
CV_Assert(stateThread==STATE_THREAD_WORKING_SLEEPING);
|
||||
objectDetectorRun.wait(mtx_lock);
|
||||
if (isWorking()) {
|
||||
stateThread=STATE_THREAD_WORKING_WITH_IMAGE;
|
||||
}
|
||||
mtx_lock.unlock();
|
||||
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- after waiting");
|
||||
} else {
|
||||
isFirstStep=false;
|
||||
}
|
||||
|
||||
if (!isWorking()) {
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- go out from the workcycle just after waiting");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (imageSeparateDetecting.empty()) {
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- imageSeparateDetecting is empty, continue");
|
||||
continue;
|
||||
}
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- start handling imageSeparateDetecting, img.size=%dx%d, img.data=0x%p",
|
||||
imageSeparateDetecting.size().width, imageSeparateDetecting.size().height, (void*)imageSeparateDetecting.data);
|
||||
|
||||
|
||||
int64 t1_detect=getTickCount();
|
||||
|
||||
cascadeInThread->detect(imageSeparateDetecting, objects);
|
||||
|
||||
/*cascadeInThread.detectMultiScale( imageSeparateDetecting, objects,
|
||||
detectionBasedTracker.parameters.scaleFactor, detectionBasedTracker.parameters.minNeighbors, 0
|
||||
|CV_HAAR_SCALE_IMAGE
|
||||
,
|
||||
min_objectSize,
|
||||
max_objectSize
|
||||
);
|
||||
*/
|
||||
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- end handling imageSeparateDetecting");
|
||||
|
||||
if (!isWorking()) {
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- go out from the workcycle just after detecting");
|
||||
break;
|
||||
}
|
||||
|
||||
int64 t2_detect = getTickCount();
|
||||
int64 dt_detect = t2_detect-t1_detect;
|
||||
double dt_detect_ms=((double)dt_detect)/freq * 1000.0;
|
||||
(void)(dt_detect_ms);
|
||||
|
||||
LOGI("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector() --- objects num==%d, t_ms=%.4f", (int)objects.size(), dt_detect_ms);
|
||||
mtx_lock.lock();
|
||||
if (!shouldObjectDetectingResultsBeForgot) {
|
||||
resultDetect=objects;
|
||||
isObjectDetectingReady=true;
|
||||
} else { //shouldObjectDetectingResultsBeForgot==true
|
||||
resultDetect.clear();
|
||||
isObjectDetectingReady=false;
|
||||
shouldObjectDetectingResultsBeForgot=false;
|
||||
}
|
||||
if(isWorking()) {
|
||||
stateThread=STATE_THREAD_WORKING_SLEEPING;
|
||||
}
|
||||
mtx_lock.unlock();
|
||||
|
||||
objects.clear();
|
||||
}// while(isWorking())
|
||||
|
||||
LOGI("DetectionBasedTracker::SeparateDetectionWork::workcycleObjectDetector: Returning");
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::SeparateDetectionWork::stop()
|
||||
{
|
||||
//FIXME: TODO: should add quickStop functionality
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
if (!isWorking()) {
|
||||
mtx_lock.unlock();
|
||||
LOGE("SimpleHighguiDemoCore::stop is called but the SimpleHighguiDemoCore pthread is not active");
|
||||
stateThread = STATE_THREAD_STOPPING;
|
||||
return;
|
||||
}
|
||||
stateThread=STATE_THREAD_STOPPING;
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::stop: before going to sleep to wait for the signal from the workthread");
|
||||
objectDetectorRun.notify_one();
|
||||
objectDetectorThreadStartStop.wait(mtx_lock);
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::stop: after receiving the signal from the workthread, stateThread=%d", (int)stateThread);
|
||||
mtx_lock.unlock();
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::SeparateDetectionWork::resetTracking()
|
||||
{
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::resetTracking");
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
|
||||
if (stateThread == STATE_THREAD_WORKING_WITH_IMAGE) {
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::resetTracking: since workthread is detecting objects at the moment, we should make cascadeInThread stop detecting and forget the detecting results");
|
||||
shouldObjectDetectingResultsBeForgot=true;
|
||||
//cascadeInThread.setStopFlag();//FIXME: TODO: this feature also should be contributed to OpenCV
|
||||
} else {
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::resetTracking: since workthread is NOT detecting objects at the moment, we should NOT make any additional actions");
|
||||
}
|
||||
|
||||
resultDetect.clear();
|
||||
isObjectDetectingReady=false;
|
||||
|
||||
mtx_lock.unlock();
|
||||
}
|
||||
|
||||
bool cv::DetectionBasedTracker::SeparateDetectionWork::communicateWithDetectingThread(const Mat& imageGray, std::vector<Rect>& rectsWhereRegions)
|
||||
{
|
||||
static double freq = getTickFrequency();
|
||||
|
||||
bool shouldCommunicateWithDetectingThread = (stateThread==STATE_THREAD_WORKING_SLEEPING);
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::communicateWithDetectingThread: shouldCommunicateWithDetectingThread=%d", (shouldCommunicateWithDetectingThread?1:0));
|
||||
|
||||
if (!shouldCommunicateWithDetectingThread) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool shouldHandleResult = false;
|
||||
|
||||
std::unique_lock<std::mutex> mtx_lock(mtx);
|
||||
|
||||
if (isObjectDetectingReady) {
|
||||
shouldHandleResult=true;
|
||||
rectsWhereRegions = resultDetect;
|
||||
isObjectDetectingReady=false;
|
||||
|
||||
double lastBigDetectionDuration = 1000.0 * (((double)(getTickCount() - timeWhenDetectingThreadStartedWork )) / freq);
|
||||
(void)(lastBigDetectionDuration);
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::communicateWithDetectingThread: lastBigDetectionDuration=%f ms", (double)lastBigDetectionDuration);
|
||||
}
|
||||
|
||||
bool shouldSendNewDataToWorkThread = true;
|
||||
if (timeWhenDetectingThreadStartedWork > 0) {
|
||||
double time_from_previous_launch_in_ms=1000.0 * (((double)(getTickCount() - timeWhenDetectingThreadStartedWork )) / freq); //the same formula as for lastBigDetectionDuration
|
||||
shouldSendNewDataToWorkThread = (time_from_previous_launch_in_ms >= detectionBasedTracker.parameters.minDetectionPeriod);
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::communicateWithDetectingThread: shouldSendNewDataToWorkThread was 1, now it is %d, since time_from_previous_launch_in_ms=%.2f, minDetectionPeriod=%d",
|
||||
(shouldSendNewDataToWorkThread?1:0), time_from_previous_launch_in_ms, detectionBasedTracker.parameters.minDetectionPeriod);
|
||||
}
|
||||
|
||||
if (shouldSendNewDataToWorkThread) {
|
||||
|
||||
imageSeparateDetecting.create(imageGray.size(), CV_8UC1);
|
||||
|
||||
imageGray.copyTo(imageSeparateDetecting);//may change imageSeparateDetecting ptr. But should not.
|
||||
|
||||
|
||||
timeWhenDetectingThreadStartedWork = getTickCount() ;
|
||||
|
||||
objectDetectorRun.notify_one();
|
||||
}
|
||||
|
||||
mtx_lock.unlock();
|
||||
LOGD("DetectionBasedTracker::SeparateDetectionWork::communicateWithDetectingThread: result: shouldHandleResult=%d", (shouldHandleResult?1:0));
|
||||
|
||||
return shouldHandleResult;
|
||||
}
|
||||
|
||||
cv::DetectionBasedTracker::Parameters::Parameters()
|
||||
{
|
||||
maxTrackLifetime = 5;
|
||||
minDetectionPeriod = 0;
|
||||
}
|
||||
|
||||
cv::DetectionBasedTracker::InnerParameters::InnerParameters()
|
||||
{
|
||||
numLastPositionsToTrack=4;
|
||||
numStepsToWaitBeforeFirstShow=6;
|
||||
numStepsToTrackWithoutDetectingIfObjectHasNotBeenShown=3;
|
||||
numStepsToShowWithoutDetecting=3;
|
||||
|
||||
coeffTrackingWindowSize=2.0;
|
||||
coeffObjectSizeToTrack=0.85f;
|
||||
coeffObjectSpeedUsingInPrediction=0.8f;
|
||||
|
||||
}
|
||||
|
||||
cv::DetectionBasedTracker::DetectionBasedTracker(cv::Ptr<IDetector> mainDetector, cv::Ptr<IDetector> trackingDetector, const Parameters& params)
|
||||
:separateDetectionWork(),
|
||||
parameters(params),
|
||||
innerParameters(),
|
||||
numTrackedSteps(0),
|
||||
cascadeForTracking(trackingDetector)
|
||||
{
|
||||
CV_Assert( (params.maxTrackLifetime >= 0)
|
||||
// && mainDetector
|
||||
&& trackingDetector );
|
||||
|
||||
if (mainDetector) {
|
||||
Ptr<SeparateDetectionWork> tmp(new SeparateDetectionWork(*this, mainDetector, params));
|
||||
separateDetectionWork.swap(tmp);
|
||||
}
|
||||
|
||||
weightsPositionsSmoothing.push_back(1);
|
||||
weightsSizesSmoothing.push_back(0.5);
|
||||
weightsSizesSmoothing.push_back(0.3f);
|
||||
weightsSizesSmoothing.push_back(0.2f);
|
||||
}
|
||||
|
||||
cv::DetectionBasedTracker::~DetectionBasedTracker()
|
||||
{
|
||||
}
|
||||
|
||||
void DetectionBasedTracker::process(const Mat& imageGray)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(imageGray.type()==CV_8UC1);
|
||||
|
||||
if ( separateDetectionWork && !separateDetectionWork->isWorking() ) {
|
||||
separateDetectionWork->run();
|
||||
}
|
||||
|
||||
static double freq = getTickFrequency();
|
||||
static long long time_when_last_call_started=getTickCount();
|
||||
|
||||
{
|
||||
double delta_time_from_prev_call=1000.0 * (((double)(getTickCount() - time_when_last_call_started)) / freq);
|
||||
(void)(delta_time_from_prev_call);
|
||||
LOGD("DetectionBasedTracker::process: time from the previous call is %f ms", (double)delta_time_from_prev_call);
|
||||
time_when_last_call_started=getTickCount();
|
||||
}
|
||||
|
||||
Mat imageDetect=imageGray;
|
||||
|
||||
std::vector<Rect> rectsWhereRegions;
|
||||
bool shouldHandleResult=false;
|
||||
if (separateDetectionWork) {
|
||||
shouldHandleResult = separateDetectionWork->communicateWithDetectingThread(imageGray, rectsWhereRegions);
|
||||
}
|
||||
|
||||
if (shouldHandleResult) {
|
||||
LOGD("DetectionBasedTracker::process: get _rectsWhereRegions were got from resultDetect");
|
||||
} else {
|
||||
LOGD("DetectionBasedTracker::process: get _rectsWhereRegions from previous positions");
|
||||
for(size_t i = 0; i < trackedObjects.size(); i++) {
|
||||
size_t n = trackedObjects[i].lastPositions.size();
|
||||
CV_Assert(n > 0);
|
||||
|
||||
Rect r = trackedObjects[i].lastPositions[n-1];
|
||||
if(r.empty()) {
|
||||
LOGE("DetectionBasedTracker::process: ERROR: ATTENTION: strange algorithm's behavior: trackedObjects[i].rect() is empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
//correction by speed of rectangle
|
||||
if (n > 1) {
|
||||
Point2f center = centerRect(r);
|
||||
Point2f center_prev = centerRect(trackedObjects[i].lastPositions[n-2]);
|
||||
Point2f shift = (center - center_prev) * innerParameters.coeffObjectSpeedUsingInPrediction;
|
||||
|
||||
r.x += cvRound(shift.x);
|
||||
r.y += cvRound(shift.y);
|
||||
}
|
||||
|
||||
|
||||
rectsWhereRegions.push_back(r);
|
||||
}
|
||||
}
|
||||
LOGI("DetectionBasedTracker::process: tracked objects num==%d", (int)trackedObjects.size());
|
||||
|
||||
std::vector<Rect> detectedObjectsInRegions;
|
||||
|
||||
LOGD("DetectionBasedTracker::process: rectsWhereRegions.size()=%d", (int)rectsWhereRegions.size());
|
||||
for(size_t i=0; i < rectsWhereRegions.size(); i++) {
|
||||
Rect r = rectsWhereRegions[i];
|
||||
|
||||
detectInRegion(imageDetect, r, detectedObjectsInRegions);
|
||||
}
|
||||
LOGD("DetectionBasedTracker::process: detectedObjectsInRegions.size()=%d", (int)detectedObjectsInRegions.size());
|
||||
|
||||
updateTrackedObjects(detectedObjectsInRegions);
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::getObjects(std::vector<cv::Rect>& result) const
|
||||
{
|
||||
result.clear();
|
||||
|
||||
for(size_t i=0; i < trackedObjects.size(); i++) {
|
||||
Rect r=calcTrackedObjectPositionToShow((int)i);
|
||||
if (r.empty()) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(r);
|
||||
LOGD("DetectionBasedTracker::process: found a object with SIZE %d x %d, rect={%d, %d, %d x %d}", r.width, r.height, r.x, r.y, r.width, r.height);
|
||||
}
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::getObjects(std::vector<Object>& result) const
|
||||
{
|
||||
result.clear();
|
||||
|
||||
for(size_t i=0; i < trackedObjects.size(); i++) {
|
||||
Rect r=calcTrackedObjectPositionToShow((int)i);
|
||||
if (r.empty()) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(Object(r, trackedObjects[i].id));
|
||||
LOGD("DetectionBasedTracker::process: found a object with SIZE %d x %d, rect={%d, %d, %d x %d}", r.width, r.height, r.x, r.y, r.width, r.height);
|
||||
}
|
||||
}
|
||||
void cv::DetectionBasedTracker::getObjects(std::vector<ExtObject>& result) const
|
||||
{
|
||||
result.clear();
|
||||
|
||||
for(size_t i=0; i < trackedObjects.size(); i++) {
|
||||
ObjectStatus status;
|
||||
Rect r=calcTrackedObjectPositionToShow((int)i, status);
|
||||
result.push_back(ExtObject(trackedObjects[i].id, r, status));
|
||||
LOGD("DetectionBasedTracker::process: found a object with SIZE %d x %d, rect={%d, %d, %d x %d}, status = %d", r.width, r.height, r.x, r.y, r.width, r.height, (int)status);
|
||||
}
|
||||
}
|
||||
|
||||
bool cv::DetectionBasedTracker::run()
|
||||
{
|
||||
if (separateDetectionWork) {
|
||||
return separateDetectionWork->run();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::stop()
|
||||
{
|
||||
if (separateDetectionWork) {
|
||||
separateDetectionWork->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::resetTracking()
|
||||
{
|
||||
if (separateDetectionWork) {
|
||||
separateDetectionWork->resetTracking();
|
||||
}
|
||||
trackedObjects.clear();
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::updateTrackedObjects(const std::vector<Rect>& detectedObjects)
|
||||
{
|
||||
enum {
|
||||
NEW_RECTANGLE=-1,
|
||||
INTERSECTED_RECTANGLE=-2
|
||||
};
|
||||
|
||||
int N1=(int)trackedObjects.size();
|
||||
int N2=(int)detectedObjects.size();
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: N1=%d, N2=%d", N1, N2);
|
||||
|
||||
for(int i=0; i < N1; i++) {
|
||||
trackedObjects[i].numDetectedFrames++;
|
||||
}
|
||||
|
||||
std::vector<int> correspondence(detectedObjects.size(), NEW_RECTANGLE);
|
||||
correspondence.clear();
|
||||
correspondence.resize(detectedObjects.size(), NEW_RECTANGLE);
|
||||
|
||||
for(int i=0; i < N1; i++) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: i=%d", i);
|
||||
TrackedObject& curObject=trackedObjects[i];
|
||||
|
||||
int bestIndex=-1;
|
||||
int bestArea=-1;
|
||||
|
||||
int numpositions=(int)curObject.lastPositions.size();
|
||||
CV_Assert(numpositions > 0);
|
||||
Rect prevRect=curObject.lastPositions[numpositions-1];
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: prevRect[%d]={%d, %d, %d x %d}", i, prevRect.x, prevRect.y, prevRect.width, prevRect.height);
|
||||
|
||||
for(int j=0; j < N2; j++) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: j=%d", j);
|
||||
if (correspondence[j] >= 0) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: j=%d is rejected, because it has correspondence=%d", j, correspondence[j]);
|
||||
continue;
|
||||
}
|
||||
if (correspondence[j] !=NEW_RECTANGLE) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: j=%d is rejected, because it is intersected with another rectangle", j);
|
||||
continue;
|
||||
}
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: detectedObjects[%d]={%d, %d, %d x %d}",
|
||||
j, detectedObjects[j].x, detectedObjects[j].y, detectedObjects[j].width, detectedObjects[j].height);
|
||||
|
||||
Rect r=prevRect & detectedObjects[j];
|
||||
if ( (r.width > 0) && (r.height > 0) ) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: There is intersection between prevRect and detectedRect, r={%d, %d, %d x %d}",
|
||||
r.x, r.y, r.width, r.height);
|
||||
correspondence[j]=INTERSECTED_RECTANGLE;
|
||||
|
||||
if ( r.area() > bestArea) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: The area of intersection is %d, it is better than bestArea=%d", r.area(), bestArea);
|
||||
bestIndex=j;
|
||||
bestArea=r.area();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestIndex >= 0) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: The best correspondence for i=%d is j=%d", i, bestIndex);
|
||||
correspondence[bestIndex]=i;
|
||||
|
||||
for(int j=0; j < N2; j++) {
|
||||
if (correspondence[j] >= 0)
|
||||
continue;
|
||||
|
||||
Rect r=detectedObjects[j] & detectedObjects[bestIndex];
|
||||
if ( (r.width > 0) && (r.height > 0) ) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: Found intersection between "
|
||||
"rectangles j=%d and bestIndex=%d, rectangle j=%d is marked as intersected", j, bestIndex, j);
|
||||
correspondence[j]=INTERSECTED_RECTANGLE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: There is no correspondence for i=%d ", i);
|
||||
curObject.numFramesNotDetected++;
|
||||
}
|
||||
}
|
||||
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: start second cycle");
|
||||
for(int j=0; j < N2; j++) {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: j=%d", j);
|
||||
int i=correspondence[j];
|
||||
if (i >= 0) {//add position
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: add position");
|
||||
trackedObjects[i].lastPositions.push_back(detectedObjects[j]);
|
||||
while ((int)trackedObjects[i].lastPositions.size() > (int) innerParameters.numLastPositionsToTrack) {
|
||||
trackedObjects[i].lastPositions.erase(trackedObjects[i].lastPositions.begin());
|
||||
}
|
||||
trackedObjects[i].numFramesNotDetected=0;
|
||||
} else if (i==NEW_RECTANGLE){ //new object
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: new object");
|
||||
trackedObjects.push_back(detectedObjects[j]);
|
||||
} else {
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: was auxiliary intersection");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TrackedObject>::iterator it=trackedObjects.begin();
|
||||
while( it != trackedObjects.end() ) {
|
||||
if ( (it->numFramesNotDetected > parameters.maxTrackLifetime)
|
||||
||
|
||||
(
|
||||
(it->numDetectedFrames <= innerParameters.numStepsToWaitBeforeFirstShow)
|
||||
&&
|
||||
(it->numFramesNotDetected > innerParameters.numStepsToTrackWithoutDetectingIfObjectHasNotBeenShown)
|
||||
)
|
||||
)
|
||||
{
|
||||
int numpos=(int)it->lastPositions.size();
|
||||
CV_Assert(numpos > 0);
|
||||
Rect r = it->lastPositions[numpos-1];
|
||||
(void)(r);
|
||||
LOGD("DetectionBasedTracker::updateTrackedObjects: deleted object {%d, %d, %d x %d}",
|
||||
r.x, r.y, r.width, r.height);
|
||||
it=trackedObjects.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cv::DetectionBasedTracker::addObject(const Rect& location)
|
||||
{
|
||||
LOGD("DetectionBasedTracker::addObject: new object {%d, %d %dx%d}",location.x, location.y, location.width, location.height);
|
||||
trackedObjects.push_back(TrackedObject(location));
|
||||
int newId = trackedObjects.back().id;
|
||||
LOGD("DetectionBasedTracker::addObject: newId = %d", newId);
|
||||
return newId;
|
||||
}
|
||||
|
||||
Rect cv::DetectionBasedTracker::calcTrackedObjectPositionToShow(int i) const
|
||||
{
|
||||
ObjectStatus status;
|
||||
return calcTrackedObjectPositionToShow(i, status);
|
||||
}
|
||||
Rect cv::DetectionBasedTracker::calcTrackedObjectPositionToShow(int i, ObjectStatus& status) const
|
||||
{
|
||||
if ( (i < 0) || (i >= (int)trackedObjects.size()) ) {
|
||||
LOGE("DetectionBasedTracker::calcTrackedObjectPositionToShow: ERROR: wrong i=%d", i);
|
||||
status = WRONG_OBJECT;
|
||||
return Rect();
|
||||
}
|
||||
if (trackedObjects[i].numDetectedFrames <= innerParameters.numStepsToWaitBeforeFirstShow){
|
||||
LOGI("DetectionBasedTracker::calcTrackedObjectPositionToShow: trackedObjects[%d].numDetectedFrames=%d <= numStepsToWaitBeforeFirstShow=%d --- return empty Rect()",
|
||||
i, trackedObjects[i].numDetectedFrames, innerParameters.numStepsToWaitBeforeFirstShow);
|
||||
status = DETECTED_NOT_SHOWN_YET;
|
||||
return Rect();
|
||||
}
|
||||
if (trackedObjects[i].numFramesNotDetected > innerParameters.numStepsToShowWithoutDetecting) {
|
||||
status = DETECTED_TEMPORARY_LOST;
|
||||
return Rect();
|
||||
}
|
||||
|
||||
const TrackedObject::PositionsVector& lastPositions=trackedObjects[i].lastPositions;
|
||||
|
||||
int N=(int)lastPositions.size();
|
||||
if (N<=0) {
|
||||
LOGE("DetectionBasedTracker::calcTrackedObjectPositionToShow: ERROR: no positions for i=%d", i);
|
||||
status = WRONG_OBJECT;
|
||||
return Rect();
|
||||
}
|
||||
|
||||
int Nsize=std::min(N, (int)weightsSizesSmoothing.size());
|
||||
int Ncenter= std::min(N, (int)weightsPositionsSmoothing.size());
|
||||
|
||||
Point2f center;
|
||||
double w=0, h=0;
|
||||
if (Nsize > 0) {
|
||||
double sum=0;
|
||||
for(int j=0; j < Nsize; j++) {
|
||||
int k=N-j-1;
|
||||
w += lastPositions[k].width * weightsSizesSmoothing[j];
|
||||
h += lastPositions[k].height * weightsSizesSmoothing[j];
|
||||
sum+=weightsSizesSmoothing[j];
|
||||
}
|
||||
w /= sum;
|
||||
h /= sum;
|
||||
} else {
|
||||
w=lastPositions[N-1].width;
|
||||
h=lastPositions[N-1].height;
|
||||
}
|
||||
|
||||
if (Ncenter > 0) {
|
||||
double sum=0;
|
||||
for(int j=0; j < Ncenter; j++) {
|
||||
int k=N-j-1;
|
||||
Point tl(lastPositions[k].tl());
|
||||
Point br(lastPositions[k].br());
|
||||
Point2f c1;
|
||||
c1=tl;
|
||||
c1=c1* 0.5f;
|
||||
Point2f c2;
|
||||
c2=br;
|
||||
c2=c2*0.5f;
|
||||
c1=c1+c2;
|
||||
|
||||
center=center+ (c1 * weightsPositionsSmoothing[j]);
|
||||
sum+=weightsPositionsSmoothing[j];
|
||||
}
|
||||
center *= (float)(1 / sum);
|
||||
} else {
|
||||
int k=N-1;
|
||||
Point tl(lastPositions[k].tl());
|
||||
Point br(lastPositions[k].br());
|
||||
Point2f c1;
|
||||
c1=tl;
|
||||
c1=c1* 0.5f;
|
||||
Point2f c2;
|
||||
c2=br;
|
||||
c2=c2*0.5f;
|
||||
|
||||
center=c1+c2;
|
||||
}
|
||||
Point2f tl=center-Point2f((float)w*0.5f,(float)h*0.5f);
|
||||
Rect res(cvRound(tl.x), cvRound(tl.y), cvRound(w), cvRound(h));
|
||||
LOGD("DetectionBasedTracker::calcTrackedObjectPositionToShow: Result for i=%d: {%d, %d, %d x %d}", i, res.x, res.y, res.width, res.height);
|
||||
|
||||
status = DETECTED;
|
||||
return res;
|
||||
}
|
||||
|
||||
void cv::DetectionBasedTracker::detectInRegion(const Mat& img, const Rect& r, std::vector<Rect>& detectedObjectsInRegions)
|
||||
{
|
||||
Rect r0(Point(), img.size());
|
||||
Rect r1 = scale_rect(r, innerParameters.coeffTrackingWindowSize);
|
||||
r1 = r1 & r0;
|
||||
|
||||
if ( (r1.width <=0) || (r1.height <= 0) ) {
|
||||
LOGD("DetectionBasedTracker::detectInRegion: Empty intersection");
|
||||
return;
|
||||
}
|
||||
|
||||
int d = cvRound(std::min(r.width, r.height) * innerParameters.coeffObjectSizeToTrack);
|
||||
|
||||
std::vector<Rect> tmpobjects;
|
||||
|
||||
Mat img1(img, r1);//subimage for rectangle -- without data copying
|
||||
LOGD("DetectionBasedTracker::detectInRegion: img1.size()=%d x %d, d=%d",
|
||||
img1.size().width, img1.size().height, d);
|
||||
|
||||
cascadeForTracking->setMinObjectSize(Size(d, d));
|
||||
cascadeForTracking->detect(img1, tmpobjects);
|
||||
/*
|
||||
detectMultiScale( img1, tmpobjects,
|
||||
parameters.scaleFactor, parameters.minNeighbors, 0
|
||||
|CV_HAAR_FIND_BIGGEST_OBJECT
|
||||
|CV_HAAR_SCALE_IMAGE
|
||||
,
|
||||
Size(d,d),
|
||||
max_objectSize
|
||||
);*/
|
||||
|
||||
for(size_t i=0; i < tmpobjects.size(); i++) {
|
||||
Rect curres(tmpobjects[i].tl() + r1.tl(), tmpobjects[i].size());
|
||||
detectedObjectsInRegions.push_back(curres);
|
||||
}
|
||||
}
|
||||
|
||||
bool cv::DetectionBasedTracker::setParameters(const Parameters& params)
|
||||
{
|
||||
if ( params.maxTrackLifetime < 0 )
|
||||
{
|
||||
LOGE("DetectionBasedTracker::setParameters: ERROR: wrong parameters value");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (separateDetectionWork) {
|
||||
separateDetectionWork->setParameters(params);
|
||||
}
|
||||
parameters=params;
|
||||
return true;
|
||||
}
|
||||
|
||||
const cv::DetectionBasedTracker::Parameters& DetectionBasedTracker::getParameters() const
|
||||
{
|
||||
return parameters;
|
||||
}
|
@ -1,661 +0,0 @@
|
||||
///////////////////////////// OpenCL kernels for face detection //////////////////////////////
|
||||
////////////////////////////// see the opencv/doc/license.txt ///////////////////////////////
|
||||
|
||||
//
|
||||
// the code has been derived from the OpenCL Haar cascade kernel by
|
||||
//
|
||||
// Niko Li, newlife20080214@gmail.com
|
||||
// Wang Weiyan, wangweiyanster@gmail.com
|
||||
// Jia Haipeng, jiahaipeng95@gmail.com
|
||||
// Nathan, liujun@multicorewareinc.com
|
||||
// Peng Xiao, pengxiao@outlook.com
|
||||
// Erping Pang, erping@multicorewareinc.com
|
||||
//
|
||||
|
||||
#ifdef HAAR
|
||||
typedef struct __attribute__((aligned(4))) OptHaarFeature
|
||||
{
|
||||
int4 ofs[3] __attribute__((aligned (4)));
|
||||
float4 weight __attribute__((aligned (4)));
|
||||
}
|
||||
OptHaarFeature;
|
||||
#endif
|
||||
|
||||
#ifdef LBP
|
||||
typedef struct __attribute__((aligned(4))) OptLBPFeature
|
||||
{
|
||||
int16 ofs __attribute__((aligned (4)));
|
||||
}
|
||||
OptLBPFeature;
|
||||
#endif
|
||||
|
||||
typedef struct __attribute__((aligned(4))) Stump
|
||||
{
|
||||
float4 st __attribute__((aligned (4)));
|
||||
}
|
||||
Stump;
|
||||
|
||||
typedef struct __attribute__((aligned(4))) Node
|
||||
{
|
||||
int4 n __attribute__((aligned (4)));
|
||||
}
|
||||
Node;
|
||||
|
||||
typedef struct __attribute__((aligned (4))) Stage
|
||||
{
|
||||
int first __attribute__((aligned (4)));
|
||||
int ntrees __attribute__((aligned (4)));
|
||||
float threshold __attribute__((aligned (4)));
|
||||
}
|
||||
Stage;
|
||||
|
||||
typedef struct __attribute__((aligned (4))) ScaleData
|
||||
{
|
||||
float scale __attribute__((aligned (4)));
|
||||
int szi_width __attribute__((aligned (4)));
|
||||
int szi_height __attribute__((aligned (4)));
|
||||
int layer_ofs __attribute__((aligned (4)));
|
||||
int ystep __attribute__((aligned (4)));
|
||||
}
|
||||
ScaleData;
|
||||
|
||||
#ifndef SUM_BUF_SIZE
|
||||
#define SUM_BUF_SIZE 0
|
||||
#endif
|
||||
|
||||
#ifndef NODE_COUNT
|
||||
#define NODE_COUNT 1
|
||||
#endif
|
||||
|
||||
#ifdef HAAR
|
||||
__kernel __attribute__((reqd_work_group_size(LOCAL_SIZE_X,LOCAL_SIZE_Y,1)))
|
||||
void runHaarClassifier(
|
||||
int nscales, __global const ScaleData* scaleData,
|
||||
__global const int* sum,
|
||||
int _sumstep, int sumoffset,
|
||||
__global const OptHaarFeature* optfeatures,
|
||||
__global const Stage* stages,
|
||||
__global const Node* nodes,
|
||||
__global const float* leaves0,
|
||||
|
||||
volatile __global int* facepos,
|
||||
int4 normrect, int sqofs, int2 windowsize)
|
||||
{
|
||||
int lx = get_local_id(0);
|
||||
int ly = get_local_id(1);
|
||||
int groupIdx = get_group_id(0);
|
||||
int i, ngroups = get_global_size(0)/LOCAL_SIZE_X;
|
||||
int scaleIdx, tileIdx, stageIdx;
|
||||
int sumstep = (int)(_sumstep/sizeof(int));
|
||||
int4 nofs0 = (int4)(mad24(normrect.y, sumstep, normrect.x),
|
||||
mad24(normrect.y, sumstep, normrect.x + normrect.z),
|
||||
mad24(normrect.y + normrect.w, sumstep, normrect.x),
|
||||
mad24(normrect.y + normrect.w, sumstep, normrect.x + normrect.z));
|
||||
int normarea = normrect.z * normrect.w;
|
||||
float invarea = 1.f/normarea;
|
||||
int lidx = ly*LOCAL_SIZE_X + lx;
|
||||
|
||||
#if SUM_BUF_SIZE > 0
|
||||
int4 nofs = (int4)(mad24(normrect.y, SUM_BUF_STEP, normrect.x),
|
||||
mad24(normrect.y, SUM_BUF_STEP, normrect.x + normrect.z),
|
||||
mad24(normrect.y + normrect.w, SUM_BUF_STEP, normrect.x),
|
||||
mad24(normrect.y + normrect.w, SUM_BUF_STEP, normrect.x + normrect.z));
|
||||
#else
|
||||
int4 nofs = nofs0;
|
||||
#endif
|
||||
#define LOCAL_SIZE (LOCAL_SIZE_X*LOCAL_SIZE_Y)
|
||||
__local int lstore[SUM_BUF_SIZE + LOCAL_SIZE*5/2+1];
|
||||
#if SUM_BUF_SIZE > 0
|
||||
__local int* ibuf = lstore;
|
||||
__local int* lcount = ibuf + SUM_BUF_SIZE;
|
||||
#else
|
||||
__local int* lcount = lstore;
|
||||
#endif
|
||||
__local float* lnf = (__local float*)(lcount + 1);
|
||||
__local float* lpartsum = lnf + LOCAL_SIZE;
|
||||
__local short* lbuf = (__local short*)(lpartsum + LOCAL_SIZE);
|
||||
|
||||
for( scaleIdx = nscales-1; scaleIdx >= 0; scaleIdx-- )
|
||||
{
|
||||
__global const ScaleData* s = scaleData + scaleIdx;
|
||||
int ystep = s->ystep;
|
||||
int2 worksize = (int2)(max(s->szi_width - windowsize.x, 0), max(s->szi_height - windowsize.y, 0));
|
||||
int2 ntiles = (int2)((worksize.x + LOCAL_SIZE_X-1)/LOCAL_SIZE_X,
|
||||
(worksize.y + LOCAL_SIZE_Y-1)/LOCAL_SIZE_Y);
|
||||
int totalTiles = ntiles.x*ntiles.y;
|
||||
|
||||
for( tileIdx = groupIdx; tileIdx < totalTiles; tileIdx += ngroups )
|
||||
{
|
||||
int ix0 = (tileIdx % ntiles.x)*LOCAL_SIZE_X;
|
||||
int iy0 = (tileIdx / ntiles.x)*LOCAL_SIZE_Y;
|
||||
int ix = lx, iy = ly;
|
||||
__global const int* psum0 = sum + mad24(iy0, sumstep, ix0) + s->layer_ofs;
|
||||
__global const int* psum1 = psum0 + mad24(iy, sumstep, ix);
|
||||
|
||||
if( ix0 >= worksize.x || iy0 >= worksize.y )
|
||||
continue;
|
||||
#if SUM_BUF_SIZE > 0
|
||||
for( i = lidx*4; i < SUM_BUF_SIZE; i += LOCAL_SIZE_X*LOCAL_SIZE_Y*4 )
|
||||
{
|
||||
int dy = i/SUM_BUF_STEP, dx = i - dy*SUM_BUF_STEP;
|
||||
vstore4(vload4(0, psum0 + mad24(dy, sumstep, dx)), 0, ibuf+i);
|
||||
}
|
||||
#endif
|
||||
|
||||
if( lidx == 0 )
|
||||
lcount[0] = 0;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if( ix0 + ix < worksize.x && iy0 + iy < worksize.y )
|
||||
{
|
||||
#if NODE_COUNT==1
|
||||
__global const Stump* stump = (__global const Stump*)nodes;
|
||||
#else
|
||||
__global const Node* node = nodes;
|
||||
__global const float* leaves = leaves0;
|
||||
#endif
|
||||
#if SUM_BUF_SIZE > 0
|
||||
__local const int* psum = ibuf + mad24(iy, SUM_BUF_STEP, ix);
|
||||
#else
|
||||
__global const int* psum = psum1;
|
||||
#endif
|
||||
|
||||
__global const int* psqsum = (__global const int*)(psum1 + sqofs);
|
||||
float sval = (psum[nofs.x] - psum[nofs.y] - psum[nofs.z] + psum[nofs.w])*invarea;
|
||||
float sqval = (psqsum[nofs0.x] - psqsum[nofs0.y] - psqsum[nofs0.z] + psqsum[nofs0.w])*invarea;
|
||||
float nf = (float)normarea * sqrt(max(sqval - sval * sval, 0.f));
|
||||
nf = nf > 0 ? nf : 1.f;
|
||||
|
||||
for( stageIdx = 0; stageIdx < SPLIT_STAGE; stageIdx++ )
|
||||
{
|
||||
int ntrees = stages[stageIdx].ntrees;
|
||||
float s = 0.f;
|
||||
#if NODE_COUNT==1
|
||||
for( i = 0; i < ntrees; i++ )
|
||||
{
|
||||
float4 st = stump[i].st;
|
||||
__global const OptHaarFeature* f = optfeatures + as_int(st.x);
|
||||
float4 weight = f->weight;
|
||||
|
||||
int4 ofs = f->ofs[0];
|
||||
sval = (psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w])*weight.x;
|
||||
ofs = f->ofs[1];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.y, sval);
|
||||
if( weight.z > 0 )
|
||||
{
|
||||
ofs = f->ofs[2];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.z, sval);
|
||||
}
|
||||
|
||||
s += (sval < st.y*nf) ? st.z : st.w;
|
||||
}
|
||||
stump += ntrees;
|
||||
#else
|
||||
for( i = 0; i < ntrees; i++, node += NODE_COUNT, leaves += NODE_COUNT+1 )
|
||||
{
|
||||
int idx = 0;
|
||||
do
|
||||
{
|
||||
int4 n = node[idx].n;
|
||||
__global const OptHaarFeature* f = optfeatures + n.x;
|
||||
float4 weight = f->weight;
|
||||
|
||||
int4 ofs = f->ofs[0];
|
||||
|
||||
sval = (psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w])*weight.x;
|
||||
ofs = f->ofs[1];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.y, sval);
|
||||
if( weight.z > 0 )
|
||||
{
|
||||
ofs = f->ofs[2];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.z, sval);
|
||||
}
|
||||
|
||||
idx = (sval < as_float(n.y)*nf) ? n.z : n.w;
|
||||
}
|
||||
while(idx > 0);
|
||||
s += leaves[-idx];
|
||||
}
|
||||
#endif
|
||||
|
||||
if( s < stages[stageIdx].threshold )
|
||||
break;
|
||||
}
|
||||
|
||||
if( stageIdx == SPLIT_STAGE && (ystep == 1 || ((ix | iy) & 1) == 0) )
|
||||
{
|
||||
int count = atomic_inc(lcount);
|
||||
lbuf[count] = (int)(ix | (iy << 8));
|
||||
lnf[count] = nf;
|
||||
}
|
||||
}
|
||||
|
||||
for( stageIdx = SPLIT_STAGE; stageIdx < N_STAGES; stageIdx++ )
|
||||
{
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
int nrects = lcount[0];
|
||||
|
||||
if( nrects == 0 )
|
||||
break;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if( lidx == 0 )
|
||||
lcount[0] = 0;
|
||||
|
||||
{
|
||||
#if NODE_COUNT == 1
|
||||
__global const Stump* stump = (__global const Stump*)nodes + stages[stageIdx].first;
|
||||
#else
|
||||
__global const Node* node = nodes + stages[stageIdx].first*NODE_COUNT;
|
||||
__global const float* leaves = leaves0 + stages[stageIdx].first*(NODE_COUNT+1);
|
||||
#endif
|
||||
int nparts = LOCAL_SIZE / nrects;
|
||||
int ntrees = stages[stageIdx].ntrees;
|
||||
int ntrees_p = (ntrees + nparts - 1)/nparts;
|
||||
int nr = lidx / nparts;
|
||||
int partidx = -1, idxval = 0;
|
||||
float partsum = 0.f, nf = 0.f;
|
||||
|
||||
if( nr < nrects )
|
||||
{
|
||||
partidx = lidx % nparts;
|
||||
idxval = lbuf[nr];
|
||||
nf = lnf[nr];
|
||||
|
||||
{
|
||||
int ntrees0 = ntrees_p*partidx;
|
||||
int ntrees1 = min(ntrees0 + ntrees_p, ntrees);
|
||||
int ix1 = idxval & 255, iy1 = idxval >> 8;
|
||||
#if SUM_BUF_SIZE > 0
|
||||
__local const int* psum = ibuf + mad24(iy1, SUM_BUF_STEP, ix1);
|
||||
#else
|
||||
__global const int* psum = psum0 + mad24(iy1, sumstep, ix1);
|
||||
#endif
|
||||
|
||||
#if NODE_COUNT == 1
|
||||
for( i = ntrees0; i < ntrees1; i++ )
|
||||
{
|
||||
float4 st = stump[i].st;
|
||||
__global const OptHaarFeature* f = optfeatures + as_int(st.x);
|
||||
float4 weight = f->weight;
|
||||
|
||||
int4 ofs = f->ofs[0];
|
||||
float sval = (psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w])*weight.x;
|
||||
ofs = f->ofs[1];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.y, sval);
|
||||
//if( weight.z > 0 )
|
||||
if( fabs(weight.z) > 0 )
|
||||
{
|
||||
ofs = f->ofs[2];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.z, sval);
|
||||
}
|
||||
|
||||
partsum += (sval < st.y*nf) ? st.z : st.w;
|
||||
}
|
||||
#else
|
||||
for( i = ntrees0; i < ntrees1; i++ )
|
||||
{
|
||||
int idx = 0;
|
||||
do
|
||||
{
|
||||
int4 n = node[i*2 + idx].n;
|
||||
__global const OptHaarFeature* f = optfeatures + n.x;
|
||||
float4 weight = f->weight;
|
||||
int4 ofs = f->ofs[0];
|
||||
|
||||
float sval = (psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w])*weight.x;
|
||||
ofs = f->ofs[1];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.y, sval);
|
||||
if( weight.z > 0 )
|
||||
{
|
||||
ofs = f->ofs[2];
|
||||
sval = mad((float)(psum[ofs.x] - psum[ofs.y] - psum[ofs.z] + psum[ofs.w]), weight.z, sval);
|
||||
}
|
||||
|
||||
idx = (sval < as_float(n.y)*nf) ? n.z : n.w;
|
||||
}
|
||||
while(idx > 0);
|
||||
partsum += leaves[i*3-idx];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
lpartsum[lidx] = partsum;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if( partidx == 0 )
|
||||
{
|
||||
float s = lpartsum[nr*nparts];
|
||||
for( i = 1; i < nparts; i++ )
|
||||
s += lpartsum[i + nr*nparts];
|
||||
if( s >= stages[stageIdx].threshold )
|
||||
{
|
||||
int count = atomic_inc(lcount);
|
||||
lbuf[count] = idxval;
|
||||
lnf[count] = nf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if( stageIdx == N_STAGES )
|
||||
{
|
||||
int nrects = lcount[0];
|
||||
if( lidx < nrects )
|
||||
{
|
||||
int nfaces = atomic_inc(facepos);
|
||||
if( nfaces < MAX_FACES )
|
||||
{
|
||||
volatile __global int* face = facepos + 1 + nfaces*3;
|
||||
int val = lbuf[lidx];
|
||||
face[0] = scaleIdx;
|
||||
face[1] = ix0 + (val & 255);
|
||||
face[2] = iy0 + (val >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LBP
|
||||
#undef CALC_SUM_OFS_
|
||||
#define CALC_SUM_OFS_(p0, p1, p2, p3, ptr) \
|
||||
((ptr)[p0] - (ptr)[p1] - (ptr)[p2] + (ptr)[p3])
|
||||
|
||||
__kernel void runLBPClassifierStumpSimple(
|
||||
int nscales, __global const ScaleData* scaleData,
|
||||
__global const int* sum,
|
||||
int _sumstep, int sumoffset,
|
||||
__global const OptLBPFeature* optfeatures,
|
||||
__global const Stage* stages,
|
||||
__global const Stump* stumps,
|
||||
__global const int* bitsets,
|
||||
int bitsetSize,
|
||||
|
||||
volatile __global int* facepos,
|
||||
int2 windowsize)
|
||||
{
|
||||
int lx = get_local_id(0);
|
||||
int ly = get_local_id(1);
|
||||
int local_size_x = get_local_size(0);
|
||||
int local_size_y = get_local_size(1);
|
||||
int groupIdx = get_group_id(1)*get_num_groups(0) + get_group_id(0);
|
||||
int ngroups = get_num_groups(0)*get_num_groups(1);
|
||||
int scaleIdx, tileIdx, stageIdx;
|
||||
int sumstep = (int)(_sumstep/sizeof(int));
|
||||
|
||||
for( scaleIdx = nscales-1; scaleIdx >= 0; scaleIdx-- )
|
||||
{
|
||||
__global const ScaleData* s = scaleData + scaleIdx;
|
||||
int ystep = s->ystep;
|
||||
int2 worksize = (int2)(max(s->szi_width - windowsize.x, 0), max(s->szi_height - windowsize.y, 0));
|
||||
int2 ntiles = (int2)((worksize.x/ystep + local_size_x-1)/local_size_x,
|
||||
(worksize.y/ystep + local_size_y-1)/local_size_y);
|
||||
int totalTiles = ntiles.x*ntiles.y;
|
||||
|
||||
for( tileIdx = groupIdx; tileIdx < totalTiles; tileIdx += ngroups )
|
||||
{
|
||||
int iy = mad24((tileIdx / ntiles.x), local_size_y, ly) * ystep;
|
||||
int ix = mad24((tileIdx % ntiles.x), local_size_x, lx) * ystep;
|
||||
|
||||
if( ix < worksize.x && iy < worksize.y )
|
||||
{
|
||||
__global const int* p = sum + mad24(iy, sumstep, ix) + s->layer_ofs;
|
||||
__global const Stump* stump = stumps;
|
||||
__global const int* bitset = bitsets;
|
||||
|
||||
for( stageIdx = 0; stageIdx < N_STAGES; stageIdx++ )
|
||||
{
|
||||
int i, ntrees = stages[stageIdx].ntrees;
|
||||
float s = 0.f;
|
||||
for( i = 0; i < ntrees; i++, stump++, bitset += bitsetSize )
|
||||
{
|
||||
float4 st = stump->st;
|
||||
__global const OptLBPFeature* f = optfeatures + as_int(st.x);
|
||||
int16 ofs = f->ofs;
|
||||
|
||||
int cval = CALC_SUM_OFS_( ofs.s5, ofs.s6, ofs.s9, ofs.sa, p );
|
||||
|
||||
int mask, idx = (CALC_SUM_OFS_( ofs.s0, ofs.s1, ofs.s4, ofs.s5, p ) >= cval ? 4 : 0); // 0
|
||||
idx |= (CALC_SUM_OFS_( ofs.s1, ofs.s2, ofs.s5, ofs.s6, p ) >= cval ? 2 : 0); // 1
|
||||
idx |= (CALC_SUM_OFS_( ofs.s2, ofs.s3, ofs.s6, ofs.s7, p ) >= cval ? 1 : 0); // 2
|
||||
|
||||
mask = (CALC_SUM_OFS_( ofs.s6, ofs.s7, ofs.sa, ofs.sb, p ) >= cval ? 16 : 0); // 5
|
||||
mask |= (CALC_SUM_OFS_( ofs.sa, ofs.sb, ofs.se, ofs.sf, p ) >= cval ? 8 : 0); // 8
|
||||
mask |= (CALC_SUM_OFS_( ofs.s9, ofs.sa, ofs.sd, ofs.se, p ) >= cval ? 4 : 0); // 7
|
||||
mask |= (CALC_SUM_OFS_( ofs.s8, ofs.s9, ofs.sc, ofs.sd, p ) >= cval ? 2 : 0); // 6
|
||||
mask |= (CALC_SUM_OFS_( ofs.s4, ofs.s5, ofs.s8, ofs.s9, p ) >= cval ? 1 : 0); // 7
|
||||
|
||||
s += (bitset[idx] & (1 << mask)) ? st.z : st.w;
|
||||
}
|
||||
|
||||
if( s < stages[stageIdx].threshold )
|
||||
break;
|
||||
}
|
||||
|
||||
if( stageIdx == N_STAGES )
|
||||
{
|
||||
int nfaces = atomic_inc(facepos);
|
||||
if( nfaces < MAX_FACES )
|
||||
{
|
||||
volatile __global int* face = facepos + 1 + nfaces*3;
|
||||
face[0] = scaleIdx;
|
||||
face[1] = ix;
|
||||
face[2] = iy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__kernel __attribute__((reqd_work_group_size(LOCAL_SIZE_X,LOCAL_SIZE_Y,1)))
|
||||
void runLBPClassifierStump(
|
||||
int nscales, __global const ScaleData* scaleData,
|
||||
__global const int* sum,
|
||||
int _sumstep, int sumoffset,
|
||||
__global const OptLBPFeature* optfeatures,
|
||||
__global const Stage* stages,
|
||||
__global const Stump* stumps,
|
||||
__global const int* bitsets,
|
||||
int bitsetSize,
|
||||
|
||||
volatile __global int* facepos,
|
||||
int2 windowsize)
|
||||
{
|
||||
int lx = get_local_id(0);
|
||||
int ly = get_local_id(1);
|
||||
int groupIdx = get_group_id(0);
|
||||
int i, ngroups = get_global_size(0)/LOCAL_SIZE_X;
|
||||
int scaleIdx, tileIdx, stageIdx;
|
||||
int sumstep = (int)(_sumstep/sizeof(int));
|
||||
int lidx = ly*LOCAL_SIZE_X + lx;
|
||||
|
||||
#define LOCAL_SIZE (LOCAL_SIZE_X*LOCAL_SIZE_Y)
|
||||
__local int lstore[SUM_BUF_SIZE + LOCAL_SIZE*3/2+1];
|
||||
#if SUM_BUF_SIZE > 0
|
||||
__local int* ibuf = lstore;
|
||||
__local int* lcount = ibuf + SUM_BUF_SIZE;
|
||||
#else
|
||||
__local int* lcount = lstore;
|
||||
#endif
|
||||
__local float* lpartsum = (__local float*)(lcount + 1);
|
||||
__local short* lbuf = (__local short*)(lpartsum + LOCAL_SIZE);
|
||||
|
||||
for( scaleIdx = nscales-1; scaleIdx >= 0; scaleIdx-- )
|
||||
{
|
||||
__global const ScaleData* s = scaleData + scaleIdx;
|
||||
int ystep = s->ystep;
|
||||
int2 worksize = (int2)(max(s->szi_width - windowsize.x, 0), max(s->szi_height - windowsize.y, 0));
|
||||
int2 ntiles = (int2)((worksize.x + LOCAL_SIZE_X-1)/LOCAL_SIZE_X,
|
||||
(worksize.y + LOCAL_SIZE_Y-1)/LOCAL_SIZE_Y);
|
||||
int totalTiles = ntiles.x*ntiles.y;
|
||||
|
||||
for( tileIdx = groupIdx; tileIdx < totalTiles; tileIdx += ngroups )
|
||||
{
|
||||
int ix0 = (tileIdx % ntiles.x)*LOCAL_SIZE_X;
|
||||
int iy0 = (tileIdx / ntiles.x)*LOCAL_SIZE_Y;
|
||||
int ix = lx, iy = ly;
|
||||
__global const int* psum0 = sum + mad24(iy0, sumstep, ix0) + s->layer_ofs;
|
||||
|
||||
if( ix0 >= worksize.x || iy0 >= worksize.y )
|
||||
continue;
|
||||
#if SUM_BUF_SIZE > 0
|
||||
for( i = lidx*4; i < SUM_BUF_SIZE; i += LOCAL_SIZE_X*LOCAL_SIZE_Y*4 )
|
||||
{
|
||||
int dy = i/SUM_BUF_STEP, dx = i - dy*SUM_BUF_STEP;
|
||||
vstore4(vload4(0, psum0 + mad24(dy, sumstep, dx)), 0, ibuf+i);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
#endif
|
||||
|
||||
if( lidx == 0 )
|
||||
lcount[0] = 0;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if( ix0 + ix < worksize.x && iy0 + iy < worksize.y )
|
||||
{
|
||||
__global const Stump* stump = stumps;
|
||||
__global const int* bitset = bitsets;
|
||||
#if SUM_BUF_SIZE > 0
|
||||
__local const int* p = ibuf + mad24(iy, SUM_BUF_STEP, ix);
|
||||
#else
|
||||
__global const int* p = psum0 + mad24(iy, sumstep, ix);
|
||||
#endif
|
||||
|
||||
for( stageIdx = 0; stageIdx < SPLIT_STAGE; stageIdx++ )
|
||||
{
|
||||
int ntrees = stages[stageIdx].ntrees;
|
||||
float s = 0.f;
|
||||
for( i = 0; i < ntrees; i++, stump++, bitset += bitsetSize )
|
||||
{
|
||||
float4 st = stump->st;
|
||||
__global const OptLBPFeature* f = optfeatures + as_int(st.x);
|
||||
int16 ofs = f->ofs;
|
||||
|
||||
int cval = CALC_SUM_OFS_( ofs.s5, ofs.s6, ofs.s9, ofs.sa, p );
|
||||
|
||||
int mask, idx = (CALC_SUM_OFS_( ofs.s0, ofs.s1, ofs.s4, ofs.s5, p ) >= cval ? 4 : 0); // 0
|
||||
idx |= (CALC_SUM_OFS_( ofs.s1, ofs.s2, ofs.s5, ofs.s6, p ) >= cval ? 2 : 0); // 1
|
||||
idx |= (CALC_SUM_OFS_( ofs.s2, ofs.s3, ofs.s6, ofs.s7, p ) >= cval ? 1 : 0); // 2
|
||||
|
||||
mask = (CALC_SUM_OFS_( ofs.s6, ofs.s7, ofs.sa, ofs.sb, p ) >= cval ? 16 : 0); // 5
|
||||
mask |= (CALC_SUM_OFS_( ofs.sa, ofs.sb, ofs.se, ofs.sf, p ) >= cval ? 8 : 0); // 8
|
||||
mask |= (CALC_SUM_OFS_( ofs.s9, ofs.sa, ofs.sd, ofs.se, p ) >= cval ? 4 : 0); // 7
|
||||
mask |= (CALC_SUM_OFS_( ofs.s8, ofs.s9, ofs.sc, ofs.sd, p ) >= cval ? 2 : 0); // 6
|
||||
mask |= (CALC_SUM_OFS_( ofs.s4, ofs.s5, ofs.s8, ofs.s9, p ) >= cval ? 1 : 0); // 7
|
||||
|
||||
s += (bitset[idx] & (1 << mask)) ? st.z : st.w;
|
||||
}
|
||||
|
||||
if( s < stages[stageIdx].threshold )
|
||||
break;
|
||||
}
|
||||
|
||||
if( stageIdx == SPLIT_STAGE && (ystep == 1 || ((ix | iy) & 1) == 0) )
|
||||
{
|
||||
int count = atomic_inc(lcount);
|
||||
lbuf[count] = (int)(ix | (iy << 8));
|
||||
}
|
||||
}
|
||||
|
||||
for( stageIdx = SPLIT_STAGE; stageIdx < N_STAGES; stageIdx++ )
|
||||
{
|
||||
int nrects = lcount[0];
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if( nrects == 0 )
|
||||
break;
|
||||
if( lidx == 0 )
|
||||
lcount[0] = 0;
|
||||
|
||||
{
|
||||
__global const Stump* stump = stumps + stages[stageIdx].first;
|
||||
__global const int* bitset = bitsets + stages[stageIdx].first*bitsetSize;
|
||||
int nparts = LOCAL_SIZE / nrects;
|
||||
int ntrees = stages[stageIdx].ntrees;
|
||||
int ntrees_p = (ntrees + nparts - 1)/nparts;
|
||||
int nr = lidx / nparts;
|
||||
int partidx = -1, idxval = 0;
|
||||
float partsum = 0.f, nf = 0.f;
|
||||
|
||||
if( nr < nrects )
|
||||
{
|
||||
partidx = lidx % nparts;
|
||||
idxval = lbuf[nr];
|
||||
|
||||
{
|
||||
int ntrees0 = ntrees_p*partidx;
|
||||
int ntrees1 = min(ntrees0 + ntrees_p, ntrees);
|
||||
int ix1 = idxval & 255, iy1 = idxval >> 8;
|
||||
#if SUM_BUF_SIZE > 0
|
||||
__local const int* p = ibuf + mad24(iy1, SUM_BUF_STEP, ix1);
|
||||
#else
|
||||
__global const int* p = psum0 + mad24(iy1, sumstep, ix1);
|
||||
#endif
|
||||
|
||||
for( i = ntrees0; i < ntrees1; i++ )
|
||||
{
|
||||
float4 st = stump[i].st;
|
||||
__global const OptLBPFeature* f = optfeatures + as_int(st.x);
|
||||
int16 ofs = f->ofs;
|
||||
|
||||
#define CALC_SUM_OFS_(p0, p1, p2, p3, ptr) \
|
||||
((ptr)[p0] - (ptr)[p1] - (ptr)[p2] + (ptr)[p3])
|
||||
|
||||
int cval = CALC_SUM_OFS_( ofs.s5, ofs.s6, ofs.s9, ofs.sa, p );
|
||||
|
||||
int mask, idx = (CALC_SUM_OFS_( ofs.s0, ofs.s1, ofs.s4, ofs.s5, p ) >= cval ? 4 : 0); // 0
|
||||
idx |= (CALC_SUM_OFS_( ofs.s1, ofs.s2, ofs.s5, ofs.s6, p ) >= cval ? 2 : 0); // 1
|
||||
idx |= (CALC_SUM_OFS_( ofs.s2, ofs.s3, ofs.s6, ofs.s7, p ) >= cval ? 1 : 0); // 2
|
||||
|
||||
mask = (CALC_SUM_OFS_( ofs.s6, ofs.s7, ofs.sa, ofs.sb, p ) >= cval ? 16 : 0); // 5
|
||||
mask |= (CALC_SUM_OFS_( ofs.sa, ofs.sb, ofs.se, ofs.sf, p ) >= cval ? 8 : 0); // 8
|
||||
mask |= (CALC_SUM_OFS_( ofs.s9, ofs.sa, ofs.sd, ofs.se, p ) >= cval ? 4 : 0); // 7
|
||||
mask |= (CALC_SUM_OFS_( ofs.s8, ofs.s9, ofs.sc, ofs.sd, p ) >= cval ? 2 : 0); // 6
|
||||
mask |= (CALC_SUM_OFS_( ofs.s4, ofs.s5, ofs.s8, ofs.s9, p ) >= cval ? 1 : 0); // 7
|
||||
|
||||
partsum += (bitset[i*bitsetSize + idx] & (1 << mask)) ? st.z : st.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
lpartsum[lidx] = partsum;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if( partidx == 0 )
|
||||
{
|
||||
float s = lpartsum[nr*nparts];
|
||||
for( i = 1; i < nparts; i++ )
|
||||
s += lpartsum[i + nr*nparts];
|
||||
if( s >= stages[stageIdx].threshold )
|
||||
{
|
||||
int count = atomic_inc(lcount);
|
||||
lbuf[count] = idxval;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if( stageIdx == N_STAGES )
|
||||
{
|
||||
int nrects = lcount[0];
|
||||
if( lidx < nrects )
|
||||
{
|
||||
int nfaces = atomic_inc(facepos);
|
||||
if( nfaces < MAX_FACES )
|
||||
{
|
||||
volatile __global int* face = facepos + 1 + nfaces*3;
|
||||
int val = lbuf[lidx];
|
||||
face[0] = scaleIdx;
|
||||
face[1] = ix0 + (val & 255);
|
||||
face[2] = iy0 + (val >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,633 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Wenju He, wenju@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#define CELL_WIDTH 8
|
||||
#define CELL_HEIGHT 8
|
||||
#define CELLS_PER_BLOCK_X 2
|
||||
#define CELLS_PER_BLOCK_Y 2
|
||||
#define NTHREADS 256
|
||||
#define CV_PI_F M_PI_F
|
||||
|
||||
#ifdef INTEL_DEVICE
|
||||
#define QANGLE_TYPE int
|
||||
#define QANGLE_TYPE2 int2
|
||||
#else
|
||||
#define QANGLE_TYPE uchar
|
||||
#define QANGLE_TYPE2 uchar2
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Histogram computation
|
||||
// 12 threads for a cell, 12x4 threads per block
|
||||
// Use pre-computed gaussian and interp_weight lookup tables
|
||||
__kernel void compute_hists_lut_kernel(
|
||||
const int cblock_stride_x, const int cblock_stride_y,
|
||||
const int cnbins, const int cblock_hist_size, const int img_block_width,
|
||||
const int blocks_in_group, const int blocks_total,
|
||||
const int grad_quadstep, const int qangle_step,
|
||||
__global const float* grad, __global const QANGLE_TYPE* qangle,
|
||||
__global const float* gauss_w_lut,
|
||||
__global float* block_hists, __local float* smem)
|
||||
{
|
||||
const int lx = get_local_id(0);
|
||||
const int lp = lx / 24; /* local group id */
|
||||
const int gid = get_group_id(0) * blocks_in_group + lp;/* global group id */
|
||||
const int gidY = gid / img_block_width;
|
||||
const int gidX = gid - gidY * img_block_width;
|
||||
|
||||
const int lidX = lx - lp * 24;
|
||||
const int lidY = get_local_id(1);
|
||||
|
||||
const int cell_x = lidX / 12;
|
||||
const int cell_y = lidY;
|
||||
const int cell_thread_x = lidX - cell_x * 12;
|
||||
|
||||
__local float* hists = smem + lp * cnbins * (CELLS_PER_BLOCK_X *
|
||||
CELLS_PER_BLOCK_Y * 12 + CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y);
|
||||
__local float* final_hist = hists + cnbins *
|
||||
(CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y * 12);
|
||||
|
||||
const int offset_x = gidX * cblock_stride_x + (cell_x << 2) + cell_thread_x;
|
||||
const int offset_y = gidY * cblock_stride_y + (cell_y << 2);
|
||||
|
||||
__global const float* grad_ptr = (gid < blocks_total) ?
|
||||
grad + offset_y * grad_quadstep + (offset_x << 1) : grad;
|
||||
__global const QANGLE_TYPE* qangle_ptr = (gid < blocks_total) ?
|
||||
qangle + offset_y * qangle_step + (offset_x << 1) : qangle;
|
||||
|
||||
__local float* hist = hists + 12 * (cell_y * CELLS_PER_BLOCK_Y + cell_x) +
|
||||
cell_thread_x;
|
||||
for (int bin_id = 0; bin_id < cnbins; ++bin_id)
|
||||
hist[bin_id * 48] = 0.f;
|
||||
|
||||
const int dist_x = -4 + cell_thread_x - 4 * cell_x;
|
||||
const int dist_center_x = dist_x - 4 * (1 - 2 * cell_x);
|
||||
|
||||
const int dist_y_begin = -4 - 4 * lidY;
|
||||
for (int dist_y = dist_y_begin; dist_y < dist_y_begin + 12; ++dist_y)
|
||||
{
|
||||
float2 vote = (float2) (grad_ptr[0], grad_ptr[1]);
|
||||
QANGLE_TYPE2 bin = (QANGLE_TYPE2) (qangle_ptr[0], qangle_ptr[1]);
|
||||
|
||||
grad_ptr += grad_quadstep;
|
||||
qangle_ptr += qangle_step;
|
||||
|
||||
int dist_center_y = dist_y - 4 * (1 - 2 * cell_y);
|
||||
|
||||
int idx = (dist_center_y + 8) * 16 + (dist_center_x + 8);
|
||||
float gaussian = gauss_w_lut[idx];
|
||||
idx = (dist_y + 8) * 16 + (dist_x + 8);
|
||||
float interp_weight = gauss_w_lut[256+idx];
|
||||
|
||||
hist[bin.x * 48] += gaussian * interp_weight * vote.x;
|
||||
hist[bin.y * 48] += gaussian * interp_weight * vote.y;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
volatile __local float* hist_ = hist;
|
||||
for (int bin_id = 0; bin_id < cnbins; ++bin_id, hist_ += 48)
|
||||
{
|
||||
if (cell_thread_x < 6)
|
||||
hist_[0] += hist_[6];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (cell_thread_x < 3)
|
||||
hist_[0] += hist_[3];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (cell_thread_x == 0)
|
||||
final_hist[(cell_x * 2 + cell_y) * cnbins + bin_id] =
|
||||
hist_[0] + hist_[1] + hist_[2];
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
int tid = (cell_y * CELLS_PER_BLOCK_Y + cell_x) * 12 + cell_thread_x;
|
||||
if ((tid < cblock_hist_size) && (gid < blocks_total))
|
||||
{
|
||||
__global float* block_hist = block_hists +
|
||||
(gidY * img_block_width + gidX) * cblock_hist_size;
|
||||
block_hist[tid] = final_hist[tid];
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Normalization of histograms via L2Hys_norm
|
||||
// optimized for the case of 9 bins
|
||||
__kernel void normalize_hists_36_kernel(__global float* block_hists,
|
||||
const float threshold, __local float *squares)
|
||||
{
|
||||
const int tid = get_local_id(0);
|
||||
const int gid = get_global_id(0);
|
||||
const int bid = tid / 36; /* block-hist id, (0 - 6) */
|
||||
const int boffset = bid * 36; /* block-hist offset in the work-group */
|
||||
const int hid = tid - boffset; /* histogram bin id, (0 - 35) */
|
||||
|
||||
float elem = block_hists[gid];
|
||||
squares[tid] = elem * elem;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
__local float* smem = squares + boffset;
|
||||
float sum = smem[hid];
|
||||
if (hid < 18)
|
||||
smem[hid] = sum = sum + smem[hid + 18];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (hid < 9)
|
||||
smem[hid] = sum = sum + smem[hid + 9];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (hid < 4)
|
||||
smem[hid] = sum + smem[hid + 4];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
sum = smem[0] + smem[1] + smem[2] + smem[3] + smem[8];
|
||||
|
||||
elem = elem / (sqrt(sum) + 3.6f);
|
||||
elem = min(elem, threshold);
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
squares[tid] = elem * elem;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
sum = smem[hid];
|
||||
if (hid < 18)
|
||||
smem[hid] = sum = sum + smem[hid + 18];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (hid < 9)
|
||||
smem[hid] = sum = sum + smem[hid + 9];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (hid < 4)
|
||||
smem[hid] = sum + smem[hid + 4];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
sum = smem[0] + smem[1] + smem[2] + smem[3] + smem[8];
|
||||
|
||||
block_hists[gid] = elem / (sqrt(sum) + 1e-3f);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Normalization of histograms via L2Hys_norm
|
||||
//
|
||||
inline float reduce_smem(volatile __local float* smem, int size)
|
||||
{
|
||||
unsigned int tid = get_local_id(0);
|
||||
float sum = smem[tid];
|
||||
|
||||
if (size >= 512) { if (tid < 256) smem[tid] = sum = sum + smem[tid + 256];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 256) { if (tid < 128) smem[tid] = sum = sum + smem[tid + 128];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 128) { if (tid < 64) smem[tid] = sum = sum + smem[tid + 64];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 64) { if (tid < 32) smem[tid] = sum = sum + smem[tid + 32];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 32) { if (tid < 16) smem[tid] = sum = sum + smem[tid + 16];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 16) { if (tid < 8) smem[tid] = sum = sum + smem[tid + 8];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 8) { if (tid < 4) smem[tid] = sum = sum + smem[tid + 4];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 4) { if (tid < 2) smem[tid] = sum = sum + smem[tid + 2];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
if (size >= 2) { if (tid < 1) smem[tid] = sum = sum + smem[tid + 1];
|
||||
barrier(CLK_LOCAL_MEM_FENCE); }
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
__kernel void normalize_hists_kernel(
|
||||
const int nthreads, const int block_hist_size, const int img_block_width,
|
||||
__global float* block_hists, const float threshold, __local float *squares)
|
||||
{
|
||||
const int tid = get_local_id(0);
|
||||
const int gidX = get_group_id(0);
|
||||
const int gidY = get_group_id(1);
|
||||
|
||||
__global float* hist = block_hists + (gidY * img_block_width + gidX) *
|
||||
block_hist_size + tid;
|
||||
|
||||
float elem = 0.f;
|
||||
if (tid < block_hist_size)
|
||||
elem = hist[0];
|
||||
|
||||
squares[tid] = elem * elem;
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
float sum = reduce_smem(squares, nthreads);
|
||||
|
||||
float scale = 1.0f / (sqrt(sum) + 0.1f * block_hist_size);
|
||||
elem = min(elem * scale, threshold);
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
squares[tid] = elem * elem;
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
sum = reduce_smem(squares, nthreads);
|
||||
scale = 1.0f / (sqrt(sum) + 1e-3f);
|
||||
|
||||
if (tid < block_hist_size)
|
||||
hist[0] = elem * scale;
|
||||
}
|
||||
|
||||
#define reduce_with_sync(target, sharedMemory, localMemory, tid, offset) \
|
||||
if (tid < target) sharedMemory[tid] = localMemory = localMemory + sharedMemory[tid + offset]; \
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Linear SVM based classification
|
||||
// 48x96 window, 9 bins and default parameters
|
||||
// 180 threads, each thread corresponds to a bin in a row
|
||||
__kernel void classify_hists_180_kernel(
|
||||
const int cdescr_width, const int cdescr_height, const int cblock_hist_size,
|
||||
const int img_win_width, const int img_block_width,
|
||||
const int win_block_stride_x, const int win_block_stride_y,
|
||||
__global const float * block_hists, __global const float* coefs,
|
||||
float free_coef, float threshold, __global uchar* labels)
|
||||
{
|
||||
const int tid = get_local_id(0);
|
||||
const int gidX = get_group_id(0);
|
||||
const int gidY = get_group_id(1);
|
||||
|
||||
__global const float* hist = block_hists + (gidY * win_block_stride_y *
|
||||
img_block_width + gidX * win_block_stride_x) * cblock_hist_size;
|
||||
|
||||
float product = 0.f;
|
||||
|
||||
for (int i = 0; i < cdescr_height; i++)
|
||||
{
|
||||
product += coefs[i * cdescr_width + tid] *
|
||||
hist[i * img_block_width * cblock_hist_size + tid];
|
||||
}
|
||||
|
||||
__local float products[180];
|
||||
|
||||
products[tid] = product;
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
reduce_with_sync(90, products, product, tid, 90);
|
||||
reduce_with_sync(45, products, product, tid, 45);
|
||||
reduce_with_sync(13, products, product, tid, 32); // 13 is not typo
|
||||
reduce_with_sync(16, products, product, tid, 16);
|
||||
reduce_with_sync(8, products, product, tid, 8);
|
||||
reduce_with_sync(4, products, product, tid, 4);
|
||||
reduce_with_sync(2, products, product, tid, 2);
|
||||
|
||||
if (tid == 0){
|
||||
product = product + products[tid + 1];
|
||||
labels[gidY * img_win_width + gidX] = (product + free_coef >= threshold);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Linear SVM based classification
|
||||
// 64x128 window, 9 bins and default parameters
|
||||
// 256 threads, 252 of them are used
|
||||
__kernel void classify_hists_252_kernel(
|
||||
const int cdescr_width, const int cdescr_height, const int cblock_hist_size,
|
||||
const int img_win_width, const int img_block_width,
|
||||
const int win_block_stride_x, const int win_block_stride_y,
|
||||
__global const float * block_hists, __global const float* coefs,
|
||||
float free_coef, float threshold, __global uchar* labels)
|
||||
{
|
||||
const int tid = get_local_id(0);
|
||||
const int gidX = get_group_id(0);
|
||||
const int gidY = get_group_id(1);
|
||||
|
||||
__global const float* hist = block_hists + (gidY * win_block_stride_y *
|
||||
img_block_width + gidX * win_block_stride_x) * cblock_hist_size;
|
||||
|
||||
float product = 0.f;
|
||||
if (tid < cdescr_width)
|
||||
{
|
||||
for (int i = 0; i < cdescr_height; i++)
|
||||
product += coefs[i * cdescr_width + tid] *
|
||||
hist[i * img_block_width * cblock_hist_size + tid];
|
||||
}
|
||||
|
||||
__local float products[NTHREADS];
|
||||
|
||||
products[tid] = product;
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
reduce_with_sync(128, products, product, tid, 128);
|
||||
reduce_with_sync(64, products, product, tid, 64);
|
||||
reduce_with_sync(32, products, product, tid, 32);
|
||||
reduce_with_sync(16, products, product, tid, 16);
|
||||
reduce_with_sync(8, products, product, tid, 8);
|
||||
reduce_with_sync(4, products, product, tid, 4);
|
||||
reduce_with_sync(2, products, product, tid, 2);
|
||||
|
||||
if (tid == 0){
|
||||
product = product + products[tid + 1];
|
||||
labels[gidY * img_win_width + gidX] = (product + free_coef >= threshold);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Linear SVM based classification
|
||||
// 256 threads
|
||||
__kernel void classify_hists_kernel(
|
||||
const int cdescr_size, const int cdescr_width, const int cblock_hist_size,
|
||||
const int img_win_width, const int img_block_width,
|
||||
const int win_block_stride_x, const int win_block_stride_y,
|
||||
__global const float * block_hists, __global const float* coefs,
|
||||
float free_coef, float threshold, __global uchar* labels)
|
||||
{
|
||||
const int tid = get_local_id(0);
|
||||
const int gidX = get_group_id(0);
|
||||
const int gidY = get_group_id(1);
|
||||
|
||||
__global const float* hist = block_hists + (gidY * win_block_stride_y *
|
||||
img_block_width + gidX * win_block_stride_x) * cblock_hist_size;
|
||||
|
||||
float product = 0.f;
|
||||
for (int i = tid; i < cdescr_size; i += NTHREADS)
|
||||
{
|
||||
int offset_y = i / cdescr_width;
|
||||
int offset_x = i - offset_y * cdescr_width;
|
||||
product += coefs[i] *
|
||||
hist[offset_y * img_block_width * cblock_hist_size + offset_x];
|
||||
}
|
||||
|
||||
__local float products[NTHREADS];
|
||||
|
||||
products[tid] = product;
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
reduce_with_sync(128, products, product, tid, 128);
|
||||
reduce_with_sync(64, products, product, tid, 64);
|
||||
reduce_with_sync(32, products, product, tid, 32);
|
||||
reduce_with_sync(16, products, product, tid, 16);
|
||||
reduce_with_sync(8, products, product, tid, 8);
|
||||
reduce_with_sync(4, products, product, tid, 4);
|
||||
reduce_with_sync(2, products, product, tid, 2);
|
||||
|
||||
if (tid == 0){
|
||||
products[tid] = product = product + products[tid + 1];
|
||||
labels[gidY * img_win_width + gidX] = (product + free_coef >= threshold);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Extract descriptors
|
||||
|
||||
__kernel void extract_descrs_by_rows_kernel(
|
||||
const int cblock_hist_size, const int descriptors_quadstep,
|
||||
const int cdescr_size, const int cdescr_width, const int img_block_width,
|
||||
const int win_block_stride_x, const int win_block_stride_y,
|
||||
__global const float* block_hists, __global float* descriptors)
|
||||
{
|
||||
int tid = get_local_id(0);
|
||||
int gidX = get_group_id(0);
|
||||
int gidY = get_group_id(1);
|
||||
|
||||
// Get left top corner of the window in src
|
||||
__global const float* hist = block_hists + (gidY * win_block_stride_y *
|
||||
img_block_width + gidX * win_block_stride_x) * cblock_hist_size;
|
||||
|
||||
// Get left top corner of the window in dst
|
||||
__global float* descriptor = descriptors +
|
||||
(gidY * get_num_groups(0) + gidX) * descriptors_quadstep;
|
||||
|
||||
// Copy elements from src to dst
|
||||
for (int i = tid; i < cdescr_size; i += NTHREADS)
|
||||
{
|
||||
int offset_y = i / cdescr_width;
|
||||
int offset_x = i - offset_y * cdescr_width;
|
||||
descriptor[i] = hist[offset_y * img_block_width * cblock_hist_size + offset_x];
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void extract_descrs_by_cols_kernel(
|
||||
const int cblock_hist_size, const int descriptors_quadstep, const int cdescr_size,
|
||||
const int cnblocks_win_x, const int cnblocks_win_y, const int img_block_width,
|
||||
const int win_block_stride_x, const int win_block_stride_y,
|
||||
__global const float* block_hists, __global float* descriptors)
|
||||
{
|
||||
int tid = get_local_id(0);
|
||||
int gidX = get_group_id(0);
|
||||
int gidY = get_group_id(1);
|
||||
|
||||
// Get left top corner of the window in src
|
||||
__global const float* hist = block_hists + (gidY * win_block_stride_y *
|
||||
img_block_width + gidX * win_block_stride_x) * cblock_hist_size;
|
||||
|
||||
// Get left top corner of the window in dst
|
||||
__global float* descriptor = descriptors +
|
||||
(gidY * get_num_groups(0) + gidX) * descriptors_quadstep;
|
||||
|
||||
// Copy elements from src to dst
|
||||
for (int i = tid; i < cdescr_size; i += NTHREADS)
|
||||
{
|
||||
int block_idx = i / cblock_hist_size;
|
||||
int idx_in_block = i - block_idx * cblock_hist_size;
|
||||
|
||||
int y = block_idx / cnblocks_win_x;
|
||||
int x = block_idx - y * cnblocks_win_x;
|
||||
|
||||
descriptor[(x * cnblocks_win_y + y) * cblock_hist_size + idx_in_block] =
|
||||
hist[(y * img_block_width + x) * cblock_hist_size + idx_in_block];
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Gradients computation
|
||||
|
||||
__kernel void compute_gradients_8UC4_kernel(
|
||||
const int height, const int width,
|
||||
const int img_step, const int grad_quadstep, const int qangle_step,
|
||||
const __global uchar4 * img, __global float * grad, __global QANGLE_TYPE * qangle,
|
||||
const float angle_scale, const char correct_gamma, const int cnbins)
|
||||
{
|
||||
const int x = get_global_id(0);
|
||||
const int tid = get_local_id(0);
|
||||
const int gSizeX = get_local_size(0);
|
||||
const int gidY = get_group_id(1);
|
||||
|
||||
__global const uchar4* row = img + gidY * img_step;
|
||||
|
||||
__local float sh_row[(NTHREADS + 2) * 3];
|
||||
|
||||
uchar4 val;
|
||||
if (x < width)
|
||||
val = row[x];
|
||||
else
|
||||
val = row[width - 2];
|
||||
|
||||
sh_row[tid + 1] = val.x;
|
||||
sh_row[tid + 1 + (NTHREADS + 2)] = val.y;
|
||||
sh_row[tid + 1 + 2 * (NTHREADS + 2)] = val.z;
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
val = row[max(x - 1, 1)];
|
||||
sh_row[0] = val.x;
|
||||
sh_row[(NTHREADS + 2)] = val.y;
|
||||
sh_row[2 * (NTHREADS + 2)] = val.z;
|
||||
}
|
||||
|
||||
if (tid == gSizeX - 1)
|
||||
{
|
||||
val = row[min(x + 1, width - 2)];
|
||||
sh_row[gSizeX + 1] = val.x;
|
||||
sh_row[gSizeX + 1 + (NTHREADS + 2)] = val.y;
|
||||
sh_row[gSizeX + 1 + 2 * (NTHREADS + 2)] = val.z;
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (x < width)
|
||||
{
|
||||
float4 a = (float4) (sh_row[tid], sh_row[tid + (NTHREADS + 2)],
|
||||
sh_row[tid + 2 * (NTHREADS + 2)], 0);
|
||||
float4 b = (float4) (sh_row[tid + 2], sh_row[tid + 2 + (NTHREADS + 2)],
|
||||
sh_row[tid + 2 + 2 * (NTHREADS + 2)], 0);
|
||||
|
||||
float4 dx;
|
||||
if (correct_gamma == 1)
|
||||
dx = sqrt(b) - sqrt(a);
|
||||
else
|
||||
dx = b - a;
|
||||
|
||||
float4 dy = (float4) 0.f;
|
||||
|
||||
if (gidY > 0 && gidY < height - 1)
|
||||
{
|
||||
a = convert_float4(img[(gidY - 1) * img_step + x].xyzw);
|
||||
b = convert_float4(img[(gidY + 1) * img_step + x].xyzw);
|
||||
|
||||
if (correct_gamma == 1)
|
||||
dy = sqrt(b) - sqrt(a);
|
||||
else
|
||||
dy = b - a;
|
||||
}
|
||||
|
||||
float4 mag = hypot(dx, dy);
|
||||
float best_dx = dx.x;
|
||||
float best_dy = dy.x;
|
||||
|
||||
float mag0 = mag.x;
|
||||
if (mag0 < mag.y)
|
||||
{
|
||||
best_dx = dx.y;
|
||||
best_dy = dy.y;
|
||||
mag0 = mag.y;
|
||||
}
|
||||
|
||||
if (mag0 < mag.z)
|
||||
{
|
||||
best_dx = dx.z;
|
||||
best_dy = dy.z;
|
||||
mag0 = mag.z;
|
||||
}
|
||||
|
||||
float ang = (atan2(best_dy, best_dx) + CV_PI_F) * angle_scale - 0.5f;
|
||||
int hidx = (int)floor(ang);
|
||||
ang -= hidx;
|
||||
hidx = (hidx + cnbins) % cnbins;
|
||||
|
||||
qangle[(gidY * qangle_step + x) << 1] = hidx;
|
||||
qangle[((gidY * qangle_step + x) << 1) + 1] = (hidx + 1) % cnbins;
|
||||
grad[(gidY * grad_quadstep + x) << 1] = mag0 * (1.f - ang);
|
||||
grad[((gidY * grad_quadstep + x) << 1) + 1] = mag0 * ang;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void compute_gradients_8UC1_kernel(
|
||||
const int height, const int width,
|
||||
const int img_step, const int grad_quadstep, const int qangle_step,
|
||||
__global const uchar * img, __global float * grad, __global QANGLE_TYPE * qangle,
|
||||
const float angle_scale, const char correct_gamma, const int cnbins)
|
||||
{
|
||||
const int x = get_global_id(0);
|
||||
const int tid = get_local_id(0);
|
||||
const int gSizeX = get_local_size(0);
|
||||
const int gidY = get_group_id(1);
|
||||
|
||||
__global const uchar* row = img + gidY * img_step;
|
||||
|
||||
__local float sh_row[NTHREADS + 2];
|
||||
|
||||
if (x < width)
|
||||
sh_row[tid + 1] = row[x];
|
||||
else
|
||||
sh_row[tid + 1] = row[width - 2];
|
||||
|
||||
if (tid == 0)
|
||||
sh_row[0] = row[max(x - 1, 1)];
|
||||
|
||||
if (tid == gSizeX - 1)
|
||||
sh_row[gSizeX + 1] = row[min(x + 1, width - 2)];
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (x < width)
|
||||
{
|
||||
float dx;
|
||||
|
||||
if (correct_gamma == 1)
|
||||
dx = sqrt(sh_row[tid + 2]) - sqrt(sh_row[tid]);
|
||||
else
|
||||
dx = sh_row[tid + 2] - sh_row[tid];
|
||||
|
||||
float dy = 0.f;
|
||||
if (gidY > 0 && gidY < height - 1)
|
||||
{
|
||||
float a = (float) img[ (gidY + 1) * img_step + x ];
|
||||
float b = (float) img[ (gidY - 1) * img_step + x ];
|
||||
if (correct_gamma == 1)
|
||||
dy = sqrt(a) - sqrt(b);
|
||||
else
|
||||
dy = a - b;
|
||||
}
|
||||
float mag = hypot(dx, dy);
|
||||
|
||||
float ang = (atan2(dy, dx) + CV_PI_F) * angle_scale - 0.5f;
|
||||
int hidx = (int)floor(ang);
|
||||
ang -= hidx;
|
||||
hidx = (hidx + cnbins) % cnbins;
|
||||
|
||||
qangle[ (gidY * qangle_step + x) << 1 ] = hidx;
|
||||
qangle[ ((gidY * qangle_step + x) << 1) + 1 ] = (hidx + 1) % cnbins;
|
||||
grad[ (gidY * grad_quadstep + x) << 1 ] = mag * (1.f - ang);
|
||||
grad[ ((gidY * grad_quadstep + x) << 1) + 1 ] = mag * ang;
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Niko Li, newlife20080214@gmail.com
|
||||
// Jia Haipeng, jiahaipeng95@gmail.com
|
||||
// Shengen Yan, yanshengen@gmail.com
|
||||
// Jiang Liyuan,jlyuan001.good@163.com
|
||||
// Rock Li, Rock.Li@amd.com
|
||||
// Zailong Wu, bullet@yeah.net
|
||||
// Yao Wang, bitwangyaoyao@gmail.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
///////////////////// HOG /////////////////////////////
|
||||
PARAM_TEST_CASE(HOG, Size, MatType)
|
||||
{
|
||||
Size winSize;
|
||||
int type;
|
||||
Mat img;
|
||||
UMat uimg;
|
||||
virtual void SetUp()
|
||||
{
|
||||
winSize = GET_PARAM(0);
|
||||
type = GET_PARAM(1);
|
||||
img = readImage("cascadeandhog/images/image_00000000_0.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(img.empty());
|
||||
img.copyTo(uimg);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(HOG, GetDescriptors)
|
||||
{
|
||||
HOGDescriptor hog;
|
||||
hog.gammaCorrection = true;
|
||||
|
||||
hog.setSVMDetector(hog.getDefaultPeopleDetector());
|
||||
|
||||
std::vector<float> cpu_descriptors;
|
||||
std::vector<float> gpu_descriptors;
|
||||
|
||||
OCL_OFF(hog.compute(img, cpu_descriptors, hog.winSize));
|
||||
OCL_ON(hog.compute(uimg, gpu_descriptors, hog.winSize));
|
||||
|
||||
Mat cpu_desc(cpu_descriptors), gpu_desc(gpu_descriptors);
|
||||
|
||||
EXPECT_MAT_SIMILAR(cpu_desc, gpu_desc, 1e-1);
|
||||
}
|
||||
|
||||
OCL_TEST_P(HOG, SVMDetector)
|
||||
{
|
||||
HOGDescriptor hog_first, hog_second;
|
||||
|
||||
// empty -> empty
|
||||
hog_first.copyTo(hog_second);
|
||||
|
||||
// first -> both
|
||||
hog_first.setSVMDetector(hog_first.getDefaultPeopleDetector());
|
||||
hog_first.copyTo(hog_second);
|
||||
|
||||
// both -> both
|
||||
hog_first.copyTo(hog_second);
|
||||
|
||||
// second -> empty
|
||||
hog_first.setSVMDetector(cv::noArray());
|
||||
hog_first.copyTo(hog_second);
|
||||
}
|
||||
|
||||
OCL_TEST_P(HOG, Detect)
|
||||
{
|
||||
HOGDescriptor hog;
|
||||
hog.winSize = winSize;
|
||||
hog.gammaCorrection = true;
|
||||
|
||||
if (winSize.width == 48 && winSize.height == 96)
|
||||
hog.setSVMDetector(hog.getDaimlerPeopleDetector());
|
||||
else
|
||||
hog.setSVMDetector(hog.getDefaultPeopleDetector());
|
||||
|
||||
std::vector<Rect> cpu_found;
|
||||
std::vector<Rect> gpu_found;
|
||||
|
||||
OCL_OFF(hog.detectMultiScale(img, cpu_found, 0, Size(8, 8), Size(0, 0), 1.05, 6));
|
||||
OCL_ON(hog.detectMultiScale(uimg, gpu_found, 0, Size(8, 8), Size(0, 0), 1.05, 6));
|
||||
|
||||
EXPECT_LT(checkRectSimilarity(img.size(), cpu_found, gpu_found), 0.05);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(OCL_ObjDetect, HOG, testing::Combine(
|
||||
testing::Values(Size(64, 128), Size(48, 96)),
|
||||
testing::Values( MatType(CV_8UC1) ) ) );
|
||||
|
||||
}} // namespace
|
||||
#endif
|
@ -109,11 +109,9 @@ imgproc = {
|
||||
],
|
||||
}
|
||||
|
||||
objdetect = {'': ['groupRectangles', 'getPredefinedDictionary', 'extendDictionary',
|
||||
objdetect = {'': ['getPredefinedDictionary', 'extendDictionary',
|
||||
'drawDetectedMarkers', 'generateImageMarker', 'drawDetectedCornersCharuco',
|
||||
'drawDetectedDiamonds'],
|
||||
'HOGDescriptor': ['load', 'HOGDescriptor', 'getDefaultPeopleDetector', 'getDaimlerPeopleDetector', 'setSVMDetector', 'detectMultiScale'],
|
||||
'CascadeClassifier': ['load', 'detectMultiScale2', 'CascadeClassifier', 'detectMultiScale3', 'empty', 'detectMultiScale'],
|
||||
'GraphicalCodeDetector': ['decode', 'detect', 'detectAndDecode', 'detectMulti', 'decodeMulti', 'detectAndDecodeMulti'],
|
||||
'QRCodeDetector': ['QRCodeDetector', 'decode', 'detect', 'detectAndDecode', 'detectMulti', 'decodeMulti', 'detectAndDecodeMulti', 'decodeCurved', 'detectAndDecodeCurved', 'setEpsX', 'setEpsY'],
|
||||
'aruco_PredefinedDictionaryType': [],
|
||||
|
@ -1,111 +0,0 @@
|
||||
#if defined(__linux__) || defined(LINUX) || defined(__APPLE__) || defined(ANDROID) || (defined(_MSC_VER) && _MSC_VER>=1800)
|
||||
|
||||
#include <opencv2/imgproc.hpp> // Gaussian Blur
|
||||
#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp> // OpenCV window I/O
|
||||
#include <opencv2/features2d.hpp>
|
||||
#include <opencv2/objdetect.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
const string WindowName = "Face Detection example";
|
||||
|
||||
class CascadeDetectorAdapter: public DetectionBasedTracker::IDetector
|
||||
{
|
||||
public:
|
||||
CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector):
|
||||
IDetector(),
|
||||
Detector(detector)
|
||||
{
|
||||
CV_Assert(detector);
|
||||
}
|
||||
|
||||
void detect(const cv::Mat &Image, std::vector<cv::Rect> &objects) CV_OVERRIDE
|
||||
{
|
||||
Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours, 0, minObjSize, maxObjSize);
|
||||
}
|
||||
|
||||
virtual ~CascadeDetectorAdapter() CV_OVERRIDE
|
||||
{}
|
||||
|
||||
private:
|
||||
CascadeDetectorAdapter();
|
||||
cv::Ptr<cv::CascadeClassifier> Detector;
|
||||
};
|
||||
|
||||
int main(int , char** )
|
||||
{
|
||||
namedWindow(WindowName);
|
||||
|
||||
VideoCapture VideoStream(0);
|
||||
|
||||
if (!VideoStream.isOpened())
|
||||
{
|
||||
printf("Error: Cannot open video stream from camera\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string cascadeFrontalfilename = samples::findFile("data/lbpcascades/lbpcascade_frontalface.xml");
|
||||
cv::Ptr<cv::CascadeClassifier> cascade = makePtr<cv::CascadeClassifier>(cascadeFrontalfilename);
|
||||
cv::Ptr<DetectionBasedTracker::IDetector> MainDetector = makePtr<CascadeDetectorAdapter>(cascade);
|
||||
if ( cascade->empty() )
|
||||
{
|
||||
printf("Error: Cannot load %s\n", cascadeFrontalfilename.c_str());
|
||||
return 2;
|
||||
}
|
||||
|
||||
cascade = makePtr<cv::CascadeClassifier>(cascadeFrontalfilename);
|
||||
cv::Ptr<DetectionBasedTracker::IDetector> TrackingDetector = makePtr<CascadeDetectorAdapter>(cascade);
|
||||
if ( cascade->empty() )
|
||||
{
|
||||
printf("Error: Cannot load %s\n", cascadeFrontalfilename.c_str());
|
||||
return 2;
|
||||
}
|
||||
|
||||
DetectionBasedTracker::Parameters params;
|
||||
DetectionBasedTracker Detector(MainDetector, TrackingDetector, params);
|
||||
|
||||
if (!Detector.run())
|
||||
{
|
||||
printf("Error: Detector initialization failed\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Mat ReferenceFrame;
|
||||
Mat GrayFrame;
|
||||
vector<Rect> Faces;
|
||||
|
||||
do
|
||||
{
|
||||
VideoStream >> ReferenceFrame;
|
||||
cvtColor(ReferenceFrame, GrayFrame, COLOR_BGR2GRAY);
|
||||
Detector.process(GrayFrame);
|
||||
Detector.getObjects(Faces);
|
||||
|
||||
for (size_t i = 0; i < Faces.size(); i++)
|
||||
{
|
||||
rectangle(ReferenceFrame, Faces[i], Scalar(0,255,0));
|
||||
}
|
||||
|
||||
imshow(WindowName, ReferenceFrame);
|
||||
} while (waitKey(30) < 0);
|
||||
|
||||
Detector.stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
int main()
|
||||
{
|
||||
printf("This sample works for UNIX or ANDROID or Visual Studio 2013+ only\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,257 +0,0 @@
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(const char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates the use of cv::CascadeClassifier class to detect objects (Face + eyes). You can use Haar or LBP features.\n"
|
||||
"This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n"
|
||||
"It's most known use is for faces.\n"
|
||||
"Usage:\n"
|
||||
<< argv[0]
|
||||
<< " [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]\n"
|
||||
" [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n"
|
||||
" [--scale=<image scale greater or equal to 1, try 1.3 for example>]\n"
|
||||
" [--try-flip]\n"
|
||||
" [filename|camera_index]\n\n"
|
||||
"example:\n"
|
||||
<< argv[0]
|
||||
<< " --cascade=\"data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"data/haarcascades/haarcascade_eye_tree_eyeglasses.xml\" --scale=1.3\n\n"
|
||||
"During execution:\n\tHit any key to quit.\n"
|
||||
"\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
|
||||
}
|
||||
|
||||
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
|
||||
CascadeClassifier& nestedCascade,
|
||||
double scale, bool tryflip );
|
||||
|
||||
string cascadeName;
|
||||
string nestedCascadeName;
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
VideoCapture capture;
|
||||
Mat frame, image;
|
||||
string inputName;
|
||||
bool tryflip;
|
||||
CascadeClassifier cascade, nestedCascade;
|
||||
double scale;
|
||||
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{help h||}"
|
||||
"{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
|
||||
"{nested-cascade|data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|}"
|
||||
"{scale|1|}{try-flip||}{@filename||}"
|
||||
);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
cascadeName = parser.get<string>("cascade");
|
||||
nestedCascadeName = parser.get<string>("nested-cascade");
|
||||
scale = parser.get<double>("scale");
|
||||
if (scale < 1)
|
||||
scale = 1;
|
||||
tryflip = parser.has("try-flip");
|
||||
inputName = parser.get<string>("@filename");
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 0;
|
||||
}
|
||||
if (!nestedCascade.load(samples::findFileOrKeep(nestedCascadeName)))
|
||||
cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
|
||||
if (!cascade.load(samples::findFile(cascadeName)))
|
||||
{
|
||||
cerr << "ERROR: Could not load classifier cascade" << endl;
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
|
||||
{
|
||||
int camera = inputName.empty() ? 0 : inputName[0] - '0';
|
||||
if(!capture.open(camera))
|
||||
{
|
||||
cout << "Capture from camera #" << camera << " didn't work" << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (!inputName.empty())
|
||||
{
|
||||
image = imread(samples::findFileOrKeep(inputName), IMREAD_COLOR);
|
||||
if (image.empty())
|
||||
{
|
||||
if (!capture.open(samples::findFileOrKeep(inputName)))
|
||||
{
|
||||
cout << "Could not read " << inputName << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image = imread(samples::findFile("lena.jpg"), IMREAD_COLOR);
|
||||
if (image.empty())
|
||||
{
|
||||
cout << "Couldn't read lena.jpg" << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( capture.isOpened() )
|
||||
{
|
||||
cout << "Video capturing has been started ..." << endl;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
capture >> frame;
|
||||
if( frame.empty() )
|
||||
break;
|
||||
|
||||
Mat frame1 = frame.clone();
|
||||
detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip );
|
||||
|
||||
char c = (char)waitKey(10);
|
||||
if( c == 27 || c == 'q' || c == 'Q' )
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Detecting face(s) in " << inputName << endl;
|
||||
if( !image.empty() )
|
||||
{
|
||||
detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
|
||||
waitKey(0);
|
||||
}
|
||||
else if( !inputName.empty() )
|
||||
{
|
||||
/* assume it is a text file containing the
|
||||
list of the image filenames to be processed - one per line */
|
||||
FILE* f = fopen( inputName.c_str(), "rt" );
|
||||
if( f )
|
||||
{
|
||||
char buf[1000+1];
|
||||
while( fgets( buf, 1000, f ) )
|
||||
{
|
||||
int len = (int)strlen(buf);
|
||||
while( len > 0 && isspace(buf[len-1]) )
|
||||
len--;
|
||||
buf[len] = '\0';
|
||||
cout << "file " << buf << endl;
|
||||
image = imread( buf, IMREAD_COLOR );
|
||||
if( !image.empty() )
|
||||
{
|
||||
detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
|
||||
char c = (char)waitKey(0);
|
||||
if( c == 27 || c == 'q' || c == 'Q' )
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Aw snap, couldn't read image " << buf << endl;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
|
||||
CascadeClassifier& nestedCascade,
|
||||
double scale, bool tryflip )
|
||||
{
|
||||
double t = 0;
|
||||
vector<Rect> faces, faces2;
|
||||
const static Scalar colors[] =
|
||||
{
|
||||
Scalar(255,0,0),
|
||||
Scalar(255,128,0),
|
||||
Scalar(255,255,0),
|
||||
Scalar(0,255,0),
|
||||
Scalar(0,128,255),
|
||||
Scalar(0,255,255),
|
||||
Scalar(0,0,255),
|
||||
Scalar(255,0,255)
|
||||
};
|
||||
Mat gray, smallImg;
|
||||
|
||||
cvtColor( img, gray, COLOR_BGR2GRAY );
|
||||
double fx = 1 / scale;
|
||||
resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
|
||||
equalizeHist( smallImg, smallImg );
|
||||
|
||||
t = (double)getTickCount();
|
||||
cascade.detectMultiScale( smallImg, faces,
|
||||
1.1, 2, 0
|
||||
//|CASCADE_FIND_BIGGEST_OBJECT
|
||||
//|CASCADE_DO_ROUGH_SEARCH
|
||||
|CASCADE_SCALE_IMAGE,
|
||||
Size(30, 30) );
|
||||
if( tryflip )
|
||||
{
|
||||
flip(smallImg, smallImg, 1);
|
||||
cascade.detectMultiScale( smallImg, faces2,
|
||||
1.1, 2, 0
|
||||
//|CASCADE_FIND_BIGGEST_OBJECT
|
||||
//|CASCADE_DO_ROUGH_SEARCH
|
||||
|CASCADE_SCALE_IMAGE,
|
||||
Size(30, 30) );
|
||||
for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
|
||||
{
|
||||
faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
|
||||
}
|
||||
}
|
||||
t = (double)getTickCount() - t;
|
||||
printf( "detection time = %g ms\n", t*1000/getTickFrequency());
|
||||
for ( size_t i = 0; i < faces.size(); i++ )
|
||||
{
|
||||
Rect r = faces[i];
|
||||
Mat smallImgROI;
|
||||
vector<Rect> nestedObjects;
|
||||
Point center;
|
||||
Scalar color = colors[i%8];
|
||||
int radius;
|
||||
|
||||
double aspect_ratio = (double)r.width/r.height;
|
||||
if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
|
||||
{
|
||||
center.x = cvRound((r.x + r.width*0.5)*scale);
|
||||
center.y = cvRound((r.y + r.height*0.5)*scale);
|
||||
radius = cvRound((r.width + r.height)*0.25*scale);
|
||||
circle( img, center, radius, color, 3, 8, 0 );
|
||||
}
|
||||
else
|
||||
rectangle( img, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
|
||||
Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
|
||||
color, 3, 8, 0);
|
||||
if( nestedCascade.empty() )
|
||||
continue;
|
||||
smallImgROI = smallImg( r );
|
||||
nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
|
||||
1.1, 2, 0
|
||||
//|CASCADE_FIND_BIGGEST_OBJECT
|
||||
//|CASCADE_DO_ROUGH_SEARCH
|
||||
//|CASCADE_DO_CANNY_PRUNING
|
||||
|CASCADE_SCALE_IMAGE,
|
||||
Size(30, 30) );
|
||||
for ( size_t j = 0; j < nestedObjects.size(); j++ )
|
||||
{
|
||||
Rect nr = nestedObjects[j];
|
||||
center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
|
||||
center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
|
||||
radius = cvRound((nr.width + nr.height)*0.25*scale);
|
||||
circle( img, center, radius, color, 3, 8, 0 );
|
||||
}
|
||||
}
|
||||
imshow( "result", img );
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
/*
|
||||
* Author: Samyak Datta (datta[dot]samyak[at]gmail.com)
|
||||
*
|
||||
* A program to detect facial feature points using
|
||||
* Haarcascade classifiers for face, eyes, nose and mouth
|
||||
*
|
||||
*/
|
||||
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// Functions for facial feature detection
|
||||
static void help(char** argv);
|
||||
static void detectFaces(Mat&, vector<Rect_<int> >&, string);
|
||||
static void detectEyes(Mat&, vector<Rect_<int> >&, string);
|
||||
static void detectNose(Mat&, vector<Rect_<int> >&, string);
|
||||
static void detectMouth(Mat&, vector<Rect_<int> >&, string);
|
||||
static void detectFacialFeaures(Mat&, const vector<Rect_<int> >, string, string, string);
|
||||
|
||||
string input_image_path;
|
||||
string face_cascade_path, eye_cascade_path, nose_cascade_path, mouth_cascade_path;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{eyes||}{nose||}{mouth||}{help h||}{@image||}{@facexml||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
input_image_path = parser.get<string>("@image");
|
||||
face_cascade_path = parser.get<string>("@facexml");
|
||||
eye_cascade_path = parser.has("eyes") ? parser.get<string>("eyes") : "";
|
||||
nose_cascade_path = parser.has("nose") ? parser.get<string>("nose") : "";
|
||||
mouth_cascade_path = parser.has("mouth") ? parser.get<string>("mouth") : "";
|
||||
if (input_image_path.empty() || face_cascade_path.empty())
|
||||
{
|
||||
cout << "IMAGE or FACE_CASCADE are not specified";
|
||||
return 1;
|
||||
}
|
||||
// Load image and cascade classifier files
|
||||
Mat image;
|
||||
image = imread(samples::findFile(input_image_path));
|
||||
|
||||
// Detect faces and facial features
|
||||
vector<Rect_<int> > faces;
|
||||
detectFaces(image, faces, face_cascade_path);
|
||||
detectFacialFeaures(image, faces, eye_cascade_path, nose_cascade_path, mouth_cascade_path);
|
||||
|
||||
imshow("Result", image);
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis file demonstrates facial feature points detection using Haarcascade classifiers.\n"
|
||||
"The program detects a face and eyes, nose and mouth inside the face."
|
||||
"The code has been tested on the Japanese Female Facial Expression (JAFFE) database and found"
|
||||
"to give reasonably accurate results. \n";
|
||||
|
||||
cout << "\nUSAGE: " << argv[0] << " [IMAGE] [FACE_CASCADE] [OPTIONS]\n"
|
||||
"IMAGE\n\tPath to the image of a face taken as input.\n"
|
||||
"FACE_CASCSDE\n\t Path to a haarcascade classifier for face detection.\n"
|
||||
"OPTIONS: \nThere are 3 options available which are described in detail. There must be a "
|
||||
"space between the option and it's argument (All three options accept arguments).\n"
|
||||
"\t-eyes=<eyes_cascade> : Specify the haarcascade classifier for eye detection.\n"
|
||||
"\t-nose=<nose_cascade> : Specify the haarcascade classifier for nose detection.\n"
|
||||
"\t-mouth=<mouth-cascade> : Specify the haarcascade classifier for mouth detection.\n";
|
||||
|
||||
|
||||
cout << "EXAMPLE:\n"
|
||||
"(1) " << argv[0] << " image.jpg face.xml -eyes=eyes.xml -mouth=mouth.xml\n"
|
||||
"\tThis will detect the face, eyes and mouth in image.jpg.\n"
|
||||
"(2) " << argv[0] << " image.jpg face.xml -nose=nose.xml\n"
|
||||
"\tThis will detect the face and nose in image.jpg.\n"
|
||||
"(3) " << argv[0] << " image.jpg face.xml\n"
|
||||
"\tThis will detect only the face in image.jpg.\n";
|
||||
|
||||
cout << " \n\nThe classifiers for face and eyes can be downloaded from : "
|
||||
" \nhttps://github.com/opencv/opencv/tree/5.x/data/haarcascades";
|
||||
|
||||
cout << "\n\nThe classifiers for nose and mouth can be downloaded from : "
|
||||
" \nhttps://github.com/opencv/opencv_contrib/tree/5.x/modules/face/data/cascades\n";
|
||||
}
|
||||
|
||||
static void detectFaces(Mat& img, vector<Rect_<int> >& faces, string cascade_path)
|
||||
{
|
||||
CascadeClassifier face_cascade;
|
||||
face_cascade.load(samples::findFile(cascade_path));
|
||||
|
||||
if (!face_cascade.empty())
|
||||
face_cascade.detectMultiScale(img, faces, 1.15, 3, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
return;
|
||||
}
|
||||
|
||||
static void detectFacialFeaures(Mat& img, const vector<Rect_<int> > faces, string eye_cascade,
|
||||
string nose_cascade, string mouth_cascade)
|
||||
{
|
||||
for(unsigned int i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
// Mark the bounding box enclosing the face
|
||||
Rect face = faces[i];
|
||||
rectangle(img, Point(face.x, face.y), Point(face.x+face.width, face.y+face.height),
|
||||
Scalar(255, 0, 0), 1, 4);
|
||||
|
||||
// Eyes, nose and mouth will be detected inside the face (region of interest)
|
||||
Mat ROI = img(Rect(face.x, face.y, face.width, face.height));
|
||||
|
||||
// Check if all features (eyes, nose and mouth) are being detected
|
||||
bool is_full_detection = false;
|
||||
if( (!eye_cascade.empty()) && (!nose_cascade.empty()) && (!mouth_cascade.empty()) )
|
||||
is_full_detection = true;
|
||||
|
||||
// Detect eyes if classifier provided by the user
|
||||
if(!eye_cascade.empty())
|
||||
{
|
||||
vector<Rect_<int> > eyes;
|
||||
detectEyes(ROI, eyes, eye_cascade);
|
||||
|
||||
// Mark points corresponding to the centre of the eyes
|
||||
for(unsigned int j = 0; j < eyes.size(); ++j)
|
||||
{
|
||||
Rect e = eyes[j];
|
||||
circle(ROI, Point(e.x+e.width/2, e.y+e.height/2), 3, Scalar(0, 255, 0), -1, 8);
|
||||
/* rectangle(ROI, Point(e.x, e.y), Point(e.x+e.width, e.y+e.height),
|
||||
Scalar(0, 255, 0), 1, 4); */
|
||||
}
|
||||
}
|
||||
|
||||
// Detect nose if classifier provided by the user
|
||||
double nose_center_height = 0.0;
|
||||
if(!nose_cascade.empty())
|
||||
{
|
||||
vector<Rect_<int> > nose;
|
||||
detectNose(ROI, nose, nose_cascade);
|
||||
|
||||
// Mark points corresponding to the centre (tip) of the nose
|
||||
for(unsigned int j = 0; j < nose.size(); ++j)
|
||||
{
|
||||
Rect n = nose[j];
|
||||
circle(ROI, Point(n.x+n.width/2, n.y+n.height/2), 3, Scalar(0, 255, 0), -1, 8);
|
||||
nose_center_height = (n.y + n.height/2);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect mouth if classifier provided by the user
|
||||
double mouth_center_height = 0.0;
|
||||
if(!mouth_cascade.empty())
|
||||
{
|
||||
vector<Rect_<int> > mouth;
|
||||
detectMouth(ROI, mouth, mouth_cascade);
|
||||
|
||||
for(unsigned int j = 0; j < mouth.size(); ++j)
|
||||
{
|
||||
Rect m = mouth[j];
|
||||
mouth_center_height = (m.y + m.height/2);
|
||||
|
||||
// The mouth should lie below the nose
|
||||
if( (is_full_detection) && (mouth_center_height > nose_center_height) )
|
||||
{
|
||||
rectangle(ROI, Point(m.x, m.y), Point(m.x+m.width, m.y+m.height), Scalar(0, 255, 0), 1, 4);
|
||||
}
|
||||
else if( (is_full_detection) && (mouth_center_height <= nose_center_height) )
|
||||
continue;
|
||||
else
|
||||
rectangle(ROI, Point(m.x, m.y), Point(m.x+m.width, m.y+m.height), Scalar(0, 255, 0), 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void detectEyes(Mat& img, vector<Rect_<int> >& eyes, string cascade_path)
|
||||
{
|
||||
CascadeClassifier eyes_cascade;
|
||||
eyes_cascade.load(samples::findFile(cascade_path, !cascade_path.empty()));
|
||||
|
||||
if (!eyes_cascade.empty())
|
||||
eyes_cascade.detectMultiScale(img, eyes, 1.20, 5, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
return;
|
||||
}
|
||||
|
||||
static void detectNose(Mat& img, vector<Rect_<int> >& nose, string cascade_path)
|
||||
{
|
||||
CascadeClassifier nose_cascade;
|
||||
nose_cascade.load(samples::findFile(cascade_path, !cascade_path.empty()));
|
||||
|
||||
if (!nose_cascade.empty())
|
||||
nose_cascade.detectMultiScale(img, nose, 1.20, 5, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
return;
|
||||
}
|
||||
|
||||
static void detectMouth(Mat& img, vector<Rect_<int> >& mouth, string cascade_path)
|
||||
{
|
||||
CascadeClassifier mouth_cascade;
|
||||
mouth_cascade.load(samples::findFile(cascade_path, !cascade_path.empty()));
|
||||
|
||||
if (!mouth_cascade.empty())
|
||||
mouth_cascade.detectMultiScale(img, mouth, 1.20, 5, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
|
||||
return;
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include <opencv2/objdetect.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
class Detector
|
||||
{
|
||||
enum Mode { Default, Daimler } m;
|
||||
HOGDescriptor hog, hog_d;
|
||||
public:
|
||||
Detector() : m(Default), hog(), hog_d(Size(48, 96), Size(16, 16), Size(8, 8), Size(8, 8), 9)
|
||||
{
|
||||
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
|
||||
hog_d.setSVMDetector(HOGDescriptor::getDaimlerPeopleDetector());
|
||||
}
|
||||
void toggleMode() { m = (m == Default ? Daimler : Default); }
|
||||
string modeName() const { return (m == Default ? "Default" : "Daimler"); }
|
||||
vector<Rect> detect(InputArray img)
|
||||
{
|
||||
// Run the detector with default parameters. to get a higher hit-rate
|
||||
// (and more false alarms, respectively), decrease the hitThreshold and
|
||||
// groupThreshold (set groupThreshold to 0 to turn off the grouping completely).
|
||||
vector<Rect> found;
|
||||
if (m == Default)
|
||||
hog.detectMultiScale(img, found, 0, Size(8,8), Size(), 1.05, 2, false);
|
||||
else if (m == Daimler)
|
||||
hog_d.detectMultiScale(img, found, 0, Size(8,8), Size(), 1.05, 2, true);
|
||||
return found;
|
||||
}
|
||||
void adjustRect(Rect & r) const
|
||||
{
|
||||
// The HOG detector returns slightly larger rectangles than the real objects,
|
||||
// so we slightly shrink the rectangles to get a nicer output.
|
||||
r.x += cvRound(r.width*0.1);
|
||||
r.width = cvRound(r.width*0.8);
|
||||
r.y += cvRound(r.height*0.07);
|
||||
r.height = cvRound(r.height*0.8);
|
||||
}
|
||||
};
|
||||
|
||||
static const string keys = "{ help h | | print help message }"
|
||||
"{ camera c | 0 | capture video from camera (device index starting from 0) }"
|
||||
"{ video v | | use video as input }";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("This sample demonstrates the use of the HoG descriptor.");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
int camera = parser.get<int>("camera");
|
||||
string file = parser.get<string>("video");
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
|
||||
VideoCapture cap;
|
||||
if (file.empty())
|
||||
cap.open(camera);
|
||||
else
|
||||
{
|
||||
file = samples::findFileOrKeep(file);
|
||||
cap.open(file);
|
||||
}
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cout << "Can not open video stream: '" << (file.empty() ? "<camera>" : file) << "'" << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
cout << "Press 'q' or <ESC> to quit." << endl;
|
||||
cout << "Press <space> to toggle between Default and Daimler detector" << endl;
|
||||
Detector detector;
|
||||
Mat frame;
|
||||
for (;;)
|
||||
{
|
||||
cap >> frame;
|
||||
if (frame.empty())
|
||||
{
|
||||
cout << "Finished reading: empty frame" << endl;
|
||||
break;
|
||||
}
|
||||
int64 t = getTickCount();
|
||||
vector<Rect> found = detector.detect(frame);
|
||||
t = getTickCount() - t;
|
||||
|
||||
// show the window
|
||||
{
|
||||
ostringstream buf;
|
||||
buf << "Mode: " << detector.modeName() << " ||| "
|
||||
<< "FPS: " << fixed << setprecision(1) << (getTickFrequency() / (double)t);
|
||||
putText(frame, buf.str(), Point(10, 30), FONT_HERSHEY_PLAIN, 2.0, Scalar(0, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
for (vector<Rect>::iterator i = found.begin(); i != found.end(); ++i)
|
||||
{
|
||||
Rect &r = *i;
|
||||
detector.adjustRect(r);
|
||||
rectangle(frame, r.tl(), r.br(), cv::Scalar(0, 255, 0), 2);
|
||||
}
|
||||
imshow("People detector", frame);
|
||||
|
||||
// interact with user
|
||||
const char key = (char)waitKey(1);
|
||||
if (key == 27 || key == 'q') // ESC
|
||||
{
|
||||
cout << "Exit requested" << endl;
|
||||
break;
|
||||
}
|
||||
else if (key == ' ')
|
||||
{
|
||||
detector.toggleMode();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(const char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates the smile detector.\n"
|
||||
"Usage:\n" <<
|
||||
argv[0] << " [--cascade=<cascade_path> this is the frontal face classifier]\n"
|
||||
" [--smile-cascade=[<smile_cascade_path>]]\n"
|
||||
" [--scale=<image scale greater or equal to 1, try 2.0 for example. The larger the faster the processing>]\n"
|
||||
" [--try-flip]\n"
|
||||
" [video_filename|camera_index]\n\n"
|
||||
"Example:\n" <<
|
||||
argv[0] << " --cascade=\"data/haarcascades/haarcascade_frontalface_alt.xml\" --smile-cascade=\"data/haarcascades/haarcascade_smile.xml\" --scale=2.0\n\n"
|
||||
"During execution:\n\tHit any key to quit.\n"
|
||||
"\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
|
||||
}
|
||||
|
||||
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
|
||||
CascadeClassifier& nestedCascade,
|
||||
double scale, bool tryflip );
|
||||
|
||||
string cascadeName;
|
||||
string nestedCascadeName;
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
VideoCapture capture;
|
||||
Mat frame, image;
|
||||
string inputName;
|
||||
bool tryflip;
|
||||
|
||||
help(argv);
|
||||
|
||||
CascadeClassifier cascade, nestedCascade;
|
||||
double scale;
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{help h||}{scale|1|}"
|
||||
"{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
|
||||
"{smile-cascade|data/haarcascades/haarcascade_smile.xml|}"
|
||||
"{try-flip||}{@input||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
cascadeName = samples::findFile(parser.get<string>("cascade"));
|
||||
nestedCascadeName = samples::findFile(parser.get<string>("smile-cascade"));
|
||||
tryflip = parser.has("try-flip");
|
||||
inputName = parser.get<string>("@input");
|
||||
scale = parser.get<int>("scale");
|
||||
if (!parser.check())
|
||||
{
|
||||
help(argv);
|
||||
return 1;
|
||||
}
|
||||
if (scale < 1)
|
||||
scale = 1;
|
||||
if( !cascade.load( cascadeName ) )
|
||||
{
|
||||
cerr << "ERROR: Could not load face cascade" << endl;
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
if( !nestedCascade.load( nestedCascadeName ) )
|
||||
{
|
||||
cerr << "ERROR: Could not load smile cascade" << endl;
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
|
||||
{
|
||||
int c = inputName.empty() ? 0 : inputName[0] - '0' ;
|
||||
if(!capture.open(c))
|
||||
cout << "Capture from camera #" << c << " didn't work" << endl;
|
||||
}
|
||||
else if( inputName.size() )
|
||||
{
|
||||
inputName = samples::findFileOrKeep(inputName);
|
||||
if(!capture.open( inputName ))
|
||||
cout << "Could not read " << inputName << endl;
|
||||
}
|
||||
|
||||
if( capture.isOpened() )
|
||||
{
|
||||
cout << "Video capturing has been started ..." << endl;
|
||||
cout << endl << "NOTE: Smile intensity will only be valid after a first smile has been detected" << endl;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
capture >> frame;
|
||||
if( frame.empty() )
|
||||
break;
|
||||
|
||||
Mat frame1 = frame.clone();
|
||||
detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip );
|
||||
|
||||
char c = (char)waitKey(10);
|
||||
if( c == 27 || c == 'q' || c == 'Q' )
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "ERROR: Could not initiate capture" << endl;
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
|
||||
CascadeClassifier& nestedCascade,
|
||||
double scale, bool tryflip)
|
||||
{
|
||||
vector<Rect> faces, faces2;
|
||||
const static Scalar colors[] =
|
||||
{
|
||||
Scalar(255,0,0),
|
||||
Scalar(255,128,0),
|
||||
Scalar(255,255,0),
|
||||
Scalar(0,255,0),
|
||||
Scalar(0,128,255),
|
||||
Scalar(0,255,255),
|
||||
Scalar(0,0,255),
|
||||
Scalar(255,0,255)
|
||||
};
|
||||
Mat gray, smallImg;
|
||||
|
||||
cvtColor( img, gray, COLOR_BGR2GRAY );
|
||||
|
||||
double fx = 1 / scale;
|
||||
resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
|
||||
equalizeHist( smallImg, smallImg );
|
||||
|
||||
cascade.detectMultiScale( smallImg, faces,
|
||||
1.1, 2, 0
|
||||
//|CASCADE_FIND_BIGGEST_OBJECT
|
||||
//|CASCADE_DO_ROUGH_SEARCH
|
||||
|CASCADE_SCALE_IMAGE,
|
||||
Size(30, 30) );
|
||||
if( tryflip )
|
||||
{
|
||||
flip(smallImg, smallImg, 1);
|
||||
cascade.detectMultiScale( smallImg, faces2,
|
||||
1.1, 2, 0
|
||||
//|CASCADE_FIND_BIGGEST_OBJECT
|
||||
//|CASCADE_DO_ROUGH_SEARCH
|
||||
|CASCADE_SCALE_IMAGE,
|
||||
Size(30, 30) );
|
||||
for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
|
||||
{
|
||||
faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
|
||||
}
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < faces.size(); i++ )
|
||||
{
|
||||
Rect r = faces[i];
|
||||
Mat smallImgROI;
|
||||
vector<Rect> nestedObjects;
|
||||
Point center;
|
||||
Scalar color = colors[i%8];
|
||||
int radius;
|
||||
|
||||
double aspect_ratio = (double)r.width/r.height;
|
||||
if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
|
||||
{
|
||||
center.x = cvRound((r.x + r.width*0.5)*scale);
|
||||
center.y = cvRound((r.y + r.height*0.5)*scale);
|
||||
radius = cvRound((r.width + r.height)*0.25*scale);
|
||||
circle( img, center, radius, color, 3, 8, 0 );
|
||||
}
|
||||
else
|
||||
rectangle( img, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
|
||||
Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
|
||||
color, 3, 8, 0);
|
||||
|
||||
const int half_height=cvRound((float)r.height/2);
|
||||
r.y=r.y + half_height;
|
||||
r.height = half_height-1;
|
||||
smallImgROI = smallImg( r );
|
||||
nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
|
||||
1.1, 0, 0
|
||||
//|CASCADE_FIND_BIGGEST_OBJECT
|
||||
//|CASCADE_DO_ROUGH_SEARCH
|
||||
//|CASCADE_DO_CANNY_PRUNING
|
||||
|CASCADE_SCALE_IMAGE,
|
||||
Size(30, 30) );
|
||||
|
||||
// The number of detected neighbors depends on image size (and also illumination, etc.). The
|
||||
// following steps use a floating minimum and maximum of neighbors. Intensity thus estimated will be
|
||||
//accurate only after a first smile has been displayed by the user.
|
||||
const int smile_neighbors = (int)nestedObjects.size();
|
||||
static int max_neighbors=-1;
|
||||
static int min_neighbors=-1;
|
||||
if (min_neighbors == -1) min_neighbors = smile_neighbors;
|
||||
max_neighbors = MAX(max_neighbors, smile_neighbors);
|
||||
|
||||
// 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, Point(0, img.rows), Point(img.cols/10, img.rows - rect_height), col, -1);
|
||||
}
|
||||
|
||||
imshow( "result", img );
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/** Function Headers */
|
||||
void detectAndDisplay( Mat frame );
|
||||
|
||||
/** Global variables */
|
||||
CascadeClassifier face_cascade;
|
||||
CascadeClassifier eyes_cascade;
|
||||
|
||||
/** @function main */
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{help h||}"
|
||||
"{face_cascade|data/haarcascades/haarcascade_frontalface_alt.xml|Path to face cascade.}"
|
||||
"{eyes_cascade|data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|Path to eyes cascade.}"
|
||||
"{camera|0|Camera device number.}");
|
||||
|
||||
parser.about( "\nThis program demonstrates using the cv::CascadeClassifier class to detect objects (Face + eyes) in a video stream.\n"
|
||||
"You can use Haar or LBP features.\n\n" );
|
||||
parser.printMessage();
|
||||
|
||||
String face_cascade_name = samples::findFile( parser.get<String>("face_cascade") );
|
||||
String eyes_cascade_name = samples::findFile( parser.get<String>("eyes_cascade") );
|
||||
|
||||
//-- 1. Load the cascades
|
||||
if( !face_cascade.load( face_cascade_name ) )
|
||||
{
|
||||
cout << "--(!)Error loading face cascade\n";
|
||||
return -1;
|
||||
};
|
||||
if( !eyes_cascade.load( eyes_cascade_name ) )
|
||||
{
|
||||
cout << "--(!)Error loading eyes cascade\n";
|
||||
return -1;
|
||||
};
|
||||
|
||||
int camera_device = parser.get<int>("camera");
|
||||
VideoCapture capture;
|
||||
//-- 2. Read the video stream
|
||||
capture.open( camera_device );
|
||||
if ( ! capture.isOpened() )
|
||||
{
|
||||
cout << "--(!)Error opening video capture\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat frame;
|
||||
while ( capture.read(frame) )
|
||||
{
|
||||
if( frame.empty() )
|
||||
{
|
||||
cout << "--(!) No captured frame -- Break!\n";
|
||||
break;
|
||||
}
|
||||
|
||||
//-- 3. Apply the classifier to the frame
|
||||
detectAndDisplay( frame );
|
||||
|
||||
if( waitKey(10) == 27 )
|
||||
{
|
||||
break; // escape
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @function detectAndDisplay */
|
||||
void detectAndDisplay( Mat frame )
|
||||
{
|
||||
Mat frame_gray;
|
||||
cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
|
||||
equalizeHist( frame_gray, frame_gray );
|
||||
|
||||
//-- Detect faces
|
||||
std::vector<Rect> faces;
|
||||
face_cascade.detectMultiScale( frame_gray, faces );
|
||||
|
||||
for ( size_t i = 0; i < faces.size(); i++ )
|
||||
{
|
||||
Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
|
||||
ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4 );
|
||||
|
||||
Mat faceROI = frame_gray( faces[i] );
|
||||
|
||||
//-- In each face, detect eyes
|
||||
std::vector<Rect> eyes;
|
||||
eyes_cascade.detectMultiScale( faceROI, eyes );
|
||||
|
||||
for ( size_t j = 0; j < eyes.size(); j++ )
|
||||
{
|
||||
Point eye_center( faces[i].x + eyes[j].x + eyes[j].width/2, faces[i].y + eyes[j].y + eyes[j].height/2 );
|
||||
int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
|
||||
circle( frame, eye_center, radius, Scalar( 255, 0, 0 ), 4 );
|
||||
}
|
||||
}
|
||||
|
||||
//-- Show what you got
|
||||
imshow( "Capture - Face detection", frame );
|
||||
}
|
@ -1,316 +0,0 @@
|
||||
// WARNING: this sample is under construction! Use it on your own risk.
|
||||
#if defined _MSC_VER && _MSC_VER >= 1400
|
||||
#pragma warning(disable : 4100)
|
||||
#endif
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/cudaobjdetect.hpp"
|
||||
#include "opencv2/cudaimgproc.hpp"
|
||||
#include "opencv2/cudawarping.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
|
||||
static void help()
|
||||
{
|
||||
cout << "Usage: ./cascadeclassifier \n\t--cascade <cascade_file>\n\t(<image>|--video <video>|--camera <camera_id>)\n"
|
||||
"Using OpenCV version " << CV_VERSION << endl << endl;
|
||||
}
|
||||
|
||||
|
||||
static void convertAndResize(const Mat& src, Mat& gray, Mat& resized, double scale)
|
||||
{
|
||||
if (src.channels() == 3)
|
||||
{
|
||||
cv::cvtColor( src, gray, COLOR_BGR2GRAY );
|
||||
}
|
||||
else
|
||||
{
|
||||
gray = src;
|
||||
}
|
||||
|
||||
Size sz(cvRound(gray.cols * scale), cvRound(gray.rows * scale));
|
||||
|
||||
if (scale != 1)
|
||||
{
|
||||
cv::resize(gray, resized, sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
resized = gray;
|
||||
}
|
||||
}
|
||||
|
||||
static void convertAndResize(const GpuMat& src, GpuMat& gray, GpuMat& resized, double scale)
|
||||
{
|
||||
if (src.channels() == 3)
|
||||
{
|
||||
cv::cuda::cvtColor( src, gray, COLOR_BGR2GRAY );
|
||||
}
|
||||
else
|
||||
{
|
||||
gray = src;
|
||||
}
|
||||
|
||||
Size sz(cvRound(gray.cols * scale), cvRound(gray.rows * scale));
|
||||
|
||||
if (scale != 1)
|
||||
{
|
||||
cv::cuda::resize(gray, resized, sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
resized = gray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void matPrint(Mat &img, int lineOffsY, Scalar fontColor, const string &ss)
|
||||
{
|
||||
int fontFace = FONT_HERSHEY_DUPLEX;
|
||||
double fontScale = 0.8;
|
||||
int fontThickness = 2;
|
||||
Size fontSize = cv::getTextSize("T[]", fontFace, fontScale, fontThickness, 0);
|
||||
|
||||
Point org;
|
||||
org.x = 1;
|
||||
org.y = 3 * fontSize.height * (lineOffsY + 1) / 2;
|
||||
putText(img, ss, org, fontFace, fontScale, Scalar(0,0,0), 5*fontThickness/2, 16);
|
||||
putText(img, ss, org, fontFace, fontScale, fontColor, fontThickness, 16);
|
||||
}
|
||||
|
||||
|
||||
static void displayState(Mat &canvas, bool bHelp, bool bGpu, bool bLargestFace, bool bFilter, double fps)
|
||||
{
|
||||
Scalar fontColorRed = Scalar(255,0,0);
|
||||
Scalar fontColorNV = Scalar(118,185,0);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "FPS = " << setprecision(1) << fixed << fps;
|
||||
matPrint(canvas, 0, fontColorRed, ss.str());
|
||||
ss.str("");
|
||||
ss << "[" << canvas.cols << "x" << canvas.rows << "], " <<
|
||||
(bGpu ? "GPU, " : "CPU, ") <<
|
||||
(bLargestFace ? "OneFace, " : "MultiFace, ") <<
|
||||
(bFilter ? "Filter:ON" : "Filter:OFF");
|
||||
matPrint(canvas, 1, fontColorRed, ss.str());
|
||||
|
||||
// by Anatoly. MacOS fix. ostringstream(const string&) is a private
|
||||
// matPrint(canvas, 2, fontColorNV, ostringstream("Space - switch GPU / CPU"));
|
||||
if (bHelp)
|
||||
{
|
||||
matPrint(canvas, 2, fontColorNV, "Space - switch GPU / CPU");
|
||||
matPrint(canvas, 3, fontColorNV, "M - switch OneFace / MultiFace");
|
||||
matPrint(canvas, 4, fontColorNV, "F - toggle rectangles Filter");
|
||||
matPrint(canvas, 5, fontColorNV, "H - toggle hotkeys help");
|
||||
matPrint(canvas, 6, fontColorNV, "1/Q - increase/decrease scale");
|
||||
}
|
||||
else
|
||||
{
|
||||
matPrint(canvas, 2, fontColorNV, "H - toggle hotkeys help");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (getCudaEnabledDeviceCount() == 0)
|
||||
{
|
||||
return cerr << "No GPU found or the library is compiled without CUDA support" << endl, -1;
|
||||
}
|
||||
|
||||
cv::cuda::printShortCudaDeviceInfo(cv::cuda::getDevice());
|
||||
|
||||
string cascadeName;
|
||||
string inputName;
|
||||
bool isInputImage = false;
|
||||
bool isInputVideo = false;
|
||||
bool isInputCamera = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (string(argv[i]) == "--cascade")
|
||||
cascadeName = argv[++i];
|
||||
else if (string(argv[i]) == "--video")
|
||||
{
|
||||
inputName = argv[++i];
|
||||
isInputVideo = true;
|
||||
}
|
||||
else if (string(argv[i]) == "--camera")
|
||||
{
|
||||
inputName = argv[++i];
|
||||
isInputCamera = true;
|
||||
}
|
||||
else if (string(argv[i]) == "--help")
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
else if (!isInputImage)
|
||||
{
|
||||
inputName = argv[i];
|
||||
isInputImage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Unknown key: " << argv[i] << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<cuda::CascadeClassifier> cascade_gpu = cuda::CascadeClassifier::create(cascadeName);
|
||||
|
||||
cv::CascadeClassifier cascade_cpu;
|
||||
if (!cascade_cpu.load(cascadeName))
|
||||
{
|
||||
return cerr << "ERROR: Could not load cascade classifier \"" << cascadeName << "\"" << endl, help(), -1;
|
||||
}
|
||||
|
||||
VideoCapture capture;
|
||||
Mat image;
|
||||
|
||||
if (isInputImage)
|
||||
{
|
||||
image = imread(inputName);
|
||||
CV_Assert(!image.empty());
|
||||
}
|
||||
else if (isInputVideo)
|
||||
{
|
||||
capture.open(inputName);
|
||||
CV_Assert(capture.isOpened());
|
||||
}
|
||||
else
|
||||
{
|
||||
capture.open(atoi(inputName.c_str()));
|
||||
CV_Assert(capture.isOpened());
|
||||
}
|
||||
|
||||
namedWindow("result", 1);
|
||||
|
||||
Mat frame, frame_cpu, gray_cpu, resized_cpu, frameDisp;
|
||||
vector<Rect> faces;
|
||||
|
||||
GpuMat frame_gpu, gray_gpu, resized_gpu, facesBuf_gpu;
|
||||
|
||||
/* parameters */
|
||||
bool useGPU = true;
|
||||
double scaleFactor = 1.0;
|
||||
bool findLargestObject = false;
|
||||
bool filterRects = true;
|
||||
bool helpScreen = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (isInputCamera || isInputVideo)
|
||||
{
|
||||
capture >> frame;
|
||||
if (frame.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(image.empty() ? frame : image).copyTo(frame_cpu);
|
||||
frame_gpu.upload(image.empty() ? frame : image);
|
||||
|
||||
convertAndResize(frame_gpu, gray_gpu, resized_gpu, scaleFactor);
|
||||
convertAndResize(frame_cpu, gray_cpu, resized_cpu, scaleFactor);
|
||||
|
||||
TickMeter tm;
|
||||
tm.start();
|
||||
|
||||
if (useGPU)
|
||||
{
|
||||
cascade_gpu->setFindLargestObject(findLargestObject);
|
||||
cascade_gpu->setScaleFactor(1.2);
|
||||
cascade_gpu->setMinNeighbors((filterRects || findLargestObject) ? 4 : 0);
|
||||
|
||||
cascade_gpu->detectMultiScale(resized_gpu, facesBuf_gpu);
|
||||
cascade_gpu->convert(facesBuf_gpu, faces);
|
||||
}
|
||||
else
|
||||
{
|
||||
Size minSize = cascade_gpu->getClassifierSize();
|
||||
cascade_cpu.detectMultiScale(resized_cpu, faces, 1.2,
|
||||
(filterRects || findLargestObject) ? 4 : 0,
|
||||
(findLargestObject ? CASCADE_FIND_BIGGEST_OBJECT : 0)
|
||||
| CASCADE_SCALE_IMAGE,
|
||||
minSize);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
rectangle(resized_cpu, faces[i], Scalar(255));
|
||||
}
|
||||
|
||||
tm.stop();
|
||||
double detectionTime = tm.getTimeMilli();
|
||||
double fps = 1000 / detectionTime;
|
||||
|
||||
//print detections to console
|
||||
cout << setfill(' ') << setprecision(2);
|
||||
cout << setw(6) << fixed << fps << " FPS, " << faces.size() << " det";
|
||||
if ((filterRects || findLargestObject) && !faces.empty())
|
||||
{
|
||||
for (size_t i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
cout << ", [" << setw(4) << faces[i].x
|
||||
<< ", " << setw(4) << faces[i].y
|
||||
<< ", " << setw(4) << faces[i].width
|
||||
<< ", " << setw(4) << faces[i].height << "]";
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
cv::cvtColor(resized_cpu, frameDisp, COLOR_GRAY2BGR);
|
||||
displayState(frameDisp, helpScreen, useGPU, findLargestObject, filterRects, fps);
|
||||
imshow("result", frameDisp);
|
||||
|
||||
char key = (char)waitKey(5);
|
||||
if (key == 27)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case ' ':
|
||||
useGPU = !useGPU;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
findLargestObject = !findLargestObject;
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
filterRects = !filterRects;
|
||||
break;
|
||||
case '1':
|
||||
scaleFactor *= 1.05;
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
scaleFactor /= 1.05;
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
helpScreen = !helpScreen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|