Bomb commit

pull/40/head
Vlad Shakhuro 11 years ago
parent 9e5b668480
commit 319b25ee85
  1. 35
      modules/adas/tools/CMakeLists.txt
  2. 34
      modules/adas/tools/fcw_detect/CMakeLists.txt
  3. 92
      modules/adas/tools/fcw_detect/fcw_detect.cpp
  4. 195
      modules/adas/tools/fcw_train.cpp
  5. 34
      modules/adas/tools/fcw_train/CMakeLists.txt
  6. 100
      modules/adas/tools/fcw_train/fcw_train.cpp
  7. 70
      modules/xobjdetect/include/opencv2/xobjdetect.hpp
  8. 24
      modules/xobjdetect/include/opencv2/xobjdetect/private.hpp
  9. 81
      modules/xobjdetect/src/acffeature.cpp
  10. 196
      modules/xobjdetect/src/icfdetector.cpp
  11. 2
      modules/xobjdetect/src/precomp.hpp
  12. 15
      modules/xobjdetect/src/stump.cpp
  13. 169
      modules/xobjdetect/src/waldboost.cpp

@ -1,33 +1,2 @@
set(name fcw_train) add_subdirectory(fcw_train)
set(the_target opencv_${name}) add_subdirectory(fcw_detect)
set(the_module opencv_adas)
ocv_check_dependencies(${OPENCV_MODULE_${the_module}_DEPS})
if(NOT OCV_DEPENDENCIES_FOUND)
return()
endif()
project(${the_target})
ocv_include_directories("${OpenCV_SOURCE_DIR}/include/opencv")
ocv_include_modules(${OPENCV_MODULE_${the_module}_DEPS})
file(GLOB ${the_target}_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
add_executable(${the_target} ${${the_target}_SOURCES})
ocv_target_link_libraries(${the_target} ${OPENCV_MODULE_${the_module}_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} RUNTIME DESTINATION bin COMPONENT main)

@ -0,0 +1,34 @@
set(name fcw_detect)
set(the_target opencv_${name})
set(OPENCV_${the_target}_DEPS 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(${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} RUNTIME DESTINATION bin COMPONENT main)

@ -0,0 +1,92 @@
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <iostream>
using std::cerr;
using std::endl;
#include <opencv2/core.hpp>
using cv::Rect;
using cv::Size;
using cv::Mat;
using cv::Mat_;
using cv::Vec3b;
#include <opencv2/highgui.hpp>
using cv::imread;
using cv::imwrite;
#include <opencv2/core/utility.hpp>
using cv::CommandLineParser;
using cv::FileStorage;
#include <opencv2/xobjdetect.hpp>
using cv::xobjdetect::ICFDetector;
static Mat visualize(const Mat &image, const vector<Rect> &objects)
{
CV_Assert(image.type() == CV_8UC3);
Mat_<Vec3b> 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;
}
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}"
;
CommandLineParser parser(argc, argv, keys);
parser.about("FCW detection");
if( parser.has("help") || argc == 1)
{
parser.printMessage();
return 0;
}
string model_filename = parser.get<string>("model_filename");
string image_path = parser.get<string>("image_path");
string out_image_path = parser.get<string>("out_image_path");
float threshold = parser.get<float>("threshold");
if( !parser.check() )
{
parser.printErrors();
return 1;
}
ICFDetector detector;
FileStorage fs(model_filename, FileStorage::READ);
fs["icfdetector"] >> detector;
fs.release();
vector<Rect> objects;
Mat img = imread(image_path);
detector.detect(img, objects, 1.1, Size(40, 40),
Size(300, 300), threshold);
imwrite(out_image_path, visualize(img, objects));
}

