From d06d7e291841d92fb2c9249bc251348a89bbda9c Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Mon, 3 Aug 2015 17:14:28 +0300 Subject: [PATCH] Change of interface and multiple fixes --- modules/adas/CMakeLists.txt | 8 - modules/adas/README.md | 2 - modules/adas/include/opencv2/adas.hpp | 44 -- modules/adas/src/adas.cpp | 0 modules/adas/tools/CMakeLists.txt | 2 - modules/adas/tools/fcw_detect/CMakeLists.txt | 35 - modules/adas/tools/fcw_detect/fcw_detect.cpp | 132 ---- modules/adas/tools/fcw_train/fcw_train.cpp | 187 ------ modules/xobjdetect/CMakeLists.txt | 3 +- .../xobjdetect/include/opencv2/xobjdetect.hpp | 196 +----- .../include/opencv2/xobjdetect/private.hpp | 88 --- modules/xobjdetect/src/acffeature.cpp | 344 ---------- modules/xobjdetect/src/cascadeclassifier.h | 44 ++ modules/xobjdetect/src/detector.cpp | 215 ++++++ modules/xobjdetect/src/features.cpp | 47 +- modules/xobjdetect/src/icfdetector.cpp | 316 --------- modules/xobjdetect/src/lbpfeatures.cpp | 48 +- modules/xobjdetect/src/lbpfeatures.h | 44 ++ modules/xobjdetect/src/main.cpp | 556 ---------------- modules/xobjdetect/src/precomp.hpp | 39 +- modules/xobjdetect/src/stump.cpp | 236 ------- .../xobjdetect/src/traincascade_features.h | 44 ++ modules/xobjdetect/src/waldboost.cpp | 623 +++++++++--------- modules/xobjdetect/src/waldboost.hpp | 88 +++ modules/xobjdetect/tools/CMakeLists.txt | 1 + .../tools/waldboost_detector}/CMakeLists.txt | 2 +- .../waldboost_detector/waldboost_detector.cpp | 33 + 27 files changed, 917 insertions(+), 2460 deletions(-) delete mode 100644 modules/adas/CMakeLists.txt delete mode 100644 modules/adas/README.md delete mode 100644 modules/adas/include/opencv2/adas.hpp delete mode 100644 modules/adas/src/adas.cpp delete mode 100644 modules/adas/tools/CMakeLists.txt delete mode 100644 modules/adas/tools/fcw_detect/CMakeLists.txt delete mode 100644 modules/adas/tools/fcw_detect/fcw_detect.cpp delete mode 100644 modules/adas/tools/fcw_train/fcw_train.cpp delete mode 100644 modules/xobjdetect/include/opencv2/xobjdetect/private.hpp delete mode 100644 modules/xobjdetect/src/acffeature.cpp create mode 100644 modules/xobjdetect/src/detector.cpp delete mode 100644 modules/xobjdetect/src/icfdetector.cpp delete mode 100644 modules/xobjdetect/src/main.cpp delete mode 100644 modules/xobjdetect/src/stump.cpp create mode 100644 modules/xobjdetect/src/waldboost.hpp create mode 100644 modules/xobjdetect/tools/CMakeLists.txt rename modules/{adas/tools/fcw_train => xobjdetect/tools/waldboost_detector}/CMakeLists.txt (97%) create mode 100644 modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp diff --git a/modules/adas/CMakeLists.txt b/modules/adas/CMakeLists.txt deleted file mode 100644 index fb122143a..000000000 --- a/modules/adas/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -if(IOS) - ocv_module_disable(adas) -endif() - -set(the_description "Automatic driver assistance algorithms") -ocv_define_module(adas opencv_xobjdetect) - -add_subdirectory(tools) diff --git a/modules/adas/README.md b/modules/adas/README.md deleted file mode 100644 index 7ccfb3a0f..000000000 --- a/modules/adas/README.md +++ /dev/null @@ -1,2 +0,0 @@ -ADAS: Advanced Driver Assistance Systems module with Forward Collision Warning -============================================================================== \ No newline at end of file diff --git a/modules/adas/include/opencv2/adas.hpp b/modules/adas/include/opencv2/adas.hpp deleted file mode 100644 index d41ca0054..000000000 --- a/modules/adas/include/opencv2/adas.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - -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 - (3-clause BSD License) - -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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -/** @defgroup adas Advanced Driver Assistance -*/ - diff --git a/modules/adas/src/adas.cpp b/modules/adas/src/adas.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/modules/adas/tools/CMakeLists.txt b/modules/adas/tools/CMakeLists.txt deleted file mode 100644 index 3cf5d7763..000000000 --- a/modules/adas/tools/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(fcw_train) -add_subdirectory(fcw_detect) diff --git a/modules/adas/tools/fcw_detect/CMakeLists.txt b/modules/adas/tools/fcw_detect/CMakeLists.txt deleted file mode 100644 index d2f2a2e11..000000000 --- a/modules/adas/tools/fcw_detect/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(name fcw_detect) -set(the_target opencv_${name}) - -set(OPENCV_${the_target}_DEPS opencv_core opencv_imgcodecs opencv_videoio - opencv_highgui opencv_xobjdetect) - -ocv_check_dependencies(${OPENCV_${the_target}_DEPS}) - -if(NOT OCV_DEPENDENCIES_FOUND) - return() -endif() - -project(${the_target}) - -ocv_include_directories("${OpenCV_SOURCE_DIR}/include/opencv") -ocv_include_modules_recurse(${OPENCV_${the_target}_DEPS}) - -file(GLOB ${the_target}_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - -add_executable(${the_target} ${${the_target}_SOURCES}) - -target_link_libraries(${the_target} ${OPENCV_${the_target}_DEPS}) - -set_target_properties(${the_target} PROPERTIES - DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" - ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} - RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib - OUTPUT_NAME ${the_target}) - -if(ENABLE_SOLUTION_FOLDERS) - set_target_properties(${the_target} PROPERTIES FOLDER "applications") -endif() - -install(TARGETS ${the_target} OPTIONAL RUNTIME DESTINATION bin COMPONENT main) diff --git a/modules/adas/tools/fcw_detect/fcw_detect.cpp b/modules/adas/tools/fcw_detect/fcw_detect.cpp deleted file mode 100644 index 318393a55..000000000 --- a/modules/adas/tools/fcw_detect/fcw_detect.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -using std::string; - -#include -using std::vector; - -#include -using std::cerr; -using std::endl; - -#include -using cv::Rect; -using cv::Size; -using cv::Mat; -using cv::Mat_; -using cv::Vec3b; - -#include -using cv::imread; -using cv::imwrite; - -#include -using cv::CommandLineParser; -using cv::FileStorage; - -#include -using cv::xobjdetect::ICFDetector; - -static Mat visualize(const Mat &image, const vector &objects) -{ - CV_Assert(image.type() == CV_8UC3); - Mat_ img = image.clone(); - for( size_t j = 0; j < objects.size(); ++j ) - { - Rect obj = objects[j]; - int x = obj.x; - int y = obj.y; - int width = obj.width; - int height = obj.height; - for( int i = y; i <= y + height; ++i ) { - img(i, x) = Vec3b(255, 0, 0); - img(i, x + width) = Vec3b(255, 0, 0); - } - for( int i = x; i <= x + width; ++i) { - img(y, i) = Vec3b(255, 0, 0); - img(y + height, i) = Vec3b(255, 0, 0); - } - } - return img; -} -static bool read_window_size(const char *str, int *rows, int *cols) -{ - int pos = 0; - if( sscanf(str, "%dx%d%n", rows, cols, &pos) != 2 || str[pos] != '\0' || - *rows <= 0 || *cols <= 0) - { - return false; - } - return true; -} - -int main(int argc, char *argv[]) -{ - const string keys = - "{help | | print this message}" - "{model_filename | model.xml | filename for reading model}" - "{image_path | test.png | path to image for detection}" - "{out_image_path | out.png | path to image for output}" - "{threshold | 0.0 | threshold for cascade}" - "{step | 8 | sliding window step}" - "{min_window_size | 40x40 | min window size in pixels}" - "{max_window_size | 300x300 | max window size in pixels}" - "{is_grayscale | false | read the image as grayscale}" - ; - - CommandLineParser parser(argc, argv, keys); - parser.about("FCW detection"); - - if( parser.has("help") || argc == 1) - { - parser.printMessage(); - return 0; - } - - string model_filename = parser.get("model_filename"); - string image_path = parser.get("image_path"); - string out_image_path = parser.get("out_image_path"); - bool is_grayscale = parser.get("is_grayscale"); - float threshold = parser.get("threshold"); - int step = parser.get("step"); - - int min_rows, min_cols, max_rows, max_cols; - string min_window_size = parser.get("min_window_size"); - if( !read_window_size(min_window_size.c_str(), &min_rows, - &min_cols) ) - { - cerr << "Error reading min window size from `" << min_window_size << "`" << endl; - return 1; - } - string max_window_size = parser.get("max_window_size"); - if( !read_window_size(max_window_size.c_str(), &max_rows, - &max_cols) ) - { - cerr << "Error reading max window size from `" << max_window_size << "`" << endl; - return 1; - } - - int color; - if(is_grayscale == false) - color = cv::IMREAD_COLOR; - else - color = cv::IMREAD_GRAYSCALE; - - - if( !parser.check() ) - { - parser.printErrors(); - return 1; - } - - ICFDetector detector; - FileStorage fs(model_filename, FileStorage::READ); - detector.read(fs["icfdetector"]); - fs.release(); - vector objects; - Mat img = imread(image_path, color); - std::vector values; - detector.detect(img, objects, 1.1f, Size(min_cols, min_rows), Size(max_cols, max_rows), threshold, step, values); - imwrite(out_image_path, visualize(img, objects)); - - -} diff --git a/modules/adas/tools/fcw_train/fcw_train.cpp b/modules/adas/tools/fcw_train/fcw_train.cpp deleted file mode 100644 index a7f0e81d0..000000000 --- a/modules/adas/tools/fcw_train/fcw_train.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include - -#include -using std::string; - -#include -using std::vector; - -#include -using std::ifstream; -using std::getline; - -#include -using std::stringstream; - -#include -using std::cerr; -using std::endl; - -#include -using cv::Rect; -using cv::Size; -#include -using cv::imread; -#include -using cv::CommandLineParser; -using cv::FileStorage; -#include - -#include // std::time -#include // std::rand, std::srand - -#include - - -using cv::xobjdetect::ICFDetectorParams; -using cv::xobjdetect::ICFDetector; -using cv::xobjdetect::WaldBoost; -using cv::xobjdetect::WaldBoostParams; -using cv::Mat; - -static bool read_model_size(const char *str, int *rows, int *cols) -{ - int pos = 0; - if( sscanf(str, "%dx%d%n", rows, cols, &pos) != 2 || str[pos] != '\0' || - *rows <= 0 || *cols <= 0) - { - return false; - } - return true; -} - -static int randomPred (int i) { return std::rand()%i;} - -int main(int argc, char *argv[]) -{ - - const string keys = - "{help | | print this message}" - "{pos_path | pos | path to training object samples}" - "{bg_path | bg | path to background images}" - "{bg_per_image | 5 | number of windows to sample per bg image}" - "{feature_count | 10000 | number of features to generate}" - "{weak_count | 100 | number of weak classifiers in cascade}" - "{model_size | 40x40 | model size in pixels}" - "{model_filename | model.xml | filename for saving model}" - "{features_type | icf | features type, \"icf\" or \"acf\"}" - "{alpha | 0.02 | alpha value}" - "{is_grayscale | false | read the image as grayscale}" - "{use_fast_log | false | use fast log function}" - "{limit_ps | -1 | limit to positive samples (-1 means all)}" - "{limit_bg | -1 | limit to negative samples (-1 means all)}" - ; - - - CommandLineParser parser(argc, argv, keys); - parser.about("FCW trainer"); - - if( parser.has("help") || argc == 1) - { - parser.printMessage(); - return 0; - } - - string pos_path = parser.get("pos_path"); - string bg_path = parser.get("bg_path"); - string model_filename = parser.get("model_filename"); - - ICFDetectorParams params; - params.feature_count = parser.get("feature_count"); - params.weak_count = parser.get("weak_count"); - params.bg_per_image = parser.get("bg_per_image"); - params.features_type = parser.get("features_type"); - params.alpha = parser.get("alpha"); - params.is_grayscale = parser.get("is_grayscale"); - params.use_fast_log = parser.get("use_fast_log"); - - int limit_ps = parser.get("limit_ps"); - int limit_bg = parser.get("limit_bg"); - - string model_size = parser.get("model_size"); - if( !read_model_size(model_size.c_str(), ¶ms.model_n_rows, - ¶ms.model_n_cols) ) - { - cerr << "Error reading model size from `" << model_size << "`" << endl; - return 1; - } - - if( params.feature_count <= 0 ) - { - cerr << "feature_count must be positive number" << endl; - return 1; - } - - if( params.weak_count <= 0 ) - { - cerr << "weak_count must be positive number" << endl; - return 1; - } - - if( params.features_type != "icf" && params.features_type != "acf" ) - { - cerr << "features_type must be \"icf\" or \"acf\"" << endl; - return 1; - } - if( params.alpha <= 0 ) - { - cerr << "alpha must be positive float number" << endl; - return 1; - } - if( !parser.check() ) - { - parser.printErrors(); - return 1; - } - - std::vector pos_filenames; - glob(pos_path, pos_filenames); - - std::vector bg_filenames; - glob(bg_path, bg_filenames); - - if(limit_ps != -1 && (int)pos_filenames.size() > limit_ps) - pos_filenames.erase(pos_filenames.begin()+limit_ps, pos_filenames.end()); - if(limit_bg != -1 && (int)bg_filenames.size() > limit_bg) - bg_filenames.erase(bg_filenames.begin()+limit_bg, bg_filenames.end()); - - //random pick input images - bool random_shuffle = false; - if(random_shuffle) - { - std::srand ( unsigned ( std::time(0) ) ); - std::random_shuffle ( pos_filenames.begin(), pos_filenames.end(), randomPred ); - std::random_shuffle ( bg_filenames.begin(), bg_filenames.end(), randomPred ); - } - - int samples_size = (int)((params.bg_per_image * bg_filenames.size()) + pos_filenames.size()); - int features_size = params.feature_count; - int max_features_allowed = (int)(INT_MAX/(sizeof(int)* samples_size)); - int max_samples_allowed = (int)(INT_MAX/(sizeof(int)* features_size)); - int total_samples = (int)((params.bg_per_image * bg_filenames.size()) + pos_filenames.size()); - - - if(total_samples >max_samples_allowed) - { - CV_Error_(1, ("exceeded maximum number of samples. Maximum number of samples with %d features is %d, you have %d (%d positive samples + (%d bg * %d bg_per_image))\n",features_size,max_samples_allowed,total_samples,pos_filenames.size(),bg_filenames.size(),params.bg_per_image )); - } - - if(params.feature_count >max_features_allowed) - { - CV_Error_(1, ("exceeded maximum number of features. Maximum number of features with %d samples is %d, you have %d\n",samples_size,max_features_allowed, features_size )); - } - - std::cout<& channels); - -/** @brief Feature evaluation interface - */ -class CV_EXPORTS FeatureEvaluator : public Algorithm -{ +class CV_EXPORTS WBDetector { public: - /** @brief Set channels for feature evaluation + WBDetector(const std::string& model_filename); - @param channels array of channels to be set - */ - virtual void setChannels(InputArrayOfArrays channels) = 0; + void train( + const std::string& pos_samples, + const std::string& neg_imgs); - /** @brief Set window position to sample features with shift. By default position is (0, 0). - - @param position position to be set - */ - virtual void setPosition(Size position) = 0; - - /** @brief Evaluate feature value with given index for current channels and window position. - - @param feature_ind index of feature to be evaluated - */ - virtual int evaluate(size_t feature_ind) const = 0; - - /** @brief Evaluate all features for current channels and window position. - - @param feature_values matrix-column of evaluated feature values - */ - virtual void evaluateAll(OutputArray feature_values) const = 0; - - virtual void assertChannels() = 0; -}; - -/** @brief Construct feature evaluator. - -@param features features for evaluation -@param type feature type. Can be "icf" or "acf" - */ -CV_EXPORTS Ptr -createFeatureEvaluator(const std::vector >& features, - const std::string& type); - -/** @brief Generate integral features. Returns vector of features. - -@param window_size size of window in which features should be evaluated -@param type feature type. Can be "icf" or "acf" -@param count number of features to generate. -@param channel_count number of feature channels - */ -std::vector > -generateFeatures(Size window_size, const std::string& type, - int count = INT_MAX, int channel_count = 10); - -//sort in-place of columns of the input matrix -void sort_columns_without_copy(Mat& m, Mat indices = Mat()); - -/** @brief Parameters for WaldBoost. weak_count — number of weak learners, alpha — cascade thresholding param. - */ -struct CV_EXPORTS WaldBoostParams -{ - int weak_count; - float alpha; - - WaldBoostParams(): weak_count(100), alpha(0.02f) - {} -}; - -/** @brief WaldBoost object detector from @cite Sochman05 . -*/ -class CV_EXPORTS WaldBoost : public Algorithm -{ -public: - /** @brief Train WaldBoost cascade for given data. - - Returns feature indices chosen for cascade. Feature enumeration starts from 0. - @param data matrix of feature values, size M x N, one feature per row - @param labels matrix of samples class labels, size 1 x N. Labels can be from {-1, +1} - @param use_fast_log - */ - virtual std::vector train(Mat& data, - const Mat& labels, bool use_fast_log=false) = 0; - - /** @brief Predict objects class given object that can compute object features. - - Returns unnormed confidence value — measure of confidence that object is from class +1. - @param feature_evaluator object that can compute features by demand - */ - virtual float predict( - const Ptr& feature_evaluator) const = 0; -}; - -/** @brief Construct WaldBoost object. - */ -CV_EXPORTS Ptr -createWaldBoost(const WaldBoostParams& params = WaldBoostParams()); - -/** @brief Params for ICFDetector training. - */ -struct CV_EXPORTS ICFDetectorParams -{ - int feature_count; - int weak_count; - int model_n_rows; - int model_n_cols; - int bg_per_image; - std::string features_type; - float alpha; - bool is_grayscale; - bool use_fast_log; - - ICFDetectorParams(): feature_count(UINT_MAX), weak_count(100), - model_n_rows(56), model_n_cols(56), bg_per_image(5), alpha(0.02f), is_grayscale(false), use_fast_log(false) - {} -}; - -/** @brief Integral Channel Features from @cite Dollar09 . -*/ -class CV_EXPORTS ICFDetector -{ -public: - - ICFDetector(): waldboost_(), features_(), ftype_() {} - - /** @brief Train detector. - - @param pos_filenames path to folder with images of objects (wildcards like /my/path/\*.png are allowed) - @param bg_filenames path to folder with background images - @param params parameters for detector training - */ - void train(const std::vector& pos_filenames, - const std::vector& bg_filenames, - ICFDetectorParams params = ICFDetectorParams()); - - /** @brief Detect objects on image. - @param image image for detection - @param objects output array of bounding boxes - @param scaleFactor scale between layers in detection pyramid - @param minSize min size of objects in pixels - @param maxSize max size of objects in pixels - @param threshold - @param slidingStep sliding window step - @param values output vector with values of positive samples - */ - void detect(const Mat& image, std::vector& objects, - float scaleFactor, Size minSize, Size maxSize, float threshold, int slidingStep, std::vector& values); - - /** @brief Detect objects on image. - @param img image for detection - @param objects output array of bounding boxes - @param minScaleFactor min factor by which the image will be resized - @param maxScaleFactor max factor by which the image will be resized - @param factorStep scaling factor is incremented each pyramid layer according to this parameter - @param threshold - @param slidingStep sliding window step - @param values output vector with values of positive samples - */ - void detect(const Mat& img, std::vector& objects, float minScaleFactor, float maxScaleFactor, float factorStep, float threshold, int slidingStep, std::vector& values); - - /** @brief Write detector to FileStorage. - @param fs FileStorage for output - */ - void write(FileStorage &fs) const; - - /** @brief Write ICFDetector to FileNode - @param node FileNode for reading - */ - void read(const FileNode &node); + void detect( + const Mat& img, + std::vector &bboxes, + std::vector &confidences); private: - Ptr waldboost_; - std::vector > features_; - int model_n_rows_; - int model_n_cols_; - std::string ftype_; + std::string model_filename_; }; -CV_EXPORTS void write(FileStorage& fs, String&, const ICFDetector& detector); - -CV_EXPORTS void read(const FileNode& node, ICFDetector& d, - const ICFDetector& default_value = ICFDetector()); - -//! @} - } /* namespace xobjdetect */ } /* namespace cv */ diff --git a/modules/xobjdetect/include/opencv2/xobjdetect/private.hpp b/modules/xobjdetect/include/opencv2/xobjdetect/private.hpp deleted file mode 100644 index 3873413db..000000000 --- a/modules/xobjdetect/include/opencv2/xobjdetect/private.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef __OPENCV_XOBJDETECT_PRIVATE_HPP__ -#define __OPENCV_XOBJDETECT_PRIVATE_HPP__ - -#ifndef __OPENCV_BUILD -# error this is a private header, do not include it outside OpenCV -#endif - -#include - -namespace cv -{ -namespace xobjdetect -{ - -class CV_EXPORTS Stump -{ -public: - - /* Initialize zero stump */ - Stump(): threshold_(0), polarity_(1), pos_value_(1), neg_value_(-1) {} - - /* Initialize stump with given threshold, polarity - and classification values */ - Stump(int threshold, int polarity, float pos_value, float neg_value): - threshold_(threshold), polarity_(polarity), - pos_value_(pos_value), neg_value_(neg_value) {} - - /* Train stump for given data - - data — matrix of feature values, size M x N, one feature per row - - labels — matrix of sample class labels, size 1 x N. Labels can be from - {-1, +1} - - weights — matrix of sample weights, size 1 x N - - visited_features: vector of already visited features (ignored in successive calls) - - Returns chosen feature index. Feature enumeration starts from 0 - */ - int train(const Mat& data, const Mat& labels, const Mat& weights, const std::vector& visited_features, bool use_fast_log = false); - - /* Predict object class given - - value — feature value. Feature must be the same as was chosen - during training stump - - Returns real value, sign(value) means class - */ - float predict(int value) const; - - /* Write stump in FileStorage */ - void write(FileStorage& fs) const - { - fs << "{" - << "threshold" << threshold_ - << "polarity" << polarity_ - << "pos_value" << pos_value_ - << "neg_value" << neg_value_ - << "}"; - } - - /* Read stump */ - void read(const FileNode& node) - { - threshold_ = (int)node["threshold"]; - polarity_ = (int)node["polarity"]; - pos_value_ = (float)node["pos_value"]; - neg_value_ = (float)node["neg_value"]; - } - -private: - /* Stump decision threshold */ - int threshold_; - /* Stump polarity, can be from {-1, +1} */ - int polarity_; - /* Classification values for positive and negative classes */ - float pos_value_, neg_value_; -}; - -void read(const FileNode& node, Stump& s, const Stump& default_value=Stump()); - -void write(FileStorage& fs, String&, const Stump& s); - -} /* namespace xobjdetect */ -} /* namespace cv */ - -#endif // __OPENCV_XOBJDETECT_PRIVATE_HPP__ diff --git a/modules/xobjdetect/src/acffeature.cpp b/modules/xobjdetect/src/acffeature.cpp deleted file mode 100644 index aac2601ef..000000000 --- a/modules/xobjdetect/src/acffeature.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - -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 - (3-clause BSD License) - -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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -#include "precomp.hpp" - -using std::vector; - -using std::min; - -#include -using std::cout; -using std::endl; - -namespace cv -{ -namespace xobjdetect -{ - -static bool isNull(const Mat_ &m) -{ - bool null_data = true; - for( int row = 0; row < m.rows; ++row ) - { - for( int col = 0; col < m.cols; ++col ) - if( m.at(row, col) ) - null_data = false; - } - return null_data; -} - -class FeatureEvaluatorImpl : public FeatureEvaluator -{ -public: - FeatureEvaluatorImpl(const vector >& features): - features_(features), channels_(), position_() - { - CV_Assert(features.size() > 0); - } - - virtual void assertChannels() - { - bool null_data = true; - for( size_t i = 0; i < channels_.size(); ++i ) - null_data &= isNull(channels_[i]); - CV_Assert(!null_data); - } - - virtual void evaluateAll(OutputArray feature_values) const - { - Mat_ feature_vals(1, (int)features_.size()); - for( int i = 0; i < (int)features_.size(); ++i ) - { - feature_vals(0, i) = evaluate(i); - } - feature_values.assign(feature_vals); - } - -protected: - /* Features to evaluate */ - vector > features_; - /* Channels for feature evaluation */ - std::vector channels_; - /* Channels window position */ - Size position_; -}; - -class ICFFeatureEvaluatorImpl : public FeatureEvaluatorImpl -{ -public: - ICFFeatureEvaluatorImpl(const vector >& features): - FeatureEvaluatorImpl(features) - { - } - - virtual void setChannels(InputArrayOfArrays channels); - virtual void setPosition(Size position); - virtual int evaluate(size_t feature_ind) const; -}; - -void ICFFeatureEvaluatorImpl::setChannels(InputArrayOfArrays channels) -{ - channels_.clear(); - vector ch; - channels.getMatVector(ch); - - for( size_t i = 0; i < ch.size(); ++i ) - { - const Mat &channel = ch[i]; - Mat integral_channel; - integral(channel, integral_channel, CV_32F); - integral_channel.convertTo(integral_channel, CV_32S); - channels_.push_back(integral_channel.clone()); - } -} - -void ICFFeatureEvaluatorImpl::setPosition(Size position) -{ - position_ = position; -} - - -int ICFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const -{ - /* - - //following return is equal to this commented code, left here for readability. The new code runs much faster. - * - const vector& feature = features_[feature_ind]; - int x = feature[0] + position_.height; - int y = feature[1] + position_.width; - int x_to = feature[2] + position_.height; - int y_to = feature[3] + position_.width; - int n = feature[4]; - const Mat_& ch = channels_[n]; - return ch(y_to + 1, x_to + 1) - ch(y, x_to + 1) - ch(y_to + 1, x) + ch(y, x); - */ - - CV_Assert(feature_ind < features_.size()); - - return *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][3] + position_.width+1))+ features_[feature_ind][2] + position_.height + 1)) - - *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][1] + position_.width))+ features_[feature_ind][2] + position_.height + 1)) - - *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][3] + position_.width+1))+ features_[feature_ind][0] + position_.height)) + - *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][1] + position_.width))+ features_[feature_ind][0] + position_.height)); -} - -class ACFFeatureEvaluatorImpl : public FeatureEvaluatorImpl -{ -public: - ACFFeatureEvaluatorImpl(const vector >& features): - FeatureEvaluatorImpl(features) - { - } - - virtual void setChannels(InputArrayOfArrays channels); - virtual void setPosition(Size position); - virtual int evaluate(size_t feature_ind) const; -}; - -void ACFFeatureEvaluatorImpl::setChannels(InputArrayOfArrays channels) -{ - channels_.clear(); - vector ch; - channels.getMatVector(ch); - - for( size_t i = 0; i < ch.size(); ++i ) - { - const Mat &channel = ch[i]; - Mat_ acf_channel = Mat_::zeros(channel.rows / 4, channel.cols / 4); - for( int row = 0; row < channel.rows; row += 4 ) - { - for( int col = 0; col < channel.cols; col += 4 ) - { - int sum = 0; - for( int cell_row = row; cell_row < row + 4; ++cell_row ) - for( int cell_col = col; cell_col < col + 4; ++cell_col ) - sum += (int)channel.at(cell_row, cell_col); - - acf_channel(row / 4, col / 4) = sum; - } - } - - channels_.push_back(acf_channel.clone()); - } -} - -void ACFFeatureEvaluatorImpl::setPosition(Size position) -{ - position_ = Size(position.width / 4, position.height / 4); -} - -int ACFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const -{ - CV_Assert(feature_ind < features_.size()); - - const vector& feature = features_[feature_ind]; - int x = feature[0]; - int y = feature[1]; - int n = feature[2]; - return channels_[n].at(y + position_.width, x + position_.height); -} - -Ptr createFeatureEvaluator( - const vector >& features, const std::string& type) -{ - FeatureEvaluator *evaluator = NULL; - if( type == "acf" ) - evaluator = new ACFFeatureEvaluatorImpl(features); - else if( type == "icf" ) - evaluator = new ICFFeatureEvaluatorImpl(features); - else - CV_Error(CV_StsBadArg, "type value is either acf or icf"); - - return Ptr(evaluator); -} - -vector > generateFeatures(Size window_size, const std::string& type, - int count, int channel_count) -{ - CV_Assert(count > 0); - vector > features; - if( type == "acf" ) - { - int cur_count = 0; - int max_count = window_size.width * window_size.height / 16; - count = min(count, max_count); - for( int x = 0; x < window_size.width / 4; ++x ) - for( int y = 0; y < window_size.height / 4; ++y ) - for( int n = 0; n < channel_count; ++n ) - { - int f[] = {x, y, n}; - vector feature(f, f + sizeof(f) / sizeof(*f)); - features.push_back(feature); - if( (cur_count += 1) == count ) - break; - } - } - else if( type == "icf" ) - { - RNG rng; - for( int i = 0; i < count; ++i ) - { - int x = rng.uniform(0, window_size.width - 1); - int y = rng.uniform(0, window_size.height - 1); - int x_to = rng.uniform(x, window_size.width - 1); - int y_to = rng.uniform(y, window_size.height - 1); - int n = rng.uniform(0, channel_count - 1); - int f[] = {x, y, x_to, y_to, n}; - vector feature(f, f + sizeof(f) / sizeof(*f)); - features.push_back(feature); - } - } - else - CV_Error(CV_StsBadArg, "type value is either acf or icf"); - - return features; -} - -void computeChannels(InputArray image, vector& channels) -{ - Mat_ grad; - Mat_ angles; - Mat luv, gray, src; - - if(image.getMat().channels() > 1) - { - src = Mat(image.getMat().rows, image.getMat().cols, CV_32FC3); - image.getMat().convertTo(src, CV_32FC3, 1./255); - - cvtColor(src, gray, CV_RGB2GRAY); - cvtColor(src, luv, CV_RGB2Luv); - } - else - { - src = Mat(image.getMat().rows, image.getMat().cols, CV_32FC1); - image.getMat().convertTo(src, CV_32FC1, 1./255); - src.copyTo(gray); - } - - Mat_ row_der, col_der; - Sobel(gray, row_der, CV_32F, 0, 1); - Sobel(gray, col_der, CV_32F, 1, 0); - - cartToPolar(col_der, row_der, grad, angles, true); - //magnitude(row_der, col_der, grad); - - Mat_ hist = Mat_::zeros(grad.rows, grad.cols); - //const float to_deg = 180 / 3.1415926f; - for (int row = 0; row < grad.rows; ++row) { - for (int col = 0; col < grad.cols; ++col) { - //float angle = atan2(row_der(row, col), col_der(row, col)) * to_deg; - float angle = angles(row, col); - if (angle > 180) - angle -= 180; - if (angle < 0) - angle += 180; - int ind = (int)(angle / 30); - - // If angle == 180, prevent index overflow - if (ind == 6) - ind = 5; - - hist(row, col)[ind] = grad(row, col) * 255; - } - } - - channels.clear(); - - if(image.getMat().channels() > 1) - { - Mat luv_channels[3]; - split(luv, luv_channels); - for( int i = 0; i < 3; ++i ) - channels.push_back(luv_channels[i]); - } - - channels.push_back(grad); - - vector hist_channels; - split(hist, hist_channels); - - for( size_t i = 0; i < hist_channels.size(); ++i ) - channels.push_back(hist_channels[i]); -} - -} /* namespace xobjdetect */ -} /* namespace cv */ diff --git a/modules/xobjdetect/src/cascadeclassifier.h b/modules/xobjdetect/src/cascadeclassifier.h index 0e8af29c0..25f836478 100644 --- a/modules/xobjdetect/src/cascadeclassifier.h +++ b/modules/xobjdetect/src/cascadeclassifier.h @@ -1,3 +1,47 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + #ifndef _OPENCV_CASCADECLASSIFIER_H_ #define _OPENCV_CASCADECLASSIFIER_H_ diff --git a/modules/xobjdetect/src/detector.cpp b/modules/xobjdetect/src/detector.cpp new file mode 100644 index 000000000..e755c6dc3 --- /dev/null +++ b/modules/xobjdetect/src/detector.cpp @@ -0,0 +1,215 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#include "precomp.hpp" + +using std::cerr; +using std::endl; +using std::vector; +using std::string; + +namespace cv { +namespace xobjdetect { + +WBDetector::WBDetector(const string& model_filename): + model_filename_(model_filename) +{} + +static vector sample_patches( + const string& path, + int n_rows, + int n_cols, + int n_patches) +{ + vector filenames; + glob(path, filenames); + vector patches; + int patch_count = 0; + for (size_t i = 0; i < filenames.size(); ++i) { + Mat img = imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE); + for (int row = 0; row + n_rows < img.rows; row += n_rows) { + for (int col = 0; col + n_cols < img.cols; col += n_cols) { + patches.push_back(img(Rect(col, row, n_cols, n_rows)).clone()); + patch_count += 1; + if (patch_count == n_patches) { + goto sampling_finished; + } + } + } + } +sampling_finished: + return patches; +} + +static vector read_imgs(const string& path) +{ + vector filenames; + glob(path, filenames); + vector imgs; + for (size_t i = 0; i < filenames.size(); ++i) { + imgs.push_back(imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE)); + } + return imgs; +} + +void WBDetector::train( + const string& pos_samples_path, + const string& neg_imgs_path) +{ + + vector pos_imgs = read_imgs(pos_samples_path); + vector neg_imgs = sample_patches(neg_imgs_path, 24, 24, pos_imgs.size()); + + assert(pos_imgs.size()); + assert(neg_imgs.size()); + + int n_features; + Mat pos_data, neg_data; + + Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); + eval->init(CvFeatureParams::create(CvFeatureParams::LBP), 1, Size(24, 24)); + n_features = eval->getNumFeatures(); + + const int stages[] = {32, 64, 128, 256, 512, 1024, 2048, 4096}; + const int stage_count = sizeof(stages) / sizeof(*stages); + const int stage_neg = 5000; + const int max_per_image = 25; + + const float scales_arr[] = {.3, .4, .5, .6, .7, .8, .9, 1}; + const vector scales(scales_arr, + scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); + + WaldBoost boost; + vector neg_filenames; + glob(neg_imgs_path, neg_filenames); + + + for (int i = 0; i < stage_count; ++i) { + + cerr << "compute features" << endl; + + pos_data = Mat1b(n_features, pos_imgs.size()); + neg_data = Mat1b(n_features, neg_imgs.size()); + + for (size_t k = 0; k < pos_imgs.size(); ++k) { + eval->setImage(pos_imgs[k], +1, 0, boost.get_feature_indices()); + for (int j = 0; j < n_features; ++j) { + pos_data.at(j, k) = (*eval)(j, 0); + } + } + + for (size_t k = 0; k < neg_imgs.size(); ++k) { + eval->setImage(neg_imgs[k], 0, 0, boost.get_feature_indices()); + for (int j = 0; j < n_features; ++j) { + neg_data.at(j, k) = (*eval)(j, 0); + } + } + + + boost.reset(stages[i]); + boost.fit(pos_data, neg_data); + + if (i + 1 == stage_count) { + break; + } + + int bootstrap_count = 0; + size_t img_i = 0; + for (; img_i < neg_filenames.size(); ++img_i) { + cerr << "win " << bootstrap_count << "/" << stage_neg + << " img " << (img_i + 1) << "/" << neg_filenames.size() << "\r"; + Mat img = imread(neg_filenames[img_i], CV_LOAD_IMAGE_GRAYSCALE); + vector bboxes; + Mat1f confidences; + boost.detect(eval, img, scales, bboxes, confidences); + + if (confidences.rows > 0) { + Mat1i indices; + sortIdx(confidences, indices, + CV_SORT_EVERY_COLUMN + CV_SORT_DESCENDING); + + int win_count = min(max_per_image, confidences.rows); + win_count = min(win_count, stage_neg - bootstrap_count); + Mat window; + for (int k = 0; k < win_count; ++k) { + resize(img(bboxes[indices(k, 0)]), window, Size(24, 24)); + neg_imgs.push_back(window.clone()); + bootstrap_count += 1; + } + if (bootstrap_count >= stage_neg) { + break; + } + } + } + cerr << "bootstrapped " << bootstrap_count << " windows from " + << (img_i + 1) << " images" << endl; + } + + boost.save(model_filename_); +} + +void WBDetector::detect( + const Mat& img, + vector &bboxes, + vector &confidences) +{ + WaldBoost boost; + boost.load(model_filename_); + + Mat test_img = img.clone(); + bboxes.clear(); + confidences.clear(); + vector scales; + for (float scale = 0.2f; scale < 1.2f; scale *= 1.1) { + scales.push_back(scale); + } + Ptr params = CvFeatureParams::create(CvFeatureParams::LBP); + Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); + eval->init(params, 1, Size(24, 24)); + boost.detect(eval, img, scales, bboxes, confidences); + assert(confidences.size() == bboxes.size()); +} + +} +} diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/features.cpp index 37e3375c9..38ba51a7e 100644 --- a/modules/xobjdetect/src/features.cpp +++ b/modules/xobjdetect/src/features.cpp @@ -1,7 +1,48 @@ -#include +/* +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. -#include "traincascade_features.h" -#include "cascadeclassifier.h" + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#include "precomp.hpp" using namespace std; using namespace cv; diff --git a/modules/xobjdetect/src/icfdetector.cpp b/modules/xobjdetect/src/icfdetector.cpp deleted file mode 100644 index aa7829233..000000000 --- a/modules/xobjdetect/src/icfdetector.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* - -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 - (3-clause BSD License) - -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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -#include -using std::ostringstream; - -#include -using std::setfill; -using std::setw; - -#include -using std::cout; -using std::endl; - -#include "precomp.hpp" - -using std::vector; -using std::string; - -using std::min; -using std::max; - - -namespace cv -{ - -namespace xobjdetect -{ - - -void ICFDetector::train(const vector& pos_filenames, - const vector& bg_filenames, - ICFDetectorParams params) -{ - - int color; - if(params.is_grayscale == false) - color = IMREAD_COLOR; - else - color = IMREAD_GRAYSCALE; - - model_n_rows_ = params.model_n_rows; - model_n_cols_ = params.model_n_cols; - ftype_ = params.features_type; - - Size model_size(params.model_n_cols, params.model_n_rows); - - vector samples; /* positive samples + negative samples */ - Mat sample, resized_sample; - int pos_count = 0; - - for( size_t i = 0; i < pos_filenames.size(); ++i, ++pos_count ) - { - cout << setw(6) << (i + 1) << "/" << pos_filenames.size() << "\r"; - Mat img = imread(pos_filenames[i], color); - resize(img, resized_sample, model_size); - samples.push_back(resized_sample.clone()); - } - cout << "\n"; - - int neg_count = 0; - RNG rng; - for( size_t i = 0; i < bg_filenames.size(); ++i ) - { - cout << setw(6) << (i + 1) << "/" << bg_filenames.size() << "\r"; - Mat img = imread(bg_filenames[i], color); - for( int j = 0; j < params.bg_per_image; ++j, ++neg_count) - { - Rect r; - r.x = rng.uniform(0, img.cols-model_size.width); - r.width = model_size.width; - r.y = rng.uniform(0, img.rows-model_size.height); - r.height = model_size.height; - sample = img.colRange(r.x, r.x + r.width).rowRange(r.y, r.y + r.height); - samples.push_back(sample.clone()); - } - } - cout << "\n"; - - Mat_ labels(1, pos_count + neg_count); - for( int i = 0; i < pos_count; ++i) - labels(0, i) = 1; - for( int i = pos_count; i < pos_count + neg_count; ++i ) - labels(0, i) = -1; - - - vector > features; - if(params.is_grayscale == false) - features = generateFeatures(model_size, params.features_type, params.feature_count, 10); - else - features = generateFeatures(model_size, params.features_type, params.feature_count, 7); - - Ptr evaluator = createFeatureEvaluator(features, params.features_type); - - - Mat_ data = Mat_::zeros((int)features.size(), (int)samples.size()); - Mat_ feature_col(1, (int)samples.size()); - - vector channels; - for( int i = 0; i < (int)samples.size(); ++i ) - { - cout << setw(6) << i << "/" << samples.size() << "\r"; - computeChannels(samples[i], channels); - evaluator->setChannels(channels); - //evaluator->assertChannels(); - evaluator->evaluateAll(feature_col); - - CV_Assert(feature_col.cols == (int)features.size()); - - for( int j = 0; j < feature_col.cols; ++j ) - data(j, i) = feature_col(0, j); - } - cout << "\n"; - samples.clear(); - - WaldBoostParams wparams; - wparams.weak_count = params.weak_count; - wparams.alpha = params.alpha; - - waldboost_ = createWaldBoost(wparams); - vector indices = waldboost_->train(data, labels, params.use_fast_log); - cout << "indices: "; - for( size_t i = 0; i < indices.size(); ++i ) - cout << indices[i] << " "; - cout << endl; - - features_.clear(); - for( size_t i = 0; i < indices.size(); ++i ) - features_.push_back(features[indices[i]]); -} - -void ICFDetector::write(FileStorage& fs) const -{ - fs << "{"; - fs << "model_n_rows" << model_n_rows_; - fs << "model_n_cols" << model_n_cols_; - fs << "ftype" << String(ftype_.c_str()); - fs << "waldboost"; - waldboost_->write(fs); - fs << "features" << "["; - for( size_t i = 0; i < features_.size(); ++i ) - { - fs << features_[i]; - } - fs << "]"; - fs << "}"; -} - -void ICFDetector::read(const FileNode& node) -{ - waldboost_ = Ptr(createWaldBoost(WaldBoostParams())); - String f_temp; - node["model_n_rows"] >> model_n_rows_; - node["model_n_cols"] >> model_n_cols_; - f_temp = (String)node["ftype"]; - this->ftype_ = (string)f_temp.c_str(); - waldboost_->read(node["waldboost"]); - FileNode features = node["features"]; - features_.clear(); - vector p; - for( FileNodeIterator n = features.begin(); n != features.end(); ++n ) - { - (*n) >> p; - features_.push_back(p); - } -} - -void ICFDetector::detect(const Mat& img, vector& objects, - float scaleFactor, Size minSize, Size maxSize, float threshold, int slidingStep, std::vector& values) -{ - - - float scale_from = min(model_n_cols_ / (float)maxSize.width, - model_n_rows_ / (float)maxSize.height); - float scale_to = max(model_n_cols_ / (float)minSize.width, - model_n_rows_ / (float)minSize.height); - objects.clear(); - Ptr evaluator = createFeatureEvaluator(features_, ftype_); - Mat rescaled_image; - vector channels; - - for( float scale = scale_from; scale < scale_to + 0.001; scale *= scaleFactor ) - { - int new_width = int(img.cols * scale); - new_width -= new_width % 4; - int new_height = int(img.rows * scale); - new_height -= new_height % 4; - - resize(img, rescaled_image, Size(new_width, new_height)); - computeChannels(rescaled_image, channels); - evaluator->setChannels(channels); - for( int row = 0; row <= rescaled_image.rows - model_n_rows_; row += slidingStep) - { - for( int col = 0; col <= rescaled_image.cols - model_n_cols_; - col += slidingStep ) - { - evaluator->setPosition(Size(row, col)); - float value = waldboost_->predict(evaluator); - if( value > threshold ) - { - values.push_back(value); - int x = (int)(col / scale); - int y = (int)(row / scale); - int width = (int)(model_n_cols_ / scale); - int height = (int)(model_n_rows_ / scale); - objects.push_back(Rect(x, y, width, height)); - } - } - } - - } - -} - -void ICFDetector::detect(const Mat& img, vector& objects, - float minScaleFactor, float maxScaleFactor, float factorStep, float threshold, int slidingStep, std::vector& values) -{ - - if(factorStep <= 0) - { - CV_Error_(CV_StsBadArg, ("factorStep must be > 0")); - } - - objects.clear(); - Ptr evaluator = createFeatureEvaluator(features_, ftype_); - Mat rescaled_image; - vector channels; - - for( float scale = minScaleFactor; scale < maxScaleFactor + 0.001; scale += factorStep ) - { - if(scale < 1.0) - resize(img, rescaled_image, Size(),scale, scale, INTER_AREA); - else if (scale > 1.0) - resize(img, rescaled_image, Size(),scale, scale, INTER_CUBIC); - else //scale == 1.0 - img.copyTo(rescaled_image); - - computeChannels(rescaled_image, channels); - evaluator->setChannels(channels); - for( int row = 0; row <= rescaled_image.rows - model_n_rows_; row += slidingStep) - { - for( int col = 0; col <= rescaled_image.cols - model_n_cols_; - col += slidingStep ) - { - evaluator->setPosition(Size(row, col)); - float value = waldboost_->predict(evaluator); - if( value > threshold ) - { - values.push_back(value); - int x = (int)(col / scale); - int y = (int)(row / scale); - int width = (int)(model_n_cols_ / scale); - int height = (int)(model_n_rows_ / scale); - objects.push_back(Rect(x, y, width, height)); - } - } - } - - } - -} - -void write(FileStorage& fs, String&, const ICFDetector& detector) -{ - detector.write(fs); -} - -void read(const FileNode& node, ICFDetector& d, - const ICFDetector& default_value) -{ - if( node.empty() ) - d = default_value; - else - d.read(node); -} - -} /* namespace xobjdetect */ -} /* namespace cv */ diff --git a/modules/xobjdetect/src/lbpfeatures.cpp b/modules/xobjdetect/src/lbpfeatures.cpp index 48a23f008..bb0c27e64 100644 --- a/modules/xobjdetect/src/lbpfeatures.cpp +++ b/modules/xobjdetect/src/lbpfeatures.cpp @@ -1,9 +1,49 @@ -#include -#include "lbpfeatures.h" -#include "cascadeclassifier.h" +/* +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. -#include + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#include "precomp.hpp" using namespace cv; diff --git a/modules/xobjdetect/src/lbpfeatures.h b/modules/xobjdetect/src/lbpfeatures.h index db0719db5..c9de6ec2c 100644 --- a/modules/xobjdetect/src/lbpfeatures.h +++ b/modules/xobjdetect/src/lbpfeatures.h @@ -1,3 +1,47 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + #ifndef _OPENCV_LBPFEATURES_H_ #define _OPENCV_LBPFEATURES_H_ diff --git a/modules/xobjdetect/src/main.cpp b/modules/xobjdetect/src/main.cpp deleted file mode 100644 index e816c2657..000000000 --- a/modules/xobjdetect/src/main.cpp +++ /dev/null @@ -1,556 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "lbpfeatures.h" - -using namespace std; -using namespace cv; - -static void compute_cdf(const Mat1b& data, - const Mat1f& weights, - Mat1f& cdf) -{ - for (int i = 0; i < cdf.cols; ++i) - cdf(0, i) = 0; - - for (int i = 0; i < weights.cols; ++i) { - cdf(0, data(0, i)) += weights(0, i); - } - - for (int i = 1; i < cdf.cols; ++i) { - cdf(0, i) += cdf(0, i - 1); - } -} - -void compute_min_step(const Mat &data_pos, const Mat &data_neg, size_t n_bins, - Mat &data_min, Mat &data_step) -{ - // Check that quantized data will fit in unsigned char - assert(n_bins <= 256); - - assert(data_pos.rows == data_neg.rows); - - Mat reduced_pos, reduced_neg; - - reduce(data_pos, reduced_pos, 1, CV_REDUCE_MIN); - reduce(data_neg, reduced_neg, 1, CV_REDUCE_MIN); - min(reduced_pos, reduced_neg, data_min); - data_min -= 0.01; - - Mat data_max; - reduce(data_pos, reduced_pos, 1, CV_REDUCE_MAX); - reduce(data_neg, reduced_neg, 1, CV_REDUCE_MAX); - max(reduced_pos, reduced_neg, data_max); - data_max += 0.01; - - data_step = (data_max - data_min) / (n_bins - 1); -} - -void quantize_data(Mat &data, Mat1f &data_min, Mat1f &data_step) -{ -#pragma omp parallel for - for (int col = 0; col < data.cols; ++col) { - data.col(col) -= data_min; - data.col(col) /= data_step; - } - data.convertTo(data, CV_8U); -} - -class WaldBoost -{ -public: - WaldBoost(int weak_count): - weak_count_(weak_count), - thresholds_(), - alphas_(), - feature_indices_(), - polarities_(), - cascade_thresholds_() {} - - WaldBoost(): - weak_count_(), - thresholds_(), - alphas_(), - feature_indices_(), - polarities_(), - cascade_thresholds_() {} - - vector get_feature_indices() - { - return feature_indices_; - } - - void detect(Ptr eval, - const Mat& img, const vector& scales, - vector& bboxes, Mat1f& confidences) - { - bboxes.clear(); - confidences.release(); - - Mat resized_img; - int step = 4; - float h; - for (size_t i = 0; i < scales.size(); ++i) { - float scale = scales[i]; - resize(img, resized_img, Size(), scale, scale); - eval->setImage(resized_img, 0, 0, feature_indices_); - int n_rows = 24 / scale; - int n_cols = 24 / scale; - for (int r = 0; r + 24 < resized_img.rows; r += step) { - for (int c = 0; c + 24 < resized_img.cols; c += step) { - //eval->setImage(resized_img(Rect(c, r, 24, 24)), 0, 0); - eval->setWindow(Point(c, r)); - if (predict(eval, &h) == +1) { - int row = r / scale; - int col = c / scale; - bboxes.push_back(Rect(col, row, n_cols, n_rows)); - confidences.push_back(h); - } - } - } - } - groupRectangles(bboxes, 3, 0.7); - } - - void fit(Mat& data_pos, Mat& data_neg) - { - // data_pos: F x N_pos - // data_neg: F x N_neg - // every feature corresponds to row - // every sample corresponds to column - assert(data_pos.rows >= weak_count_); - assert(data_pos.rows == data_neg.rows); - - vector feature_ignore; - for (int i = 0; i < data_pos.rows; ++i) { - feature_ignore.push_back(false); - } - - Mat1f pos_weights(1, data_pos.cols, 1.0f / (2 * data_pos.cols)); - Mat1f neg_weights(1, data_neg.cols, 1.0f / (2 * data_neg.cols)); - Mat1f pos_trace(1, data_pos.cols, 0.0f); - Mat1f neg_trace(1, data_neg.cols, 0.0f); - - bool quantize = false; - if (data_pos.type() != CV_8U) { - cerr << "quantize" << endl; - quantize = true; - } - - Mat1f data_min, data_step; - int n_bins = 256; - if (quantize) { - compute_min_step(data_pos, data_neg, n_bins, data_min, data_step); - quantize_data(data_pos, data_min, data_step); - quantize_data(data_neg, data_min, data_step); - } - - cerr << "pos=" << data_pos.cols << " neg=" << data_neg.cols << endl; - for (int i = 0; i < weak_count_; ++i) { - // Train weak learner with lowest error using weights - double min_err = DBL_MAX; - int min_feature_ind = -1; - int min_polarity = 0; - int threshold_q = 0; - float min_threshold = 0; -#pragma omp parallel for - for (int feat_i = 0; feat_i < data_pos.rows; ++feat_i) { - if (feature_ignore[feat_i]) - continue; - - // Construct cdf - Mat1f pos_cdf(1, n_bins), neg_cdf(1, n_bins); - compute_cdf(data_pos.row(feat_i), pos_weights, pos_cdf); - compute_cdf(data_neg.row(feat_i), neg_weights, neg_cdf); - - float neg_total = sum(neg_weights)[0]; - Mat1f err_direct = pos_cdf + neg_total - neg_cdf; - Mat1f err_backward = 1.0f - err_direct; - - int idx1[2], idx2[2]; - double err1, err2; - minMaxIdx(err_direct, &err1, NULL, idx1); - minMaxIdx(err_backward, &err2, NULL, idx2); -#pragma omp critical - { - if (min(err1, err2) < min_err) { - if (err1 < err2) { - min_err = err1; - min_polarity = +1; - threshold_q = idx1[1]; - } else { - min_err = err2; - min_polarity = -1; - threshold_q = idx2[1]; - } - min_feature_ind = feat_i; - if (quantize) { - min_threshold = data_min(feat_i, 0) + data_step(feat_i, 0) * - (threshold_q + .5f); - } else { - min_threshold = threshold_q + .5f; - } - } - } - } - - - float alpha = .5f * log((1 - min_err) / min_err); - alphas_.push_back(alpha); - feature_indices_.push_back(min_feature_ind); - thresholds_.push_back(min_threshold); - polarities_.push_back(min_polarity); - feature_ignore[min_feature_ind] = true; - - - double loss = 0; - // Update positive weights - for (int j = 0; j < data_pos.cols; ++j) { - int val = data_pos.at(min_feature_ind, j); - int label = min_polarity * (val - threshold_q) >= 0 ? +1 : -1; - pos_weights(0, j) *= exp(-alpha * label); - pos_trace(0, j) += alpha * label; - loss += exp(-pos_trace(0, j)) / (2.0f * data_pos.cols); - } - double min_pos, max_neg = -100000; - minMaxIdx(pos_trace, &min_pos, NULL); - - // Update negative weights - for (int j = 0; j < data_neg.cols; ++j) { - int val = data_neg.at(min_feature_ind, j); - int label = min_polarity * (val - threshold_q) >= 0 ? +1 : -1; - neg_weights(0, j) *= exp(alpha * label); - neg_trace(0, j) += alpha * label; - loss += exp(+neg_trace(0, j)) / (2.0f * data_neg.cols); - } - - // Compute threshold - double a = 0.02; - int col_pos = 0, col_neg = 0; - int cur_pos = 0, cur_neg = 0; - int max_col = -1; - bool max_pos = false; - for (; - col_pos < data_pos.cols && - col_neg < data_neg.cols; ) { - bool pos = false; - if (data_pos.at(min_feature_ind, col_pos) < - data_pos.at(min_feature_ind, col_neg)) { - col_pos += 1; - cur_pos += 1; - pos = true; - } else { - col_neg += 1; - cur_neg += 1; - } - float p_neg = cur_neg / (float)data_neg.cols; - float p_pos = cur_pos / (float)data_pos.cols; - if (a * p_neg > p_pos) { - if (pos) - max_col = col_pos; - else - max_col = col_neg; - max_pos = pos; - } - } - - if (max_pos) { - cascade_thresholds_.push_back(pos_trace(0, max_col)); - } else { - cascade_thresholds_.push_back(neg_trace(0, max_col)); - } - - cerr << "i=" << setw(4) << i; - cerr << " feat=" << setw(5) << min_feature_ind; - cerr << " thr=" << setw(3) << threshold_q; - cerr << " alpha=" << fixed << setprecision(3) - << alpha << " err=" << fixed << setprecision(3) << min_err - << " loss=" << scientific << loss << endl; - - if (loss < 1e-50 || min_err > 0.5) { - cerr << "Stopping early" << endl; - weak_count_ = i + 1; - break; - } - - // Normalize weights - double z = (sum(pos_weights) + sum(neg_weights))[0]; - pos_weights /= z; - neg_weights /= z; - } - } - - int predict(Ptr eval, float *h) const - { - const float thr = -2.5; - assert(feature_indices_.size() == size_t(weak_count_)); - float res = 0; - for (int i = 0; i < weak_count_; ++i) { - float val = (*eval)(feature_indices_[i], 0); - int label = polarities_[i] * (val - thresholds_[i]) > 0 ? +1: -1; - res += alphas_[i] * label; - if (res < cascade_thresholds_[i]) { - return -1; - } - } - *h = res; - return res > thr ? +1 : -1; - } - - void save(const string& filename) - { - ofstream f(filename.c_str()); - f << weak_count_ << endl; - for (size_t i = 0; i < thresholds_.size(); ++i) { - f << thresholds_[i] << " "; - } - f << endl; - for (size_t i = 0; i < alphas_.size(); ++i) { - f << alphas_[i] << " "; - } - f << endl; - for (size_t i = 0; i < polarities_.size(); ++i) { - f << polarities_[i] << " "; - } - f << endl; - for (size_t i = 0; i < cascade_thresholds_.size(); ++i) { - f << cascade_thresholds_[i] << " "; - } - f << endl; - for (size_t i = 0; i < feature_indices_.size(); ++i) { - f << feature_indices_[i] << " "; - } - f << endl; - } - - void load(const string& filename) - { - ifstream f(filename.c_str()); - f >> weak_count_; - thresholds_.resize(weak_count_); - alphas_.resize(weak_count_); - polarities_.resize(weak_count_); - cascade_thresholds_.resize(weak_count_); - feature_indices_.resize(weak_count_); - - for (int i = 0; i < weak_count_; ++i) { - f >> thresholds_[i]; - } - for (int i = 0; i < weak_count_; ++i) { - f >> alphas_[i]; - } - for (int i = 0; i < weak_count_; ++i) { - f >> polarities_[i]; - } - for (int i = 0; i < weak_count_; ++i) { - f >> cascade_thresholds_[i]; - } - for (int i = 0; i < weak_count_; ++i) { - f >> feature_indices_[i]; - } - } - - void reset(int weak_count) - { - weak_count_ = weak_count; - thresholds_.clear(); - alphas_.clear(); - feature_indices_.clear(); - polarities_.clear(); - cascade_thresholds_.clear(); - } - - ~WaldBoost() - { - } -private: - int weak_count_; - vector thresholds_; - vector alphas_; - vector feature_indices_; - vector polarities_; - vector cascade_thresholds_; -}; - - -void test_boosting(); -void train_boosting(); - - -int main(int argc, char **argv) -{ - string stage = argv[1]; - if (stage == "train") { - train_boosting(); - } else if (stage == "detect") { - test_boosting(); - } -} - - -vector -read_imgs(const string& path) -{ - vector filenames; - glob(path, filenames); - vector imgs; - for (size_t i = 0; i < filenames.size(); ++i) { - imgs.push_back(imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE)); - } - return imgs; -} -vector -sample_patches(const string& path, int n_rows, int n_cols, int n_patches) -{ - vector filenames; - glob(path, filenames); - vector patches; - int patch_count = 0; - for (size_t i = 0; i < filenames.size(); ++i) { - Mat img = imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE); - for (int row = 0; row + n_rows < img.rows; row += n_rows) { - for (int col = 0; col + n_cols < img.cols; col += n_cols) { - patches.push_back(img(Rect(col, row, n_cols, n_rows)).clone()); - patch_count += 1; - if (patch_count == n_patches) { - goto sampling_finished; - } - } - } - } -sampling_finished: - return patches; -} - -void train_boosting() -{ - cerr << "read imgs" << endl; - vector pos_imgs = read_imgs("/home/vlad/gsoc/lfw_faces"); - assert(pos_imgs.size()); - - const char neg_path[] = "/home/vlad/gsoc/rtsd_bg"; - vector neg_imgs = sample_patches(neg_path, 24, 24, pos_imgs.size()); - assert(neg_imgs.size()); - - int n_features; - Mat pos_data, neg_data; - - Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); - eval->init(CvFeatureParams::create(CvFeatureParams::LBP), 1, Size(24, 24)); - n_features = eval->getNumFeatures(); - - const int stages[] = {32, 64, 128, 256, 512, 1024, 2048, 4096}; - const int stage_count = sizeof(stages) / sizeof(*stages); - const int stage_neg = 5000; - const int max_per_image = 25; - - const float scales_arr[] = {.3, .4, .5, .6, .7, .8, .9, 1}; - const vector scales(scales_arr, - scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); - - WaldBoost boost; - vector neg_filenames; - glob(neg_path, neg_filenames); - - - for (int i = 0; i < stage_count; ++i) { - - cerr << "compute features" << endl; - - pos_data = Mat1b(n_features, pos_imgs.size()); - neg_data = Mat1b(n_features, neg_imgs.size()); - - for (size_t k = 0; k < pos_imgs.size(); ++k) { - eval->setImage(pos_imgs[k], +1, 0, boost.get_feature_indices()); - for (int j = 0; j < n_features; ++j) { - pos_data.at(j, k) = (*eval)(j, 0); - } - } - - for (size_t k = 0; k < neg_imgs.size(); ++k) { - eval->setImage(neg_imgs[k], 0, 0, boost.get_feature_indices()); - for (int j = 0; j < n_features; ++j) { - neg_data.at(j, k) = (*eval)(j, 0); - } - } - - - boost.reset(stages[i]); - boost.fit(pos_data, neg_data); - - if (i + 1 == stage_count) { - break; - } - - int bootstrap_count = 0; - int img_i = 0; - for (; img_i < neg_filenames.size(); ++img_i) { - cerr << "win " << bootstrap_count << "/" << stage_neg - << " img " << (img_i + 1) << "/" << neg_filenames.size() << "\r"; - Mat img = imread(neg_filenames[img_i], CV_LOAD_IMAGE_GRAYSCALE); - vector bboxes; - Mat1f confidences; - boost.detect(eval, img, scales, bboxes, confidences); - - if (confidences.rows > 0) { - Mat1i indices; - sortIdx(confidences, indices, - CV_SORT_EVERY_COLUMN + CV_SORT_DESCENDING); - - int win_count = min(max_per_image, confidences.rows); - win_count = min(win_count, stage_neg - bootstrap_count); - Mat window; - for (int k = 0; k < win_count; ++k) { - resize(img(bboxes[indices(k, 0)]), window, Size(24, 24)); - neg_imgs.push_back(window.clone()); - bootstrap_count += 1; - } - if (bootstrap_count >= stage_neg) { - break; - } - } - } - cerr << "bootstrapped " << bootstrap_count << " windows from " - << (img_i + 1) << " images" << endl; - } - - boost.save("models/model.txt"); - vector feature_indices = boost.get_feature_indices(); - Mat1i feature_map(1, feature_indices.size()); - for (size_t i = 0; i < feature_indices.size(); ++i) { - feature_map(0, i) = feature_indices[i]; - } - FileStorage fs("models/features.yaml", FileStorage::WRITE); - eval->writeFeatures(fs, feature_map); -} - -void test_boosting() -{ - WaldBoost boost; - const char model_filename[] = "models/model.txt"; - boost.load(model_filename); - - Mat test_img = imread("imgs/test4.png", CV_LOAD_IMAGE_GRAYSCALE); - vector bboxes; - Mat1f confidences; - vector scales; - for (float scale = 0.2f; scale < 1.2f; scale *= 1.2) { - scales.push_back(scale); - } - Ptr params = CvFeatureParams::create(CvFeatureParams::LBP); - Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); - eval->init(params, 1, Size(24, 24)); - boost.detect(eval, test_img, scales, bboxes, confidences); - cerr << "detected " << bboxes.size() << " objects" << endl; - - for (size_t i = 0; i < bboxes.size(); ++i) { - rectangle(test_img, bboxes[i], Scalar(255, 0, 0)); - } - imwrite("imgs/out.png", test_img); -} - diff --git a/modules/xobjdetect/src/precomp.hpp b/modules/xobjdetect/src/precomp.hpp index 811329940..d8e5be196 100644 --- a/modules/xobjdetect/src/precomp.hpp +++ b/modules/xobjdetect/src/precomp.hpp @@ -1,7 +1,6 @@ /* - -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, +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. @@ -9,7 +8,12 @@ copy or use the software. For Open Source Computer Vision Library (3-clause BSD License) -Copyright (C) 2013, OpenCV Foundation, all rights reserved. +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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, @@ -28,31 +32,48 @@ are permitted provided that the following conditions are met: 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 copyright holders or contributors be liable for -any direct, indirect, incidental, special, exemplary, or consequential damages +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders 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. - */ #ifndef __OPENCV_XOBJDETECT_PRECOMP_HPP__ #define __OPENCV_XOBJDETECT_PRECOMP_HPP__ #include -#include #include #include #include +#include +#include + #include +#include + +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include + +#include "cascadeclassifier.h" +#include "traincascade_features.h" +#include "lbpfeatures.h" +#include "waldboost.hpp" #endif /* __OPENCV_XOBJDETECT_PRECOMP_HPP__ */ diff --git a/modules/xobjdetect/src/stump.cpp b/modules/xobjdetect/src/stump.cpp deleted file mode 100644 index 221445a29..000000000 --- a/modules/xobjdetect/src/stump.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - -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 - (3-clause BSD License) - -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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -#include "precomp.hpp" - -namespace cv -{ -namespace xobjdetect -{ - -/* Cumulative sum by rows */ -static void cumsum(const Mat_& src, Mat_ dst) -{ - CV_Assert(src.cols > 0); - - for( int row = 0; row < src.rows; ++row ) - { - dst(row, 0) = src(row, 0); - for( int col = 1; col < src.cols; ++col ) - { - dst(row, col) = dst(row, col - 1) + src(row, col); - } - } -} - -//fast log implementation. A bit less accurate but ~5x faster -inline float fast_log2 (float val) -{ - int * const exp_ptr = reinterpret_cast (&val); - int x = *exp_ptr; - const int log_2 = ((x >> 23) & 255) - 128; - x &= ~(255 << 23); - x += 127 << 23; - *exp_ptr = x; - - val = ((-1.0f/3) * val + 2) * val - 2.0f/3; // (1) - - return (val + log_2); -} - -inline float fast_log (const float &val) -{ - return (fast_log2 (val) * 0.69314718f); -} - -int Stump::train(const Mat& data, const Mat& labels, const Mat& weights, const std::vector& visited_features, bool use_fast_log) -{ - CV_Assert(labels.rows == 1 && labels.cols == data.cols); - CV_Assert(weights.rows == 1 && weights.cols == data.cols); - CV_Assert(data.cols > 1); - /* Assert that data and labels have int type */ - /* Assert that weights have float type */ - - Mat_ d = Mat_::zeros(1, data.cols); - const Mat_& l = labels; - const Mat_& w = weights; - - Mat_ indices(1, l.cols); - - Mat_ sorted_d(1, data.cols); - Mat_ sorted_l(1, l.cols); - Mat_ sorted_w(1, w.cols); - - - Mat_ pos_c_w = Mat_::zeros(1, w.cols); - Mat_ neg_c_w = Mat_::zeros(1, w.cols); - - - float min_err = FLT_MAX; - int min_row = -1; - int min_thr = -1; - int min_pol = -1; - float min_pos = 1; - float min_neg = -1; - float eps = 1.0f / (4 * l.cols); - - /* For every feature */ - for( int row = 0; row < data.rows; ++row ) - { - if(std::find(visited_features.begin(), visited_features.end(), row) != visited_features.end()) { - //feature discarded - continue; - } - data.row(row).copyTo(d.row(0)); - - sortIdx(d, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - for( int col = 0; col < indices.cols; ++col ) - { - int ind = indices(0, col); - sorted_d(0, col) = d(0, ind); - sorted_l(0, col) = l(0, ind); - sorted_w(0, col) = w(0, ind); - } - - Mat_ pos_w = Mat_::zeros(1, w.cols); - Mat_ neg_w = Mat_::zeros(1, w.cols); - for( int col = 0; col < d.cols; ++col ) - { - float weight = sorted_w(0, col); - if( sorted_l(0, col) == +1) - pos_w(0, col) = weight; - else - neg_w(0, col) = weight; - } - - cumsum(pos_w, pos_c_w); - cumsum(neg_w, neg_c_w); - - float pos_total_w = pos_c_w(0, w.cols - 1); - float neg_total_w = neg_c_w(0, w.cols - 1); - - for( int col = 0; col < w.cols - 1; ++col ) - { - float err, h_pos, h_neg; - float pos_wrong, pos_right; - float neg_wrong, neg_right; - - /* Direct polarity */ - - pos_wrong = pos_c_w(0, col); - pos_right = pos_total_w - pos_wrong; - - neg_right = neg_c_w(0, col); - neg_wrong = neg_total_w - neg_right; - - err = sqrt(pos_right * neg_wrong) + sqrt(pos_wrong * neg_right); - - if(use_fast_log) - { - h_pos = .5f * fast_log((pos_right + eps) / (pos_wrong + eps)); - h_neg = .5f * fast_log((neg_wrong + eps) / (neg_right + eps)); - } - else - { - h_pos = .5f * log((pos_right + eps) / (pos_wrong + eps)); - h_neg = .5f * log((neg_wrong + eps) / (neg_right + eps)); - } - - if( err < min_err ) - { - min_err = err; - min_row = row; - min_thr = (sorted_d(0, col) + sorted_d(0, col + 1)) / 2; - min_pol = +1; - min_pos = h_pos; - min_neg = h_neg; - } - - /* Opposite polarity */ - - swap(pos_right, pos_wrong); - swap(neg_right, neg_wrong); - - err = sqrt(pos_right * neg_wrong) + sqrt(pos_wrong * neg_right); - - if( err < min_err ) - { - min_err = err; - min_row = row; - min_thr = (sorted_d(0, col) + sorted_d(0, col + 1)) / 2; - min_pol = -1; - min_pos = -h_pos; - min_neg = -h_neg; - } - - } - } - - threshold_ = min_thr; - polarity_ = min_pol; - pos_value_ = min_pos; - neg_value_ = min_neg; - - return min_row; -} - -float Stump::predict(int value) const -{ - return polarity_ * (value - threshold_) > 0 ? pos_value_ : neg_value_; -} - - -void read(const FileNode& node, Stump& s, const Stump& default_value) -{ - if( node.empty() ) - s = default_value; - else - s.read(node); -} - -void write(FileStorage& fs, String&, const Stump& s) -{ - s.write(fs); -} - -} /* namespace xobjdetect */ -} /* namespace cv */ diff --git a/modules/xobjdetect/src/traincascade_features.h b/modules/xobjdetect/src/traincascade_features.h index 59cda4081..3e9b36748 100644 --- a/modules/xobjdetect/src/traincascade_features.h +++ b/modules/xobjdetect/src/traincascade_features.h @@ -1,3 +1,47 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + #ifndef _OPENCV_FEATURES_H_ #define _OPENCV_FEATURES_H_ diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 8866ab260..de95220bd 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -1,7 +1,6 @@ /* - -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, +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. @@ -9,7 +8,12 @@ copy or use the software. For Open Source Computer Vision Library (3-clause BSD License) -Copyright (C) 2013, OpenCV Foundation, all rights reserved. +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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, @@ -28,360 +32,367 @@ are permitted provided that the following conditions are met: 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 copyright holders or contributors be liable for -any direct, indirect, incidental, special, exemplary, or consequential damages +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders 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. - */ #include "precomp.hpp" -using std::swap; - -using std::vector; - -#include -using std::cout; -using std::endl; - - - - - - namespace cv { -namespace xobjdetect +static void compute_cdf(const Mat1b& data, + const Mat1f& weights, + Mat1f& cdf) { - //sort in-place of columns of the input matrix - void sort_columns_without_copy(Mat& m, Mat indices) - { - - if(indices.data == 0) - sortIdx(m, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - Mat indices_of_indices; - sortIdx(indices, indices_of_indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - std::vector visited; - for(int c = 0; c(0,ind_v))).copyTo(column); - } - else - { - temp_column.copyTo(column); - } - - - if(indices_of_indices.at(0,next) != next) //value is in the right place - { - //store the next value to change - (m.col(indices_of_indices.at(0,next))).copyTo(temp_column); - //insert the value to change at the right place - column.copyTo(m.col(indices_of_indices.at(0,next))); - - //find the index of the next value to change - next = indices_of_indices.at(0,next); - //if the idenx is not visited yet - if(visited[next] == false) - { - //then mark it as visited, it will be computed in the next round - visited[next] = true; - } - else - { - //find first non visited index - int i = 0; - while(i<(int)visited.size() && visited[i] == true) - { - i++; - } - ind_v = i; - next = i; - temp_column = Mat(); - - } - } - else // value is already at the right place - { - visited[next] = true; - int i = 0; - while(i<(int)visited.size() && visited[i] == true) - { - i++; - } - next = i; - temp_column = Mat(); - ind_v = i; - } - - - } - - - } - -class WaldBoostImpl : public WaldBoost -{ -public: - /* Initialize WaldBoost cascade with default of specified parameters */ - WaldBoostImpl(const WaldBoostParams& params): - params_(params) - {} - - virtual std::vector train(Mat& data, - const Mat& labels, bool use_fast_log=false); - - virtual float predict( - const Ptr& feature_evaluator) const; - - virtual void write(FileStorage& fs) const; - - virtual void read(const FileNode& node); - -private: - /* Parameters for cascade training */ - WaldBoostParams params_; - /* Stumps in cascade */ - std::vector stumps_; - /* Rejection thresholds for linear combination at every stump evaluation */ - std::vector thresholds_; -}; - -static int count(const Mat_ &m, int elem) -{ - int res = 0; - for( int row = 0; row < m.rows; ++row ) - for( int col = 0; col < m.cols; ++col ) - if( m(row, col) == elem) - res += 1; - return res; -} - -void WaldBoostImpl::read(const FileNode& node) -{ - FileNode params = node["waldboost_params"]; - params_.weak_count = (int)(params["weak_count"]); - params_.alpha = (float)(params["alpha"]); - - FileNode stumps = node["waldboost_stumps"]; - stumps_.clear(); - for( FileNodeIterator n = stumps.begin(); n != stumps.end(); ++n ) - { - Stump s; - *n >> s; - stumps_.push_back(s); + for (int i = 0; i < weights.cols; ++i) { + cdf(0, data(0, i)) += weights(0, i); } - FileNode thresholds = node["waldboost_thresholds"]; - thresholds_.clear(); - for( FileNodeIterator n = thresholds.begin(); n != thresholds.end(); ++n ) - { - float t; - *n >> t; - thresholds_.push_back(t); + for (int i = 1; i < cdf.cols; ++i) { + cdf(0, i) += cdf(0, i - 1); } } -void WaldBoostImpl::write(FileStorage& fs) const +static void compute_min_step(const Mat &data_pos, const Mat &data_neg, size_t n_bins, + Mat &data_min, Mat &data_step) { - fs << "{"; - fs << "waldboost_params" << "{" - << "weak_count" << params_.weak_count - << "alpha" << params_.alpha - << "}"; + // Check that quantized data will fit in unsigned char + assert(n_bins <= 256); - fs << "waldboost_stumps" << "["; - for( size_t i = 0; i < stumps_.size(); ++i ) - fs << stumps_[i]; - fs << "]"; + assert(data_pos.rows == data_neg.rows); - fs << "waldboost_thresholds" << "["; - for( size_t i = 0; i < thresholds_.size(); ++i ) - fs << thresholds_[i]; - fs << "]"; - fs << "}"; + Mat reduced_pos, reduced_neg; + reduce(data_pos, reduced_pos, 1, CV_REDUCE_MIN); + reduce(data_neg, reduced_neg, 1, CV_REDUCE_MIN); + min(reduced_pos, reduced_neg, data_min); + data_min -= 0.01; + + Mat data_max; + reduce(data_pos, reduced_pos, 1, CV_REDUCE_MAX); + reduce(data_neg, reduced_neg, 1, CV_REDUCE_MAX); + max(reduced_pos, reduced_neg, data_max); + data_max += 0.01; + + data_step = (data_max - data_min) / (n_bins - 1); } -vector WaldBoostImpl::train(Mat& data, const Mat& labels_, bool use_fast_log) +static void quantize_data(Mat &data, Mat1f &data_min, Mat1f &data_step) { - CV_Assert(labels_.rows == 1 && labels_.cols == data.cols); - CV_Assert(data.rows >= params_.weak_count); - - Mat labels; - labels_.copyTo(labels); - - bool null_data = true; - for( int row = 0; row < data.rows; ++row ) - { - for( int col = 0; col < data.cols; ++col ) - if( data.at(row, col) ) - null_data = false; +//#pragma omp parallel for + for (int col = 0; col < data.cols; ++col) { + data.col(col) -= data_min; + data.col(col) /= data_step; } - CV_Assert(!null_data); + data.convertTo(data, CV_8U); +} - int pos_count = count(labels, +1); - int neg_count = count(labels, -1); +WaldBoost::WaldBoost(int weak_count): + weak_count_(weak_count), + thresholds_(), + alphas_(), + feature_indices_(), + polarities_(), + cascade_thresholds_() {} - Mat_ weights(labels.rows, labels.cols); - float pos_weight = 1.0f / (2 * pos_count); - float neg_weight = 1.0f / (2 * neg_count); - for( int col = 0; col < weights.cols; ++col ) - { - if( labels.at(0, col) == +1 ) - weights.at(0, col) = pos_weight; - else - weights.at(0, col) = neg_weight; +WaldBoost::WaldBoost(): + weak_count_(), + thresholds_(), + alphas_(), + feature_indices_(), + polarities_(), + cascade_thresholds_() {} + +std::vector WaldBoost::get_feature_indices() +{ + return feature_indices_; +} + +void WaldBoost::detect(Ptr eval, + const Mat& img, const std::vector& scales, + std::vector& bboxes, Mat1f& confidences) +{ + bboxes.clear(); + confidences.release(); + + Mat resized_img; + int step = 4; + float h; + for (size_t i = 0; i < scales.size(); ++i) { + float scale = scales[i]; + resize(img, resized_img, Size(), scale, scale); + eval->setImage(resized_img, 0, 0, feature_indices_); + int n_rows = 24 / scale; + int n_cols = 24 / scale; + for (int r = 0; r + 24 < resized_img.rows; r += step) { + for (int c = 0; c + 24 < resized_img.cols; c += step) { + //eval->setImage(resized_img(Rect(c, r, 24, 24)), 0, 0); + eval->setWindow(Point(c, r)); + if (predict(eval, &h) == +1) { + int row = r / scale; + int col = c / scale; + bboxes.push_back(Rect(col, row, n_cols, n_rows)); + confidences.push_back(h); + } + } + } + } + groupRectangles(bboxes, 3, 0.7); +} + +void WaldBoost::detect(Ptr eval, + const Mat& img, const std::vector& scales, + std::vector& bboxes, std::vector& confidences) +{ + bboxes.clear(); + confidences.clear(); + + Mat resized_img; + int step = 4; + float h; + for (size_t i = 0; i < scales.size(); ++i) { + float scale = scales[i]; + resize(img, resized_img, Size(), scale, scale); + eval->setImage(resized_img, 0, 0, feature_indices_); + int n_rows = 24 / scale; + int n_cols = 24 / scale; + for (int r = 0; r + 24 < resized_img.rows; r += step) { + for (int c = 0; c + 24 < resized_img.cols; c += step) { + eval->setWindow(Point(c, r)); + if (predict(eval, &h) == +1) { + int row = r / scale; + int col = c / scale; + bboxes.push_back(Rect(col, row, n_cols, n_rows)); + confidences.push_back(h); + } + } + } + } + std::vector levels(bboxes.size(), 0); + groupRectangles(bboxes, levels, confidences, 3, 0.7); +} + +void WaldBoost::fit(Mat& data_pos, Mat& data_neg) +{ + // data_pos: F x N_pos + // data_neg: F x N_neg + // every feature corresponds to row + // every sample corresponds to column + assert(data_pos.rows >= weak_count_); + assert(data_pos.rows == data_neg.rows); + + std::vector feature_ignore; + for (int i = 0; i < data_pos.rows; ++i) { + feature_ignore.push_back(false); } - vector feature_indices_pool; - for( int ind = 0; ind < data.rows; ++ind ) - feature_indices_pool.push_back(ind); + Mat1f pos_weights(1, data_pos.cols, 1.0f / (2 * data_pos.cols)); + Mat1f neg_weights(1, data_neg.cols, 1.0f / (2 * data_neg.cols)); + Mat1f pos_trace(1, data_pos.cols, 0.0f); + Mat1f neg_trace(1, data_neg.cols, 0.0f); - vector feature_indices; - vector visited_features; - Mat_ trace = Mat_::zeros(labels.rows, labels.cols); - stumps_.clear(); - thresholds_.clear(); - for( int i = 0; i < params_.weak_count; ++i) - { - Stump s; - int feature_ind = s.train(data, labels, weights, visited_features, use_fast_log); - stumps_.push_back(s); - int ind = feature_indices_pool[feature_ind]; - //we don't need to erase the feature index anymore, because we ignore them if already visited - //feature_indices_pool.erase(feature_indices_pool.begin() + feature_ind); - feature_indices.push_back(ind); + bool quantize = false; + if (data_pos.type() != CV_8U) { + std::cerr << "quantize" << std::endl; + quantize = true; + } - // Recompute weights - for( int col = 0; col < weights.cols; ++col ) - { - float h = s.predict(data.at(feature_ind, col)); - trace(0, col) += h; - int label = labels.at(0, col); - weights.at(0, col) *= exp(-label * h); + Mat1f data_min, data_step; + int n_bins = 256; + if (quantize) { + compute_min_step(data_pos, data_neg, n_bins, data_min, data_step); + quantize_data(data_pos, data_min, data_step); + quantize_data(data_neg, data_min, data_step); + } + + std::cerr << "pos=" << data_pos.cols << " neg=" << data_neg.cols << std::endl; + for (int i = 0; i < weak_count_; ++i) { + // Train weak learner with lowest error using weights + double min_err = DBL_MAX; + int min_feature_ind = -1; + int min_polarity = 0; + int threshold_q = 0; + float min_threshold = 0; +//#pragma omp parallel for + for (int feat_i = 0; feat_i < data_pos.rows; ++feat_i) { + if (feature_ignore[feat_i]) + continue; + + // Construct cdf + Mat1f pos_cdf(1, n_bins), neg_cdf(1, n_bins); + compute_cdf(data_pos.row(feat_i), pos_weights, pos_cdf); + compute_cdf(data_neg.row(feat_i), neg_weights, neg_cdf); + + float neg_total = sum(neg_weights)[0]; + Mat1f err_direct = pos_cdf + neg_total - neg_cdf; + Mat1f err_backward = 1.0f - err_direct; + + int idx1[2], idx2[2]; + double err1, err2; + minMaxIdx(err_direct, &err1, NULL, idx1); + minMaxIdx(err_backward, &err2, NULL, idx2); +//#pragma omp critical + { + if (min(err1, err2) < min_err) { + if (err1 < err2) { + min_err = err1; + min_polarity = +1; + threshold_q = idx1[1]; + } else { + min_err = err2; + min_polarity = -1; + threshold_q = idx2[1]; + } + min_feature_ind = feat_i; + if (quantize) { + min_threshold = data_min(feat_i, 0) + data_step(feat_i, 0) * + (threshold_q + .5f); + } else { + min_threshold = threshold_q + .5f; + } + } + } } - // set to zero row for feature in data - for(int jc = 0; jc(feature_ind, jc) = 0; - } - visited_features.push_back(feature_ind); + float alpha = .5f * log((1 - min_err) / min_err); + alphas_.push_back(alpha); + feature_indices_.push_back(min_feature_ind); + thresholds_.push_back(min_threshold); + polarities_.push_back(min_polarity); + feature_ignore[min_feature_ind] = true; + cascade_thresholds_.push_back(-1); - - // Normalize weights - float z = (float)sum(weights)[0]; - for( int col = 0; col < weights.cols; ++col) - { - weights.at(0, col) /= z; + double loss = 0; + // Update positive weights + for (int j = 0; j < data_pos.cols; ++j) { + int val = data_pos.at(min_feature_ind, j); + int label = min_polarity * (val - threshold_q) >= 0 ? +1 : -1; + pos_weights(0, j) *= exp(-alpha * label); + pos_trace(0, j) += alpha * label; + loss += exp(-pos_trace(0, j)) / (2.0f * data_pos.cols); } - // Sort trace - Mat indices; - sortIdx(trace, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - Mat new_weights = Mat_::zeros(weights.rows, weights.cols); - Mat new_labels = Mat_::zeros(labels.rows, labels.cols); - Mat new_trace; - for( int col = 0; col < new_weights.cols; ++col ) - { - new_weights.at(0, col) = - weights.at(0, indices.at(0, col)); - new_labels.at(0, col) = - labels.at(0, indices.at(0, col)); + // Update negative weights + for (int j = 0; j < data_neg.cols; ++j) { + int val = data_neg.at(min_feature_ind, j); + int label = min_polarity * (val - threshold_q) >= 0 ? +1 : -1; + neg_weights(0, j) *= exp(alpha * label); + neg_trace(0, j) += alpha * label; + loss += exp(+neg_trace(0, j)) / (2.0f * data_neg.cols); } - - //sort in-place to save memory - sort_columns_without_copy(data, indices); - sort(trace, new_trace, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); + std::cerr << "i=" << std::setw(4) << i; + std::cerr << " feat=" << std::setw(5) << min_feature_ind; + std::cerr << " thr=" << std::setw(3) << threshold_q; + std::cerr << " alpha=" << std::fixed << std::setprecision(3) + << alpha << " err=" << std::fixed << std::setprecision(3) << min_err + << " loss=" << std::scientific << loss << std::endl; - // Compute threshold for trace - /* - int col = 0; - for( int pos_i = 0; - pos_i < pos_count * params_.alpha && col < new_labels.cols; - ++col ) - { - if( new_labels.at(0, col) == +1 ) - ++pos_i; - } - */ - - int cur_pos = 0, cur_neg = 0; - int max_col = 0; - for( int col = 0; col < new_labels.cols - 1; ++col ) { - if (new_labels.at(0, col) == +1 ) - ++cur_pos; - else - ++cur_neg; - - float p_neg = cur_neg / (float)neg_count; - float p_pos = cur_pos / (float)pos_count; - if( params_.alpha * p_neg > p_pos ) - max_col = col; - } - - thresholds_.push_back(new_trace.at(0, max_col)); - - // Drop samples below threshold - //uses Rois instead of copyTo to save memory - data = data(Rect(max_col, 0, data.cols - max_col, data.rows)); - new_trace.colRange(max_col, new_trace.cols).copyTo(trace); - new_weights.colRange(max_col, new_weights.cols).copyTo(weights); - new_labels.colRange(max_col, new_labels.cols).copyTo(labels); - - pos_count = count(labels, +1); - neg_count = count(labels, -1); - - if( data.cols < 2 || neg_count == 0) - { + if (loss < 1e-50 || min_err > 0.5) { + std::cerr << "Stopping early" << std::endl; + weak_count_ = i + 1; break; } + + // Normalize weights + double z = (sum(pos_weights) + sum(neg_weights))[0]; + pos_weights /= z; + neg_weights /= z; } - return feature_indices; } -float WaldBoostImpl::predict( - const Ptr& feature_evaluator) const +int WaldBoost::predict(Ptr eval, float *h) const { - float trace = 0; - CV_Assert(stumps_.size() == thresholds_.size()); - for( size_t i = 0; i < stumps_.size(); ++i ) - { - int value = feature_evaluator->evaluate(i); - trace += stumps_[i].predict(value); - - if( trace < thresholds_[i] ) + double THR = -6; + assert(feature_indices_.size() == size_t(weak_count_)); + float res = 0; + for (int i = 0; i < weak_count_; ++i) { + float val = (*eval)(feature_indices_[i], 0); + int label = polarities_[i] * (val - thresholds_[i]) > 0 ? +1: -1; + res += alphas_[i] * label; + if (res < THR) { return -1; + } } - return trace; + *h = res; + return res > THR ? +1 : -1; } -Ptr -createWaldBoost(const WaldBoostParams& params) +void WaldBoost::save(const std::string& filename) { - return Ptr(new WaldBoostImpl(params)); + std::ofstream f(filename.c_str()); + f << weak_count_ << std::endl; + for (size_t i = 0; i < thresholds_.size(); ++i) { + f << thresholds_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < alphas_.size(); ++i) { + f << alphas_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < polarities_.size(); ++i) { + f << polarities_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < cascade_thresholds_.size(); ++i) { + f << cascade_thresholds_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < feature_indices_.size(); ++i) { + f << feature_indices_[i] << " "; + } + f << std::endl; } +void WaldBoost::load(const std::string& filename) +{ + std::ifstream f(filename.c_str()); + f >> weak_count_; + thresholds_.resize(weak_count_); + alphas_.resize(weak_count_); + polarities_.resize(weak_count_); + cascade_thresholds_.resize(weak_count_); + feature_indices_.resize(weak_count_); -} /* namespace xobjdetect */ -} /* namespace cv */ + for (int i = 0; i < weak_count_; ++i) { + f >> thresholds_[i]; + } + for (int i = 0; i < weak_count_; ++i) { + f >> alphas_[i]; + } + for (int i = 0; i < weak_count_; ++i) { + f >> polarities_[i]; + } + for (int i = 0; i < weak_count_; ++i) { + f >> cascade_thresholds_[i]; + } + for (int i = 0; i < weak_count_; ++i) { + f >> feature_indices_[i]; + } +} + +void WaldBoost::reset(int weak_count) +{ + weak_count_ = weak_count; + thresholds_.clear(); + alphas_.clear(); + feature_indices_.clear(); + polarities_.clear(); + cascade_thresholds_.clear(); +} + +WaldBoost::~WaldBoost() +{ +} + +}; diff --git a/modules/xobjdetect/src/waldboost.hpp b/modules/xobjdetect/src/waldboost.hpp new file mode 100644 index 000000000..4e9aaa6e6 --- /dev/null +++ b/modules/xobjdetect/src/waldboost.hpp @@ -0,0 +1,88 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#ifndef __OPENCV_XOBJDETECT_WALDBOOST_HPP__ +#define __OPENCV_XOBJDETECT_WALDBOOST_HPP__ + +#include "precomp.hpp" + +namespace cv { + +class WaldBoost { +public: + WaldBoost(int weak_count); + WaldBoost(); + std::vector get_feature_indices(); + + void detect(Ptr eval, + const Mat& img, + const std::vector& scales, + std::vector& bboxes, + Mat1f& confidences); + + void detect(Ptr eval, + const Mat& img, + const std::vector& scales, + std::vector& bboxes, + std::vector& confidences); + + void fit(Mat& data_pos, Mat& data_neg); + int predict(Ptr eval, float *h) const; + void save(const std::string& filename); + void load(const std::string& filename); + void reset(int weak_count); + ~WaldBoost(); + +private: + int weak_count_; + std::vector thresholds_; + std::vector alphas_; + std::vector feature_indices_; + std::vector polarities_; + std::vector cascade_thresholds_; +}; + +} + +#endif diff --git a/modules/xobjdetect/tools/CMakeLists.txt b/modules/xobjdetect/tools/CMakeLists.txt new file mode 100644 index 000000000..5c40af12e --- /dev/null +++ b/modules/xobjdetect/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(waldboost_detector) diff --git a/modules/adas/tools/fcw_train/CMakeLists.txt b/modules/xobjdetect/tools/waldboost_detector/CMakeLists.txt similarity index 97% rename from modules/adas/tools/fcw_train/CMakeLists.txt rename to modules/xobjdetect/tools/waldboost_detector/CMakeLists.txt index 5b1d78303..5f34f1e71 100644 --- a/modules/adas/tools/fcw_train/CMakeLists.txt +++ b/modules/xobjdetect/tools/waldboost_detector/CMakeLists.txt @@ -1,4 +1,4 @@ -set(name fcw_train) +set(name waldboost_detector) set(the_target opencv_${name}) set(OPENCV_${the_target}_DEPS opencv_core opencv_imgcodecs opencv_videoio diff --git a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp new file mode 100644 index 000000000..f0bbf87fb --- /dev/null +++ b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +using namespace std; +using namespace cv; +using namespace cv::xobjdetect; + +int main(int argc, char **argv) +{ + if (argc < 5) { + cerr << "Usage: " << argv[0] << " train " << endl; + cerr << " " << argv[0] << " detect " << endl; + return 0; + } + + string mode = argv[1]; + WBDetector detector(argv[2]); + if (mode == "train") { + detector.train(argv[3], argv[4]); + } else if (mode == "detect") { + cerr << "detect" << endl; + vector bboxes; + vector confidences; + Mat img = imread(argv[3], CV_LOAD_IMAGE_GRAYSCALE); + detector.detect(img, bboxes, confidences); + for (size_t i = 0; i < bboxes.size(); ++i) { + rectangle(img, bboxes[i], Scalar(255, 0, 0)); + } + imwrite(argv[4], img); + } +} +