402 lines
12 KiB
C++
402 lines
12 KiB
C++
// Copyright (C) 2013 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#ifndef DLIB_SCAN_IMAGE_CuSTOM_Hh_
|
|
#define DLIB_SCAN_IMAGE_CuSTOM_Hh_
|
|
|
|
#include "scan_image_custom_abstract.h"
|
|
#include "../matrix.h"
|
|
#include "../geometry.h"
|
|
#include <vector>
|
|
#include "../image_processing/full_object_detection.h"
|
|
|
|
namespace dlib
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
class scan_image_custom : noncopyable
|
|
{
|
|
|
|
public:
|
|
|
|
typedef matrix<double,0,1> feature_vector_type;
|
|
typedef Feature_extractor_type feature_extractor_type;
|
|
|
|
scan_image_custom (
|
|
);
|
|
|
|
template <
|
|
typename image_type
|
|
>
|
|
void load (
|
|
const image_type& img
|
|
);
|
|
|
|
inline bool is_loaded_with_image (
|
|
) const;
|
|
|
|
inline void copy_configuration(
|
|
const feature_extractor_type& fe
|
|
);
|
|
|
|
const Feature_extractor_type& get_feature_extractor (
|
|
) const { return feats; }
|
|
|
|
inline void copy_configuration (
|
|
const scan_image_custom& item
|
|
);
|
|
|
|
inline long get_num_dimensions (
|
|
) const;
|
|
|
|
void detect (
|
|
const feature_vector_type& w,
|
|
std::vector<std::pair<double, rectangle> >& dets,
|
|
const double thresh
|
|
) const;
|
|
|
|
void get_feature_vector (
|
|
const full_object_detection& obj,
|
|
feature_vector_type& psi
|
|
) const;
|
|
|
|
full_object_detection get_full_object_detection (
|
|
const rectangle& rect,
|
|
const feature_vector_type& w
|
|
) const;
|
|
|
|
const rectangle get_best_matching_rect (
|
|
const rectangle& rect
|
|
) const;
|
|
|
|
inline unsigned long get_num_detection_templates (
|
|
) const { return 1; }
|
|
|
|
inline unsigned long get_num_movable_components_per_detection_template (
|
|
) const { return 0; }
|
|
|
|
template <typename T>
|
|
friend void serialize (
|
|
const scan_image_custom<T>& item,
|
|
std::ostream& out
|
|
);
|
|
|
|
template <typename T>
|
|
friend void deserialize (
|
|
scan_image_custom<T>& item,
|
|
std::istream& in
|
|
);
|
|
|
|
private:
|
|
static bool compare_pair_rect (
|
|
const std::pair<double, rectangle>& a,
|
|
const std::pair<double, rectangle>& b
|
|
)
|
|
{
|
|
return a.first < b.first;
|
|
}
|
|
|
|
|
|
DLIB_MAKE_HAS_MEMBER_FUNCTION_TEST(
|
|
has_compute_object_score,
|
|
double,
|
|
compute_object_score,
|
|
( const matrix<double,0,1>& w, const rectangle& obj) const
|
|
);
|
|
|
|
template <typename fe_type>
|
|
typename enable_if<has_compute_object_score<fe_type> >::type compute_all_rect_scores (
|
|
const fe_type& feats,
|
|
const feature_vector_type& w,
|
|
std::vector<std::pair<double, rectangle> >& dets,
|
|
const double thresh
|
|
) const
|
|
{
|
|
for (unsigned long i = 0; i < search_rects.size(); ++i)
|
|
{
|
|
const double score = feats.compute_object_score(w, search_rects[i]);
|
|
if (score >= thresh)
|
|
{
|
|
dets.push_back(std::make_pair(score, search_rects[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename fe_type>
|
|
typename disable_if<has_compute_object_score<fe_type> >::type compute_all_rect_scores (
|
|
const fe_type& feats,
|
|
const feature_vector_type& w,
|
|
std::vector<std::pair<double, rectangle> >& dets,
|
|
const double thresh
|
|
) const
|
|
{
|
|
matrix<double,0,1> psi(w.size());
|
|
psi = 0;
|
|
double prev_dot = 0;
|
|
for (unsigned long i = 0; i < search_rects.size(); ++i)
|
|
{
|
|
// Reset these back to zero every so often to avoid the accumulation of
|
|
// rounding error. Note that the only reason we do this loop in this
|
|
// complex way is to avoid needing to zero the psi vector every iteration.
|
|
if ((i%500) == 499)
|
|
{
|
|
psi = 0;
|
|
prev_dot = 0;
|
|
}
|
|
|
|
feats.get_feature_vector(search_rects[i], psi);
|
|
const double cur_dot = dot(psi, w);
|
|
const double score = cur_dot - prev_dot;
|
|
if (score >= thresh)
|
|
{
|
|
dets.push_back(std::make_pair(score, search_rects[i]));
|
|
}
|
|
prev_dot = cur_dot;
|
|
}
|
|
}
|
|
|
|
|
|
feature_extractor_type feats;
|
|
std::vector<rectangle> search_rects;
|
|
bool loaded_with_image;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
void serialize (
|
|
const scan_image_custom<T>& item,
|
|
std::ostream& out
|
|
)
|
|
{
|
|
int version = 1;
|
|
serialize(version, out);
|
|
serialize(item.feats, out);
|
|
serialize(item.search_rects, out);
|
|
serialize(item.loaded_with_image, out);
|
|
serialize(item.get_num_dimensions(), out);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
void deserialize (
|
|
scan_image_custom<T>& item,
|
|
std::istream& in
|
|
)
|
|
{
|
|
int version = 0;
|
|
deserialize(version, in);
|
|
if (version != 1)
|
|
throw serialization_error("Unsupported version found when deserializing a scan_image_custom object.");
|
|
|
|
deserialize(item.feats, in);
|
|
deserialize(item.search_rects, in);
|
|
deserialize(item.loaded_with_image, in);
|
|
|
|
// When developing some feature extractor, it's easy to accidentally change its
|
|
// number of dimensions and then try to deserialize data from an older version of
|
|
// your extractor into the current code. This check is here to catch that kind of
|
|
// user error.
|
|
long dims;
|
|
deserialize(dims, in);
|
|
if (item.get_num_dimensions() != dims)
|
|
throw serialization_error("Number of dimensions in serialized scan_image_custom doesn't match the expected number.");
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// scan_image_custom member functions
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
scan_image_custom<Feature_extractor_type>::
|
|
scan_image_custom (
|
|
) :
|
|
loaded_with_image(false)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
template <
|
|
typename image_type
|
|
>
|
|
void scan_image_custom<Feature_extractor_type>::
|
|
load (
|
|
const image_type& img
|
|
)
|
|
{
|
|
feats.load(img, search_rects);
|
|
loaded_with_image = true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
bool scan_image_custom<Feature_extractor_type>::
|
|
is_loaded_with_image (
|
|
) const
|
|
{
|
|
return loaded_with_image;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
void scan_image_custom<Feature_extractor_type>::
|
|
copy_configuration(
|
|
const feature_extractor_type& fe
|
|
)
|
|
{
|
|
feats.copy_configuration(fe);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
void scan_image_custom<Feature_extractor_type>::
|
|
copy_configuration (
|
|
const scan_image_custom& item
|
|
)
|
|
{
|
|
feats.copy_configuration(item.feats);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
long scan_image_custom<Feature_extractor_type>::
|
|
get_num_dimensions (
|
|
) const
|
|
{
|
|
return feats.get_num_dimensions();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
void scan_image_custom<Feature_extractor_type>::
|
|
detect (
|
|
const feature_vector_type& w,
|
|
std::vector<std::pair<double, rectangle> >& dets,
|
|
const double thresh
|
|
) const
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(is_loaded_with_image() &&
|
|
w.size() >= get_num_dimensions(),
|
|
"\t void scan_image_custom::detect()"
|
|
<< "\n\t Invalid inputs were given to this function "
|
|
<< "\n\t is_loaded_with_image(): " << is_loaded_with_image()
|
|
<< "\n\t w.size(): " << w.size()
|
|
<< "\n\t get_num_dimensions(): " << get_num_dimensions()
|
|
<< "\n\t this: " << this
|
|
);
|
|
|
|
dets.clear();
|
|
compute_all_rect_scores(feats, w,dets,thresh);
|
|
std::sort(dets.rbegin(), dets.rend(), compare_pair_rect);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
const rectangle scan_image_custom<Feature_extractor_type>::
|
|
get_best_matching_rect (
|
|
const rectangle& rect
|
|
) const
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(is_loaded_with_image(),
|
|
"\t const rectangle scan_image_custom::get_best_matching_rect()"
|
|
<< "\n\t Invalid inputs were given to this function "
|
|
<< "\n\t is_loaded_with_image(): " << is_loaded_with_image()
|
|
<< "\n\t this: " << this
|
|
);
|
|
|
|
|
|
double best_score = -1;
|
|
rectangle best_rect;
|
|
for (unsigned long i = 0; i < search_rects.size(); ++i)
|
|
{
|
|
const double score = (rect.intersect(search_rects[i])).area()/(double)(rect+search_rects[i]).area();
|
|
if (score > best_score)
|
|
{
|
|
best_score = score;
|
|
best_rect = search_rects[i];
|
|
}
|
|
}
|
|
return best_rect;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
full_object_detection scan_image_custom<Feature_extractor_type>::
|
|
get_full_object_detection (
|
|
const rectangle& rect,
|
|
const feature_vector_type& /*w*/
|
|
) const
|
|
{
|
|
return full_object_detection(rect);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename Feature_extractor_type
|
|
>
|
|
void scan_image_custom<Feature_extractor_type>::
|
|
get_feature_vector (
|
|
const full_object_detection& obj,
|
|
feature_vector_type& psi
|
|
) const
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(is_loaded_with_image() &&
|
|
psi.size() >= get_num_dimensions() &&
|
|
obj.num_parts() == 0,
|
|
"\t void scan_image_custom::get_feature_vector()"
|
|
<< "\n\t Invalid inputs were given to this function "
|
|
<< "\n\t is_loaded_with_image(): " << is_loaded_with_image()
|
|
<< "\n\t psi.size(): " << psi.size()
|
|
<< "\n\t get_num_dimensions(): " << get_num_dimensions()
|
|
<< "\n\t obj.num_parts(): " << obj.num_parts()
|
|
<< "\n\t this: " << this
|
|
);
|
|
|
|
|
|
feats.get_feature_vector(get_best_matching_rect(obj.get_rect()), psi);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
}
|
|
|
|
#endif // DLIB_SCAN_IMAGE_CuSTOM_Hh_
|
|
|