@ -1,195 +0,0 @@
#include <cstdio>
#include <cstring>
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <fstream>
using std::ifstream;
using std::getline;
#include <sstream>
using std::stringstream;
#include <iostream>
using std::cerr;
using std::endl;
#include <opencv2/core.hpp>
using cv::Rect;
#include <opencv2/xobjdetect.hpp>
using cv::xobjdetect::ICFDetectorParams;
using cv::xobjdetect::ICFDetector;
using cv::xobjdetect::WaldBoost;
using cv::xobjdetect::WaldBoostParams;
using cv::Mat;
static bool read_pos_int(const char *str, int *n)
{
int pos = 0;
if( sscanf(str, "%d%n", n, &pos) != 1 || str[pos] != '\0' || *n <= 0 )
{
return false;
}
return true;
}
static bool read_model_size(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 bool read_overlap(const char *str, double *overlap)
{
int pos = 0;
if( sscanf(str, "%lf%n", overlap, &pos) != 1 || str[pos] != '\0' ||
*overlap < 0 || *overlap > 1)
{
return false;
}
return true;
}
static bool read_labels(const string& path,
vector<string>& filenames, vector< vector<Rect> >& labels)
{
string labels_path = path + "/gt.txt";
string filename, line;
int x1, y1, x2, y2;
char delim;
ifstream ifs(labels_path.c_str());
if( !ifs.good() )
return false;
while( getline(ifs, line) )
{
stringstream stream(line);
stream >> filename;
filenames.push_back(path + "/" + filename);
vector<Rect> filename_labels;
while( stream >> x1 >> y1 >> x2 >> y2 >> delim )
{
filename_labels.push_back(Rect(x1, y1, x2, y2));
}
labels.push_back(filename_labels);
filename_labels.clear();
}
return true;
}
int main(int argc, char *argv[])
{
if( argc == 1 )
{
printf("Usage: %s OPTIONS, where OPTIONS are:\n"
"\n"
"--path <path> - path to dir with data and labels\n"
" (labels should have name gt.txt)\n"
"\n"
"--feature_count <count> - number of features to generate\n"
"\n"
"--weak_count <count> - number of weak classifiers in cascade\n"
"\n"
"--model_size <rowsxcols> - model size in pixels\n"
"\n"
"--overlap <measure> - number from [0, 1], means maximum\n"
" overlap with objects while sampling background\n"
"\n"
"--model_filename <path> - filename for saving model\n",
argv[0]);
return 0;
}
string path, model_path;
ICFDetectorParams params;
for( int i = 1; i < argc; ++i )
{
if( !strcmp("--path", argv[i]) )
{
i += 1;
path = argv[i];
}
else if( !strcmp("--feature_count", argv[i]) )
{
i += 1;
if( !read_pos_int(argv[i], &params.feature_count) )
{
fprintf(stderr, "Error reading feature count from `%s`\n",
argv[i]);
return 1;
}
}
else if( !strcmp("--weak_count", argv[i]) )
{
i += 1;
if( !read_pos_int(argv[i], &params.weak_count) )
{
fprintf(stderr, "Error reading weak count from `%s`\n",
argv[i]);
return 1;
}
}
else if( !strcmp("--model_size", argv[i]) )
{
i += 1;
if( !read_model_size(argv[i], &params.model_n_rows,
&params.model_n_cols) )
{
fprintf(stderr, "Error reading model size from `%s`\n",
argv[i]);
return 1;
}
}
else if( !strcmp("--overlap", argv[i]) )
{
i += 1;
if( !read_overlap(argv[i], &params.overlap) )
{
fprintf(stderr, "Error reading overlap from `%s`\n",
argv[i]);
return 1;
}
}
else if( !strcmp("--model_filename", argv[i]) )
{
i += 1;
model_path = argv[i];
}
else
{
fprintf(stderr, "Error: unknown argument `%s`\n", argv[i]);
return 1;
}
}
try
{
ICFDetector detector;
vector<string> filenames;
vector< vector<Rect> > labels;
read_labels(path, filenames, labels);
detector.train(filenames, labels, params);
}
catch( const char *err )
{
cerr << err << endl;
}
catch( ... )
{
cerr << "Unknown error\n" << endl;
}
}

@ -0,0 +1,34 @@
set(name fcw_train)
set(the_target opencv_${name})
set(OPENCV_${the_target}_DEPS 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(${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} RUNTIME DESTINATION bin COMPONENT main)

@ -0,0 +1,100 @@
#include <cstdio>
#include <cstring>
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <fstream>
using std::ifstream;
using std::getline;
#include <sstream>
using std::stringstream;
#include <iostream>
using std::cerr;
using std::endl;
#include <opencv2/core.hpp>
using cv::Rect;
using cv::Size;
#include <opencv2/highgui.hpp>
using cv::imread;
#include <opencv2/core/utility.hpp>
using cv::CommandLineParser;
using cv::FileStorage;
#include <opencv2/xobjdetect.hpp>
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;
}
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}"
"{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}"
;
CommandLineParser parser(argc, argv, keys);
parser.about("FCW trainer");
if( parser.has("help") || argc == 1)
{
parser.printMessage();
return 0;
}
string pos_path = parser.get<string>("pos_path");
string bg_path = parser.get<string>("bg_path");
string model_filename = parser.get<string>("model_filename");
cerr << pos_path << endl;
cerr << bg_path << endl;
ICFDetectorParams params;
params.feature_count = parser.get<size_t>("feature_count");
params.weak_count = parser.get<size_t>("weak_count");
string model_size = parser.get<string>("model_size");
if( !read_model_size(model_size.c_str(), &params.model_n_rows,
&params.model_n_cols) )
{
cerr << "Error reading model size from `" << model_size << "`" << endl;
return 1;
}
if( !parser.check() )
{
parser.printErrors();
return 1;
}
ICFDetector detector;
detector.train(pos_path, bg_path, params);
FileStorage fs(model_filename, FileStorage::WRITE);
fs << "icfdetector" << detector;
fs.release();
}

