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. 194
      modules/xobjdetect/src/icfdetector.cpp
  11. 2
      modules/xobjdetect/src/precomp.hpp
  12. 15
      modules/xobjdetect/src/stump.cpp
  13. 167
      modules/xobjdetect/src/waldboost.cpp

@ -1,33 +1,2 @@
set(name fcw_train)
set(the_target opencv_${name})
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)
add_subdirectory(fcw_train)
add_subdirectory(fcw_detect)

@ -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
*/
void computeChannels(InputArray image, OutputArrayOfArrays channels);
void computeChannels(InputArray image, std::vector<Mat>& channels);
class CV_EXPORTS ACFFeatureEvaluator : public Algorithm
{
@ -69,6 +69,8 @@ public:
/* Set window position */
virtual void setPosition(Size position) = 0;
virtual void assertChannels() = 0;
/* Evaluate feature with given index for current channels
and window position */
virtual int evaluate(size_t feature_ind) const = 0;
@ -103,7 +105,7 @@ struct CV_EXPORTS WaldBoostParams
int weak_count;
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.
Feature enumeration starts from 0
*/
virtual std::vector<int> train(const Mat& data,
const Mat& labels) = 0;
virtual std::vector<int> train(const Mat& /*data*/,
const Mat& /*labels*/) {return std::vector<int>();}
/* Predict object class given object that can compute object features
@ -133,10 +135,21 @@ public:
is from class +1
*/
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>
createWaldBoost(const WaldBoostParams& params = WaldBoostParams());
@ -146,32 +159,63 @@ struct CV_EXPORTS ICFDetectorParams
int weak_count;
int model_n_rows;
int model_n_cols;
double overlap;
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
{
public:
ICFDetector(): waldboost_(), features_() {}
/* 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
*/
void train(const std::vector<std::string>& image_filenames,
const std::vector<std::vector<cv::Rect> >& labelling,
void train(const String& pos_path,
const String& bg_path,
ICFDetectorParams params = ICFDetectorParams());
/* Save detector in file, return true on success, false otherwise */
bool save(const std::string& filename);
/* Detect object on image
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 cv */

@ -47,6 +47,26 @@ public:
*/
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_;
@ -56,6 +76,10 @@ private:
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 */

@ -64,6 +64,7 @@ public:
}
virtual void setChannels(InputArrayOfArrays channels);
virtual void assertChannels();
virtual void setPosition(Size position);
virtual int evaluate(size_t feature_ind) const;
virtual void evaluateAll(OutputArray feature_values) const;
@ -77,16 +78,56 @@ private:
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)
{
channels_.clear();
vector<Mat> ch;
channels.getMatVector(ch);
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];
Mat_<int> acf_channel(channel.rows / 4, channel.cols / 4);
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 )
{
const Mat &channel = ch[i];
Mat_<int> acf_channel = Mat_<int>::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 )
@ -94,18 +135,23 @@ void ACFFeatureEvaluatorImpl::setChannels(cv::InputArrayOfArrays channels)
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 )
{
//cout << channel.rows << " " << channel.cols << endl;
//cout << cell_row << " " << cell_col << endl;
sum += (int)channel.at<float>(cell_row, cell_col);
}
acf_channel(row / 4, col / 4) = sum;
}
}
channels_.push_back(acf_channel);
channels_.push_back(acf_channel.clone());
}
}
void ACFFeatureEvaluatorImpl::setPosition(Size position)
{
position_ = position;
position_ = Size(position.width / 4, position.height / 4);
}
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 y = feature.y;
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
@ -127,7 +173,7 @@ void ACFFeatureEvaluatorImpl::evaluateAll(OutputArray feature_values) const
{
feature_vals(0, i) = evaluate(i);
}
feature_values.setTo(feature_vals);
feature_values.assign(feature_vals);
}
Ptr<ACFFeatureEvaluator>
@ -159,14 +205,15 @@ vector<Point3i> generateFeatures(Size window_size, int count)
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);
image.getMat().convertTo(src, CV_32FC3, 1./255);
Mat_<float> grad;
Mat gray;
Mat luv, gray;
cvtColor(src, gray, CV_RGB2GRAY);
cvtColor(src, luv, CV_RGB2Luv);
Mat_<float> row_der, col_der;
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);
Mat_<Vec6f> hist(grad.rows, grad.cols);
Mat_<Vec6f> hist = Mat_<Vec6f>::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) {
@ -182,12 +229,22 @@ void computeChannels(cv::InputArray image, cv::OutputArrayOfArrays channels_)
if (angle < 0)
angle += 180;
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.push_back(gray);
channels.clear();
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<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 )
channels.push_back(hist_channels[i]);
channels_.setTo(channels);
}
} /* 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"
using std::vector;
@ -52,64 +63,54 @@ namespace cv
namespace xobjdetect
{
static bool overlap(const Rect& r, const vector<Rect>& gt)
{
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,
void ICFDetector::train(const String& pos_path,
const String& bg_path,
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);
vector<Mat> samples; /* positive samples + negative samples */
Mat sample, resized_sample;
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))
.rowRange(max(r.y, 0), min(r.height, img.rows));
resize(sample, resized_sample, model_size);
samples.push_back(resized_sample);
}
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]);
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 < image_filenames.size(); ++i )
for( size_t i = 0; i < bg_filenames.size(); ++i )
{
Mat img = imread(String(image_filenames[i].c_str()));
for( int j = 0; j < (int)(pos_count / image_filenames.size() + 1); )
cout << setw(6) << (i + 1) << "/" << bg_filenames.size() << "\r";
Mat img = imread(bg_filenames[i]);
for( int j = 0; j < 50;
++j, ++neg_count)
{
Rect r;
r.x = rng.uniform(0, img.cols);
r.width = rng.uniform(r.x + 1, img.cols);
r.x = rng.uniform(0, img.cols); r.width = rng.uniform(r.x + 1, img.cols);
r.y = rng.uniform(0, 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);
samples.push_back(resized_sample);
++neg_count;
++j;
}
resize(sample, resized_sample, model_size);
samples.push_back(resized_sample.clone());
}
}
cout << "\n";
Mat_<int> labels(1, pos_count + neg_count);
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);
Ptr<ACFFeatureEvaluator> feature_evaluator = createACFFeatureEvaluator(features);
Mat_<int> data((int)features.size(), (int)samples.size());
Mat_<int> feature_col;
Mat_<int> data = Mat_<int>::zeros((int)features.size(), (int)samples.size());
Mat_<int> feature_col(1, (int)samples.size());
vector<Mat> channels;
for( int i = 0; i < (int)samples.size(); ++i )
{
cout << setw(6) << i << "/" << samples.size() << "\r";
computeChannels(samples[i], channels);
feature_evaluator->setChannels(channels);
//feature_evaluator->assertChannels();
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;
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]]);
}
void ICFDetector::write(FileStorage& fs) const
{
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);
}
}
Ptr<WaldBoost> waldboost = createWaldBoost(wparams);
waldboost->train(data, labels);
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 */