@ -58,7 +58,7 @@ namespace xobjdetect
channels output array for computed channels channels output array for computed channels
*/ */
void computeChannels(InputArray image, OutputArrayOfArrays channels); void computeChannels(InputArray image, std::vector<Mat>& channels);
class CV_EXPORTS ACFFeatureEvaluator : public Algorithm class CV_EXPORTS ACFFeatureEvaluator : public Algorithm
{ {
@ -69,6 +69,8 @@ public:
/* Set window position */ /* Set window position */
virtual void setPosition(Size position) = 0; virtual void setPosition(Size position) = 0;
virtual void assertChannels() = 0;
/* Evaluate feature with given index for current channels /* Evaluate feature with given index for current channels
and window position */ and window position */
virtual int evaluate(size_t feature_ind) const = 0; virtual int evaluate(size_t feature_ind) const = 0;
@ -103,7 +105,7 @@ struct CV_EXPORTS WaldBoostParams
int weak_count; int weak_count;
float alpha; float alpha;
WaldBoostParams(): weak_count(100), alpha(0.01f) WaldBoostParams(): weak_count(100), alpha(0.02f)
{} {}
}; };
@ -122,8 +124,8 @@ public:
Returns feature indices chosen for cascade. Returns feature indices chosen for cascade.
Feature enumeration starts from 0 Feature enumeration starts from 0
*/ */
virtual std::vector<int> train(const Mat& data, virtual std::vector<int> train(const Mat& /*data*/,
const Mat& labels) = 0; const Mat& /*labels*/) {return std::vector<int>();}
/* Predict object class given object that can compute object features /* Predict object class given object that can compute object features
@ -133,10 +135,21 @@ public:
is from class +1 is from class +1
*/ */
virtual float predict( virtual float predict(
const Ptr<ACFFeatureEvaluator>& feature_evaluator) const = 0; const Ptr<ACFFeatureEvaluator>& /*feature_evaluator*/) const
{return 0.0f;}
/* Write WaldBoost to FileStorage */
virtual void write(FileStorage& /*fs*/) const {}
/* Read WaldBoost */
virtual void read(const FileNode& /*node*/) {}
}; };
void write(FileStorage& fs, String&, const WaldBoost& waldboost);
void read(const FileNode& node, WaldBoost& w,
const WaldBoost& default_value = WaldBoost());
CV_EXPORTS Ptr<WaldBoost> CV_EXPORTS Ptr<WaldBoost>
createWaldBoost(const WaldBoostParams& params = WaldBoostParams()); createWaldBoost(const WaldBoostParams& params = WaldBoostParams());
@ -146,32 +159,63 @@ struct CV_EXPORTS ICFDetectorParams
int weak_count; int weak_count;
int model_n_rows; int model_n_rows;
int model_n_cols; int model_n_cols;
double overlap;
ICFDetectorParams(): feature_count(UINT_MAX), weak_count(100), ICFDetectorParams(): feature_count(UINT_MAX), weak_count(100),
model_n_rows(40), model_n_cols(40), overlap(0.0) model_n_rows(40), model_n_cols(40)
{} {}
}; };
class CV_EXPORTS ICFDetector class CV_EXPORTS ICFDetector
{ {
public: public:
ICFDetector(): waldboost_(), features_() {}
/* Train detector /* Train detector
image_filenames filenames of images for training pos_path path to folder with images of objects
labelling vector of object bounding boxes per every image bg_path path to folder with background images
params parameters for detector training params parameters for detector training
*/ */
void train(const std::vector<std::string>& image_filenames, void train(const String& pos_path,
const std::vector<std::vector<cv::Rect> >& labelling, const String& bg_path,
ICFDetectorParams params = ICFDetectorParams()); ICFDetectorParams params = ICFDetectorParams());
/* Save detector in file, return true on success, false otherwise */ /* Detect object on image
bool save(const std::string& filename);
image image for detection
object output array of bounding boxes
scaleFactor scale between layers in detection pyramid
minSize min size of objects in pixels
maxSize max size of objects in pixels
*/
void detect(const Mat& image, std::vector<Rect>& objects,
double scaleFactor, Size minSize, Size maxSize, float threshold);
/* Write detector to FileStorage */
void write(FileStorage &fs) const;
/* Read detector */
void read(const FileNode &node);
private:
Ptr<WaldBoost> waldboost_;
std::vector<Point3i> features_;
int model_n_rows_;
int model_n_cols_;
}; };
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 xobjdetect */
} /* namespace cv */ } /* namespace cv */

@ -47,6 +47,26 @@ public:
*/ */
float predict(int value) const; 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: private:
/* Stump decision threshold */ /* Stump decision threshold */
int threshold_; int threshold_;
@ -56,6 +76,10 @@ private:
float pos_value_, neg_value_; 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 xobjdetect */
} /* namespace cv */ } /* namespace cv */

@ -64,6 +64,7 @@ public:
} }
virtual void setChannels(InputArrayOfArrays channels); virtual void setChannels(InputArrayOfArrays channels);
virtual void assertChannels();
virtual void setPosition(Size position); virtual void setPosition(Size position);
virtual int evaluate(size_t feature_ind) const; virtual int evaluate(size_t feature_ind) const;
virtual void evaluateAll(OutputArray feature_values) const; virtual void evaluateAll(OutputArray feature_values) const;
@ -77,16 +78,56 @@ private:
Size position_; Size position_;
}; };
static bool isNull(const Mat_<int> &m)
{
bool null_data = true;
for( int row = 0; row < m.rows; ++row )
{
for( int col = 0; col < m.cols; ++col )
if( m.at<int>(row, col) )
null_data = false;
}
return null_data;
}
void ACFFeatureEvaluatorImpl::assertChannels()
{
bool null_data = true;
for( size_t i = 0; i < channels_.size(); ++i )
null_data &= isNull(channels_[i]);
CV_Assert(!null_data);
}
void ACFFeatureEvaluatorImpl::setChannels(cv::InputArrayOfArrays channels) void ACFFeatureEvaluatorImpl::setChannels(cv::InputArrayOfArrays channels)
{ {
channels_.clear(); channels_.clear();
vector<Mat> ch; vector<Mat> ch;
channels.getMatVector(ch); channels.getMatVector(ch);
CV_Assert(ch.size() == 10); CV_Assert(ch.size() == 10);
/*int min_val = 100500, max_val = -1;
for( size_t i = 0; i < ch.size(); ++i )
{
const Mat &channel = ch[i];
for( int row = 0; row < channel.rows; ++row )
for( int col = 0; col < channel.cols; ++col )
{
int val = (int)channel.at<float>(row, col);
if( val < min_val )
min_val = val;
else if( val > max_val )
max_val = val;
}
}
cout << "SET " << min_val << " " << max_val << endl;
*/
for( size_t i = 0; i < ch.size(); ++i ) for( size_t i = 0; i < ch.size(); ++i )
{ {
const Mat &channel = ch[i]; const Mat &channel = ch[i];
Mat_<int> acf_channel(channel.rows / 4, channel.cols / 4); Mat_<int> acf_channel = Mat_<int>::zeros(channel.rows / 4, channel.cols / 4);
for( int row = 0; row < channel.rows; row += 4 ) for( int row = 0; row < channel.rows; row += 4 )
{ {
for( int col = 0; col < channel.cols; col += 4 ) for( int col = 0; col < channel.cols; col += 4 )
@ -94,18 +135,23 @@ void ACFFeatureEvaluatorImpl::setChannels(cv::InputArrayOfArrays channels)
int sum = 0; int sum = 0;
for( int cell_row = row; cell_row < row + 4; ++cell_row ) for( int cell_row = row; cell_row < row + 4; ++cell_row )
for( int cell_col = col; cell_col < col + 4; ++cell_col ) for( int cell_col = col; cell_col < col + 4; ++cell_col )
{
//cout << channel.rows << " " << channel.cols << endl;
//cout << cell_row << " " << cell_col << endl;
sum += (int)channel.at<float>(cell_row, cell_col); sum += (int)channel.at<float>(cell_row, cell_col);
}
acf_channel(row / 4, col / 4) = sum; acf_channel(row / 4, col / 4) = sum;
} }
} }
channels_.push_back(acf_channel);
channels_.push_back(acf_channel.clone());
} }
} }
void ACFFeatureEvaluatorImpl::setPosition(Size position) void ACFFeatureEvaluatorImpl::setPosition(Size position)
{ {
position_ = position; position_ = Size(position.width / 4, position.height / 4);
} }
int ACFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const int ACFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const
@ -117,7 +163,7 @@ int ACFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const
int x = feature.x; int x = feature.x;
int y = feature.y; int y = feature.y;
int n = feature.z; int n = feature.z;
return channels_[n].at<int>(y, x); return channels_[n].at<int>(y + position_.width, x + position_.height);
} }
void ACFFeatureEvaluatorImpl::evaluateAll(OutputArray feature_values) const void ACFFeatureEvaluatorImpl::evaluateAll(OutputArray feature_values) const
@ -127,7 +173,7 @@ void ACFFeatureEvaluatorImpl::evaluateAll(OutputArray feature_values) const
{ {
feature_vals(0, i) = evaluate(i); feature_vals(0, i) = evaluate(i);
} }
feature_values.setTo(feature_vals); feature_values.assign(feature_vals);
} }
Ptr<ACFFeatureEvaluator> Ptr<ACFFeatureEvaluator>
@ -159,14 +205,15 @@ vector<Point3i> generateFeatures(Size window_size, int count)
return features; return features;
} }
void computeChannels(cv::InputArray image, cv::OutputArrayOfArrays channels_) void computeChannels(InputArray image, vector<Mat>& channels)
{ {
Mat src(image.getMat().rows, image.getMat().cols, CV_32FC3); Mat src(image.getMat().rows, image.getMat().cols, CV_32FC3);
image.getMat().convertTo(src, CV_32FC3, 1./255); image.getMat().convertTo(src, CV_32FC3, 1./255);
Mat_<float> grad; Mat_<float> grad;
Mat gray; Mat luv, gray;
cvtColor(src, gray, CV_RGB2GRAY); cvtColor(src, gray, CV_RGB2GRAY);
cvtColor(src, luv, CV_RGB2Luv);
Mat_<float> row_der, col_der; Mat_<float> row_der, col_der;
Sobel(gray, row_der, CV_32F, 0, 1); Sobel(gray, row_der, CV_32F, 0, 1);
@ -174,7 +221,7 @@ void computeChannels(cv::InputArray image, cv::OutputArrayOfArrays channels_)
magnitude(row_der, col_der, grad); magnitude(row_der, col_der, grad);
Mat_<Vec6f> hist(grad.rows, grad.cols); Mat_<Vec6f> hist = Mat_<Vec6f>::zeros(grad.rows, grad.cols);
const float to_deg = 180 / 3.1415926f; const float to_deg = 180 / 3.1415926f;
for (int row = 0; row < grad.rows; ++row) { for (int row = 0; row < grad.rows; ++row) {
for (int col = 0; col < grad.cols; ++col) { for (int col = 0; col < grad.cols; ++col) {
@ -182,12 +229,22 @@ void computeChannels(cv::InputArray image, cv::OutputArrayOfArrays channels_)
if (angle < 0) if (angle < 0)
angle += 180; angle += 180;
int ind = (int)(angle / 30); int ind = (int)(angle / 30);
hist(row, col)[ind] = grad(row, col);
// If angle == 180, prevent index overflow
if (ind == 6)
ind = 5;
hist(row, col)[ind] = grad(row, col) * 255;
} }
} }
vector<Mat> channels; channels.clear();
channels.push_back(gray);
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); channels.push_back(grad);
vector<Mat> hist_channels; vector<Mat> hist_channels;
@ -195,8 +252,6 @@ void computeChannels(cv::InputArray image, cv::OutputArrayOfArrays channels_)
for( size_t i = 0; i < hist_channels.size(); ++i ) for( size_t i = 0; i < hist_channels.size(); ++i )
channels.push_back(hist_channels[i]); channels.push_back(hist_channels[i]);
channels_.setTo(channels);
} }
} /* namespace xobjdetect */ } /* namespace xobjdetect */