@ -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/private.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#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(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 */
@ -199,5 +200,19 @@ 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 */

@ -45,6 +45,11 @@ using std::swap;
using std::vector;
#include <iostream>
using std::cout;
using std::endl;
namespace cv
{
namespace xobjdetect
@ -64,6 +69,10 @@ public:
virtual float predict(
const Ptr<ACFFeatureEvaluator>& feature_evaluator) const;
virtual void write(FileStorage& fs) const;
virtual void read(const FileNode& node);
private:
/* Parameters for cascade training */
WaldBoostParams params_;
@ -73,18 +82,96 @@ private:
std::vector<float> thresholds_;
};
vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
static int count(const Mat_<int> &m, int elem)
{
CV_Assert(labels.rows == 1 && labels.cols == data.cols);
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;
}
int pos_count = 0, neg_count = 0;
for( int col = 0; col < labels.cols; ++col )
void write(FileStorage& fs, String&, const WaldBoost& waldboost)
{
if( labels.at<int>(0, col) == +1 )
pos_count += 1;
waldboost.write(fs);
}
void read(const FileNode& node, WaldBoost& w,
const WaldBoost& default_value)
{
if( node.empty() )
w = default_value;
else
neg_count += 1;
w.read(node);
}
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);
}
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);
float pos_weight = 1.0f / (2 * pos_count);
@ -97,6 +184,9 @@ vector<int> WaldBoostImpl::train(const Mat& data, const Mat& labels)
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;
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();
for( int i = 0; i < params_.weak_count; ++i)
{
cout << "stage " << i << endl;
Stump s;
int feature_ind = s.train(data, labels, weights);
cout << "feature_ind " << feature_ind << endl;
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
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);
}
// 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
float z = (float)sum(weights)[0];
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;
}
// Sort trace
Mat indices;
sortIdx(trace, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING);
Mat new_weights = Mat_<float>::zeros(weights.rows, weights.cols);
Mat new_labels = Mat_<int>::zeros(labels.rows, labels.cols);
Mat new_data = Mat_<int>::zeros(data.rows, data.cols);
Mat new_trace;
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));
new_labels.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);
// Compute threshold for trace
/*
int col = 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 )
{
if( labels.at<int>(0, col) == +1 )
if( new_labels.at<int>(0, col) == +1 )
++pos_i;
}
*/
thresholds_.push_back(new_trace.at<float>(0, col));
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, max_col));
cout << "threshold " << *(thresholds_.end() - 1) << endl;
cout << "col " << max_col << " size " << data.cols << endl;
// Drop samples below threshold
new_trace.colRange(col, new_trace.cols).copyTo(trace);
new_weights.colRange(col, new_weights.cols).copyTo(weights);
new_labels.colRange(col, new_labels.cols).copyTo(labels);
new_data.colRange(max_col, new_data.cols).copyTo(data);
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);
cout << "pos_count " << pos_count << "; neg_count " << neg_count << endl;
if( data.cols < 2 || neg_count == 0)
{
break;
}
}
return feature_indices;
}
@ -166,6 +302,7 @@ float WaldBoostImpl::predict(
const Ptr<ACFFeatureEvaluator>& feature_evaluator) 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);

Loading…
Cancel
Save