@ -39,6 +39,17 @@ the use of this software, even if advised of the possibility of such damage.
*/ */
#include <sstream>
using std::ostringstream;
#include <iomanip>
using std::setfill;
using std::setw;
#include <iostream>
using std::cout;
using std::endl;
#include "precomp.hpp" #include "precomp.hpp"
using std::vector; using std::vector;
@ -52,64 +63,54 @@ namespace cv
namespace xobjdetect namespace xobjdetect
{ {
static bool overlap(const Rect& r, const vector<Rect>& gt) void ICFDetector::train(const String& pos_path,
{ const String& bg_path,
for( size_t i = 0; i < gt.size(); ++i )
if( (r & gt[i]).area() )
return true;
return false;
}
void ICFDetector::train(const vector<string>& image_filenames,
const vector< vector<Rect> >& labelling,
ICFDetectorParams params) ICFDetectorParams params)
{ {
vector<String> pos_filenames;
glob(pos_path + "/*.png", pos_filenames);
vector<String> bg_filenames;
glob(bg_path + "/*.png", bg_filenames);
model_n_rows_ = params.model_n_rows;
model_n_cols_ = params.model_n_cols;
Size model_size(params.model_n_cols, params.model_n_rows); Size model_size(params.model_n_cols, params.model_n_rows);
vector<Mat> samples; /* positive samples + negative samples */ vector<Mat> samples; /* positive samples + negative samples */
Mat sample, resized_sample; Mat sample, resized_sample;
int pos_count = 0; int pos_count = 0;
for( size_t i = 0; i < image_filenames.size(); ++i, ++pos_count )
{
Mat img = imread(String(image_filenames[i].c_str()));
for( size_t j = 0; j < labelling[i].size(); ++j )
{
Rect r = labelling[i][j];
if( r.x > img.cols || r.y > img.rows )
continue;
sample = img.colRange(max(r.x, 0), min(r.width, img.cols)) for( size_t i = 0; i < pos_filenames.size(); ++i, ++pos_count )
.rowRange(max(r.y, 0), min(r.height, img.rows)); {
cout << setw(6) << (i + 1) << "/" << pos_filenames.size() << "\r";
resize(sample, resized_sample, model_size); Mat img = imread(pos_filenames[i]);
resize(img, resized_sample, model_size);
samples.push_back(resized_sample); samples.push_back(resized_sample.clone());
}
} }
cout << "\n";
int neg_count = 0; int neg_count = 0;
RNG rng; RNG rng;
for( size_t i = 0; i < image_filenames.size(); ++i ) for( size_t i = 0; i < bg_filenames.size(); ++i )
{ {
Mat img = imread(String(image_filenames[i].c_str())); cout << setw(6) << (i + 1) << "/" << bg_filenames.size() << "\r";
for( int j = 0; j < (int)(pos_count / image_filenames.size() + 1); ) Mat img = imread(bg_filenames[i]);
for( int j = 0; j < 50;
++j, ++neg_count)
{ {
Rect r; Rect r;
r.x = rng.uniform(0, img.cols); r.x = rng.uniform(0, img.cols); r.width = rng.uniform(r.x + 1, img.cols);
r.width = rng.uniform(r.x + 1, img.cols);
r.y = rng.uniform(0, img.rows); r.y = rng.uniform(0, img.rows);
r.height = rng.uniform(r.y + 1, img.rows); r.height = rng.uniform(r.y + 1, img.rows);
if( !overlap(r, labelling[i]) ) sample = img.colRange(r.x, r.width).rowRange(r.y, r.height);
{ resize(sample, resized_sample, model_size);
sample = img.colRange(r.x, r.width).rowRange(r.y, r.height); samples.push_back(resized_sample.clone());
//resize(sample, resized_sample);
samples.push_back(resized_sample);
++neg_count;
++j;
}
} }
} }
cout << "\n";
Mat_<int> labels(1, pos_count + neg_count); Mat_<int> labels(1, pos_count + neg_count);
for( int i = 0; i < pos_count; ++i) for( int i = 0; i < pos_count; ++i)
@ -120,30 +121,131 @@ void ICFDetector::train(const vector<string>& image_filenames,
vector<Point3i> features = generateFeatures(model_size); vector<Point3i> features = generateFeatures(model_size);
Ptr<ACFFeatureEvaluator> feature_evaluator = createACFFeatureEvaluator(features); Ptr<ACFFeatureEvaluator> feature_evaluator = createACFFeatureEvaluator(features);
Mat_<int> data((int)features.size(), (int)samples.size()); Mat_<int> data = Mat_<int>::zeros((int)features.size(), (int)samples.size());
Mat_<int> feature_col; Mat_<int> feature_col(1, (int)samples.size());
vector<Mat> channels; vector<Mat> channels;
for( int i = 0; i < (int)samples.size(); ++i ) for( int i = 0; i < (int)samples.size(); ++i )
{ {
cout << setw(6) << i << "/" << samples.size() << "\r";
computeChannels(samples[i], channels); computeChannels(samples[i], channels);
feature_evaluator->setChannels(channels); feature_evaluator->setChannels(channels);
//feature_evaluator->assertChannels();
feature_evaluator->evaluateAll(feature_col); feature_evaluator->evaluateAll(feature_col);
for( int j = 0; j < feature_col.rows; ++j )
data(i, j) = feature_col(0, j); 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; WaldBoostParams wparams;
wparams.weak_count = params.weak_count; wparams.weak_count = params.weak_count;
wparams.alpha = 0.001f; wparams.alpha = 0.02f;
waldboost_ = createWaldBoost(wparams);
vector<int> indices = waldboost_->train(data, labels);
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]]);
}
Ptr<WaldBoost> waldboost = createWaldBoost(wparams); void ICFDetector::write(FileStorage& fs) const
waldboost->train(data, labels); {
fs << "{";
fs << "model_n_rows" << model_n_rows_;
fs << "model_n_cols" << model_n_cols_;
fs << "waldboost" << *waldboost_;
fs << "features" << "[";
for( size_t i = 0; i < features_.size(); ++i )
{
fs << features_[i];
}
fs << "]";
fs << "}";
}
void ICFDetector::read(const FileNode& node)
{
waldboost_ = Ptr<WaldBoost>(createWaldBoost(WaldBoostParams()));
node["model_n_rows"] >> model_n_rows_;
node["model_n_cols"] >> model_n_cols_;
node["waldboost"] >> *waldboost_;
FileNode features = node["features"];
features_.clear();
Point3i p;
for( FileNodeIterator n = features.begin(); n != features.end(); ++n )
{
(*n) >> p;
features_.push_back(p);
}
}
void ICFDetector::detect(const Mat& img, vector<Rect>& objects,
double scaleFactor, Size minSize, Size maxSize, float threshold)
{
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<ACFFeatureEvaluator> evaluator = createACFFeatureEvaluator(features_);
Mat rescaled_image;
int step = 8;
vector<Mat> channels;
for( float scale = scale_from; scale < scale_to + 0.001; scale *= scaleFactor )
{
cout << "scale " << scale << endl;
int new_width = img.cols * scale;
new_width -= new_width % 4;
int new_height = 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 += step)
{
for( int col = 0; col <= rescaled_image.cols - model_n_cols_;
col += step )
{
evaluator->setPosition(Size(row, col));
float value = waldboost_->predict(evaluator);
if( value > threshold )
{
int x = (int)(col / scale);
int y = (int)(row / scale);
int width = (int)(model_n_cols_ / scale);
int height = (int)(model_n_rows_ / scale);
cout << value << " " << x << " " << y << " " << width << " "
<< height << endl;
objects.push_back(Rect(x, y, width, height));
}
}
}
}
}
void write(FileStorage& fs, String&, const ICFDetector& detector)
{
detector.write(fs);
} }
bool ICFDetector::save(const string&) void read(const FileNode& node, ICFDetector& d,
const ICFDetector& default_value)
{ {
return true; if( node.empty() )
d = default_value;
else
d.read(node);
} }
} /* namespace xobjdetect */ } /* namespace xobjdetect */

@ -45,6 +45,8 @@ the use of this software, even if advised of the possibility of such damage.
#include <opencv2/xobjdetect.hpp> #include <opencv2/xobjdetect.hpp>
#include <opencv2/xobjdetect/private.hpp> #include <opencv2/xobjdetect/private.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp> #include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h> #include <opencv2/imgproc/types_c.h>

@ -65,6 +65,7 @@ int Stump::train(const Mat& data, const Mat& labels, const Mat& weights)
{ {
CV_Assert(labels.rows == 1 && labels.cols == data.cols); CV_Assert(labels.rows == 1 && labels.cols == data.cols);
CV_Assert(weights.rows == 1 && weights.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 data and labels have int type */
/* Assert that weights have float type */ /* Assert that weights have float type */
@ -199,5 +200,19 @@ float Stump::predict(int value) const
return polarity_ * (value - threshold_) > 0 ? pos_value_ : neg_value_; 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 xobjdetect */
} /* namespace cv */ } /* namespace cv */

@ -45,6 +45,11 @@ using std::swap;
using std::vector; using std::vector;
#include <iostream>
using std::cout;
using std::endl;
namespace cv namespace cv
{ {
namespace xobjdetect namespace xobjdetect
@ -64,6 +69,10 @@ public:
virtual float predict( virtual float predict(
const Ptr<ACFFeatureEvaluator>& feature_evaluator) const; const Ptr<ACFFeatureEvaluator>& feature_evaluator) const;
virtual void write(FileStorage& fs) const;
virtual void read(const FileNode& node);
private: private:
/* Parameters for cascade training */ /* Parameters for cascade training */
WaldBoostParams params_; WaldBoostParams params_;
@ -73,19 +82,97 @@ private:
std::vector<float> thresholds_; std::vector<float> thresholds_;
}; };
vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels) static int count(const Mat_<int> &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 write(FileStorage& fs, String&, const WaldBoost& waldboost)
{
waldboost.write(fs);
}
void read(const FileNode& node, WaldBoost& w,
const WaldBoost& default_value)
{
if( node.empty() )
w = default_value;
else
w.read(node);
}
void WaldBoostImpl::read(const FileNode& node)
{ {
CV_Assert(labels.rows == 1 && labels.cols == data.cols); FileNode params = node["waldboost_params"];
params_.weak_count = (int)(params["weak_count"]);
params_.alpha = (float)(params["alpha"]);
int pos_count = 0, neg_count = 0; FileNode stumps = node["waldboost_stumps"];
for( int col = 0; col < labels.cols; ++col ) stumps_.clear();
for( FileNodeIterator n = stumps.begin(); n != stumps.end(); ++n )
{ {
if( labels.at<int>(0, col) == +1 ) Stump s;
pos_count += 1; *n >> s;
else stumps_.push_back(s);
neg_count += 1;
} }
FileNode thresholds = node["waldboost_thresholds"];
thresholds_.clear();
for( FileNodeIterator n = thresholds.begin(); n != thresholds.end(); ++n )
{
float t;
*n >> t;
thresholds_.push_back(t);
}
}
void WaldBoostImpl::write(FileStorage& fs) const
{
fs << "{";
fs << "waldboost_params" << "{"
<< "weak_count" << params_.weak_count
<< "alpha" << params_.alpha
<< "}";
fs << "waldboost_stumps" << "[";
for( size_t i = 0; i < stumps_.size(); ++i )
fs << stumps_[i];
fs << "]";
fs << "waldboost_thresholds" << "[";
for( size_t i = 0; i < thresholds_.size(); ++i )
fs << thresholds_[i];
fs << "]";
fs << "}";
}
vector<int> WaldBoostImpl::train(const Mat& data_, const Mat& labels_)
{
CV_Assert(labels_.rows == 1 && labels_.cols == data_.cols);
CV_Assert(data_.rows >= params_.weak_count);
Mat labels, data;
data_.copyTo(data);
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<int>(row, col) )
null_data = false;
}
CV_Assert(!null_data);
int pos_count = count(labels, +1);
int neg_count = count(labels, -1);
Mat_<float> weights(labels.rows, labels.cols); Mat_<float> weights(labels.rows, labels.cols);
float pos_weight = 1.0f / (2 * pos_count); float pos_weight = 1.0f / (2 * pos_count);
float neg_weight = 1.0f / (2 * neg_count); float neg_weight = 1.0f / (2 * neg_count);
@ -97,6 +184,9 @@ vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
weights.at<float>(0, col) = neg_weight; weights.at<float>(0, col) = neg_weight;
} }
vector<int> feature_indices_pool;
for( int ind = 0; ind < data.rows; ++ind )
feature_indices_pool.push_back(ind);
vector<int> feature_indices; vector<int> feature_indices;
Mat_<float> trace = Mat_<float>::zeros(labels.rows, labels.cols); Mat_<float> trace = Mat_<float>::zeros(labels.rows, labels.cols);
@ -104,10 +194,14 @@ vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
thresholds_.clear(); thresholds_.clear();
for( int i = 0; i < params_.weak_count; ++i) for( int i = 0; i < params_.weak_count; ++i)
{ {
cout << "stage " << i << endl;
Stump s; Stump s;
int feature_ind = s.train(data, labels, weights); int feature_ind = s.train(data, labels, weights);
cout << "feature_ind " << feature_ind << endl;
stumps_.push_back(s); stumps_.push_back(s);
feature_indices.push_back(feature_ind); int ind = feature_indices_pool[feature_ind];
feature_indices_pool.erase(feature_indices_pool.begin() + feature_ind);
feature_indices.push_back(ind);
// Recompute weights // Recompute weights
for( int col = 0; col < weights.cols; ++col ) for( int col = 0; col < weights.cols; ++col )
@ -118,6 +212,14 @@ vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
weights.at<float>(0, col) *= exp(-label * h); weights.at<float>(0, col) *= exp(-label * h);
} }
// Erase row for feature in data
Mat fixed_data;
fixed_data.push_back(data.rowRange(0, feature_ind));
fixed_data.push_back(data.rowRange(feature_ind + 1, data.rows));
data = fixed_data;
// Normalize weights // Normalize weights
float z = (float)sum(weights)[0]; float z = (float)sum(weights)[0];
for( int col = 0; col < weights.cols; ++col) for( int col = 0; col < weights.cols; ++col)
@ -125,12 +227,12 @@ vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
weights.at<float>(0, col) /= z; weights.at<float>(0, col) /= z;
} }
// Sort trace // Sort trace
Mat indices; Mat indices;
sortIdx(trace, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); sortIdx(trace, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING);
Mat new_weights = Mat_<float>::zeros(weights.rows, weights.cols); Mat new_weights = Mat_<float>::zeros(weights.rows, weights.cols);
Mat new_labels = Mat_<int>::zeros(labels.rows, labels.cols); Mat new_labels = Mat_<int>::zeros(labels.rows, labels.cols);
Mat new_data = Mat_<int>::zeros(data.rows, data.cols);
Mat new_trace; Mat new_trace;
for( int col = 0; col < new_weights.cols; ++col ) for( int col = 0; col < new_weights.cols; ++col )
{ {
@ -138,26 +240,60 @@ vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
weights.at<float>(0, indices.at<int>(0, col)); weights.at<float>(0, indices.at<int>(0, col));
new_labels.at<int>(0, col) = new_labels.at<int>(0, col) =
labels.at<int>(0, indices.at<int>(0, col)); labels.at<int>(0, indices.at<int>(0, col));
for( int row = 0; row < new_data.rows; ++row )
{
new_data.at<int>(row, col) =
data.at<int>(row, indices.at<int>(0, col));
}
} }
sort(trace, new_trace, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); sort(trace, new_trace, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING);
// Compute threshold for trace // Compute threshold for trace
/*
int col = 0; int col = 0;
for( int pos_i = 0; for( int pos_i = 0;
pos_i < pos_count * params_.alpha && col < weights.cols; pos_i < pos_count * params_.alpha && col < new_labels.cols;
++col ) ++col )
{ {
if( labels.at<int>(0, col) == +1 ) if( new_labels.at<int>(0, col) == +1 )
++pos_i; ++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<int>(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<float>(0, col)); thresholds_.push_back(new_trace.at<float>(0, max_col));
cout << "threshold " << *(thresholds_.end() - 1) << endl;
cout << "col " << max_col << " size " << data.cols << endl;
// Drop samples below threshold // Drop samples below threshold
new_trace.colRange(col, new_trace.cols).copyTo(trace); new_data.colRange(max_col, new_data.cols).copyTo(data);
new_weights.colRange(col, new_weights.cols).copyTo(weights); new_trace.colRange(max_col, new_trace.cols).copyTo(trace);
new_labels.colRange(col, new_labels.cols).copyTo(labels); 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);
cout << "pos_count " << pos_count << "; neg_count " << neg_count << endl;
if( data.cols < 2 || neg_count == 0)
{
break;
}
} }
return feature_indices; return feature_indices;
} }
@ -166,6 +302,7 @@ float WaldBoostImpl::predict(
const Ptr<ACFFeatureEvaluator>& feature_evaluator) const const Ptr<ACFFeatureEvaluator>& feature_evaluator) const
{ {
float trace = 0; float trace = 0;
CV_Assert(stumps_.size() == thresholds_.size());
for( size_t i = 0; i < stumps_.size(); ++i ) for( size_t i = 0; i < stumps_.size(); ++i )
{ {
int value = feature_evaluator->evaluate(i); int value = feature_evaluator->evaluate(i);

Loading…
Cancel
Save