Commit 033cf733 authored by Davis King's avatar Davis King

Refactored the load_image_dataset() routines so they are easier to use and more

flexible.  This introduces a slight backwards incompatibility in that the
version that loads full_object_detection objects now returns an ignore
rectangle set instead of a parts name list.  But other than that the changes
are backwards compatible with previous version of dlib.
parent 5da76449
...@@ -12,12 +12,75 @@ ...@@ -12,12 +12,75 @@
#include "../geometry.h" #include "../geometry.h"
#include "image_dataset_metadata.h" #include "image_dataset_metadata.h"
#include <string> #include <string>
#include <set>
#include "../image_processing/full_object_detection.h" #include "../image_processing/full_object_detection.h"
namespace dlib namespace dlib
{ {
// ----------------------------------------------------------------------------------------
class image_dataset_file
{
public:
image_dataset_file(const std::string& filename)
{
_skip_empty_images = false;
_have_parts = false;
_filename = filename;
}
image_dataset_file boxes_match_label(
const std::string& label
) const
{
image_dataset_file temp(*this);
temp._labels.insert(label);
return temp;
}
image_dataset_file skip_empty_images(
) const
{
image_dataset_file temp(*this);
temp._skip_empty_images = true;
return temp;
}
image_dataset_file boxes_have_parts(
) const
{
image_dataset_file temp(*this);
temp._have_parts = true;
return temp;
}
bool should_load_box (
const image_dataset_metadata::box& box
) const
{
if (_have_parts && box.parts.size() == 0)
return false;
if (_labels.size() == 0)
return true;
if (_labels.count(box.label) != 0)
return true;
return false;
}
const std::string& get_filename() const { return _filename; }
bool should_skip_empty_images() const { return _skip_empty_images; }
bool should_boxes_have_parts() const { return _have_parts; }
const std::set<std::string>& get_selected_box_labels() const { return _labels; }
private:
std::string _filename;
std::set<std::string> _labels;
bool _skip_empty_images;
bool _have_parts;
};
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template < template <
...@@ -27,9 +90,7 @@ namespace dlib ...@@ -27,9 +90,7 @@ namespace dlib
std::vector<std::vector<rectangle> > load_image_dataset ( std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images, array<image_type,MM>& images,
std::vector<std::vector<rectangle> >& object_locations, std::vector<std::vector<rectangle> >& object_locations,
const std::string& filename, const image_dataset_file& source
const std::string& label,
bool skip_empty_images = false
) )
{ {
images.clear(); images.clear();
...@@ -40,12 +101,12 @@ namespace dlib ...@@ -40,12 +101,12 @@ namespace dlib
using namespace dlib::image_dataset_metadata; using namespace dlib::image_dataset_metadata;
dataset data; dataset data;
load_image_dataset_metadata(data, filename); load_image_dataset_metadata(data, source.get_filename());
// Set the current directory to be the one that contains the // Set the current directory to be the one that contains the
// metadata file. We do this because the file might contain // metadata file. We do this because the file might contain
// file paths which are relative to this folder. // file paths which are relative to this folder.
set_current_dir(get_parent_directory(file(filename))); set_current_dir(get_parent_directory(file(source.get_filename())));
...@@ -58,7 +119,7 @@ namespace dlib ...@@ -58,7 +119,7 @@ namespace dlib
ignored.clear(); ignored.clear();
for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j)
{ {
if (label.size() == 0 || data.images[i].boxes[j].label == label) if (source.should_load_box(data.images[i].boxes[j]))
{ {
if (data.images[i].boxes[j].ignore) if (data.images[i].boxes[j].ignore)
ignored.push_back(data.images[i].boxes[j].rect); ignored.push_back(data.images[i].boxes[j].rect);
...@@ -67,7 +128,7 @@ namespace dlib ...@@ -67,7 +128,7 @@ namespace dlib
} }
} }
if (!skip_empty_images || rects.size() != 0) if (!source.should_skip_empty_images() || rects.size() != 0)
{ {
object_locations.push_back(rects); object_locations.push_back(rects);
ignored_rects.push_back(ignored); ignored_rects.push_back(ignored);
...@@ -80,6 +141,29 @@ namespace dlib ...@@ -80,6 +141,29 @@ namespace dlib
return ignored_rects; return ignored_rects;
} }
// ----------------------------------------------------------------------------------------
// ******* THIS FUNCTION IS DEPRECATED, you should use another version of load_image_dataset() *******
template <
typename image_type,
typename MM
>
std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images,
std::vector<std::vector<rectangle> >& object_locations,
const std::string& filename,
const std::string& label,
bool skip_empty_images = false
)
{
image_dataset_file f(filename);
if (label.size() != 0)
f = f.boxes_match_label(label);
if (skip_empty_images)
f = f.skip_empty_images();
return load_image_dataset(images, object_locations, f);
}
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template < template <
...@@ -92,35 +176,37 @@ namespace dlib ...@@ -92,35 +176,37 @@ namespace dlib
const std::string& filename const std::string& filename
) )
{ {
return load_image_dataset(images, object_locations, filename, ""); return load_image_dataset(images, object_locations, image_dataset_file(filename));
} }
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template < template <
typename image_type, typename image_type,
typename MM typename MM
> >
std::vector<std::string> load_image_dataset ( std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images, array<image_type,MM>& images,
std::vector<std::vector<full_object_detection> >& object_locations, std::vector<std::vector<full_object_detection> >& object_locations,
const std::string& filename, const image_dataset_file& source,
const std::string& label, std::vector<std::string>& parts_list
bool skip_empty_images = false
) )
{ {
parts_list.clear();
images.clear(); images.clear();
object_locations.clear(); object_locations.clear();
const std::string old_working_dir = get_current_dir(); const std::string old_working_dir = get_current_dir();
using namespace dlib::image_dataset_metadata; using namespace dlib::image_dataset_metadata;
dataset data; dataset data;
load_image_dataset_metadata(data, filename); load_image_dataset_metadata(data, source.get_filename());
// Set the current directory to be the one that contains the // Set the current directory to be the one that contains the
// metadata file. We do this because the file might contain // metadata file. We do this because the file might contain
// file paths which are relative to this folder. // file paths which are relative to this folder.
set_current_dir(get_parent_directory(file(filename))); set_current_dir(get_parent_directory(file(source.get_filename())));
std::set<std::string> all_parts; std::set<std::string> all_parts;
...@@ -130,7 +216,7 @@ namespace dlib ...@@ -130,7 +216,7 @@ namespace dlib
{ {
for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j)
{ {
if (label.size() == 0 || data.images[i].boxes[j].label == label) if (source.should_load_box(data.images[i].boxes[j]))
{ {
const std::map<std::string,point>& parts = data.images[i].boxes[j].parts; const std::map<std::string,point>& parts = data.images[i].boxes[j].parts;
std::map<std::string,point>::const_iterator itr; std::map<std::string,point>::const_iterator itr;
...@@ -145,21 +231,29 @@ namespace dlib ...@@ -145,21 +231,29 @@ namespace dlib
// make a mapping between part names and the integers [0, all_parts.size()) // make a mapping between part names and the integers [0, all_parts.size())
std::map<std::string,int> parts_idx; std::map<std::string,int> parts_idx;
std::vector<std::string> ret_parts_list;
for (std::set<std::string>::iterator i = all_parts.begin(); i != all_parts.end(); ++i) for (std::set<std::string>::iterator i = all_parts.begin(); i != all_parts.end(); ++i)
{ {
parts_idx[*i] = ret_parts_list.size(); parts_idx[*i] = parts_list.size();
ret_parts_list.push_back(*i); parts_list.push_back(*i);
} }
std::vector<std::vector<rectangle> > ignored_rects;
std::vector<rectangle> ignored;
image_type img; image_type img;
std::vector<full_object_detection> object_dets; std::vector<full_object_detection> object_dets;
for (unsigned long i = 0; i < data.images.size(); ++i) for (unsigned long i = 0; i < data.images.size(); ++i)
{ {
object_dets.clear(); object_dets.clear();
ignored.clear();
for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j)
{ {
if (label.size() == 0 || data.images[i].boxes[j].label == label) if (source.should_load_box(data.images[i].boxes[j]))
{
if (data.images[i].boxes[j].ignore)
{
ignored.push_back(data.images[i].boxes[j].rect);
}
else
{ {
std::vector<point> partlist(parts_idx.size(), OBJECT_PART_NOT_PRESENT); std::vector<point> partlist(parts_idx.size(), OBJECT_PART_NOT_PRESENT);
...@@ -174,10 +268,12 @@ namespace dlib ...@@ -174,10 +268,12 @@ namespace dlib
object_dets.push_back(full_object_detection(data.images[i].boxes[j].rect, partlist)); object_dets.push_back(full_object_detection(data.images[i].boxes[j].rect, partlist));
} }
} }
}
if (!skip_empty_images || object_dets.size() != 0) if (!source.should_skip_empty_images() || object_dets.size() != 0)
{ {
object_locations.push_back(object_dets); object_locations.push_back(object_dets);
ignored_rects.push_back(ignored);
load_image(img, data.images[i].filename); load_image(img, data.images[i].filename);
images.push_back(img); images.push_back(img);
} }
...@@ -185,7 +281,7 @@ namespace dlib ...@@ -185,7 +281,7 @@ namespace dlib
set_current_dir(old_working_dir); set_current_dir(old_working_dir);
return ret_parts_list; return ignored_rects;
} }
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
...@@ -194,13 +290,30 @@ namespace dlib ...@@ -194,13 +290,30 @@ namespace dlib
typename image_type, typename image_type,
typename MM typename MM
> >
std::vector<std::string> load_image_dataset ( std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images,
std::vector<std::vector<full_object_detection> >& object_locations,
const image_dataset_file& source
)
{
std::vector<std::string> parts_list;
return load_image_dataset(images, object_locations, source, parts_list);
}
// ----------------------------------------------------------------------------------------
template <
typename image_type,
typename MM
>
std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images, array<image_type,MM>& images,
std::vector<std::vector<full_object_detection> >& object_locations, std::vector<std::vector<full_object_detection> >& object_locations,
const std::string& filename const std::string& filename
) )
{ {
return load_image_dataset(images, object_locations, filename, ""); std::vector<std::string> parts_list;
return load_image_dataset(images, object_locations, image_dataset_file(filename), parts_list);
} }
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
......
...@@ -13,6 +13,111 @@ ...@@ -13,6 +13,111 @@
namespace dlib namespace dlib
{ {
// ----------------------------------------------------------------------------------------
class image_dataset_file
{
/*!
WHAT THIS OBJECT REPRESENTS
This object is a tool used to tell the load_image_dataset() functions which
boxes and images to load from an XML based image dataset file. By default,
this object tells load_image_dataset() to load all images and object boxes.
!*/
public:
image_dataset_file(
const std::string& filename
);
/*!
ensures
- #get_filename() == filename
- #should_skip_empty_images() == false
- #get_selected_box_labels().size() == 0
This means that, initially, all boxes will be loaded. Therefore, for all
possible boxes B we have:
- #should_load_box(B) == true
!*/
const std::string& get_filename(
) const;
/*!
ensures
- returns the name of the XML image dataset metadata file given to this
object's constructor.
!*/
bool should_skip_empty_images(
) const;
/*!
ensures
- returns true if we are supposed to skip images that don't have any boxes
to load when loading an image dataset using load_image_dataset().
!*/
image_dataset_file boxes_match_label(
const std::string& label
) const;
/*!
ensures
- returns a copy of *this that is identical in all respects to *this except
that label will be included in the labels set (i.e. the set returned by
get_selected_box_labels()).
!*/
const std::set<std::string>& get_selected_box_labels(
) const;
/*!
ensures
- returns the set of box labels currently selected by the should_load_box()
method. Note that if the set is empty then we select all boxes.
!*/
image_dataset_file skip_empty_images(
) const;
/*!
ensures
- returns a copy of *this that is identical in all respects to *this except
that #should_skip_empty_images() == true.
!*/
bool should_boxes_have_parts(
) const;
/*!
ensures
- returns true if boxes must have some parts defined for them to be loaded.
!*/
image_dataset_file boxes_have_parts(
) const;
/*!
ensures
- returns a copy of *this that is identical in all respects to *this except
that #should_boxes_have_parts() == true.
!*/
bool should_load_box (
const image_dataset_metadata::box& box
) const;
/*!
ensures
- returns true if we are supposed to load the given box from an image
dataset XML file. In particular, if should_load_box() returns false then
the load_image_dataset() routines will not return the box at all, neither
in the ignore rectangles list or in the primary object_locations vector.
The behavior of this function is defined as follows:
- if (should_boxes_have_parts() && boxes.parts.size() == 0) then
- returns false
- else if (get_selected_box_labels().size() == 0) then
- returns true
- else if (get_selected_box_labels().count(box.label) != 0) then
- returns true
- else
- returns false
!*/
};
// ----------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template < template <
...@@ -22,9 +127,7 @@ namespace dlib ...@@ -22,9 +127,7 @@ namespace dlib
std::vector<std::vector<rectangle> > load_image_dataset ( std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images, array<image_type,MM>& images,
std::vector<std::vector<rectangle> >& object_locations, std::vector<std::vector<rectangle> >& object_locations,
const std::string& filename, const image_dataset_file& source
const std::string& label,
bool skip_empty_images = false
); );
/*! /*!
requires requires
...@@ -32,32 +135,28 @@ namespace dlib ...@@ -32,32 +135,28 @@ namespace dlib
- pixel_traits<typename image_type::type> is defined - pixel_traits<typename image_type::type> is defined
ensures ensures
- This routine loads the images and their associated object boxes from the - This routine loads the images and their associated object boxes from the
image metadata file indicated by filename. This metadata file should be in image metadata file indicated by source.get_filename(). This metadata file
the XML format used by the save_image_dataset_metadata() routine. should be in the XML format used by the save_image_dataset_metadata() routine.
- #images.size() == The number of images loaded from the metadata file. This - #images.size() == The number of images loaded from the metadata file. This
is all the images listed in the file unless skip_empty_images is set to true. is all the images listed in the file unless source.should_skip_empty_images()
is set to true.
- #images.size() == #object_locations.size() - #images.size() == #object_locations.size()
- This routine is capable of loading any image format which can be read by the - This routine is capable of loading any image format which can be read by the
load_image() routine. load_image() routine.
- let IGNORED_RECTS denote the vector returned from this function. - let IGNORED_RECTS denote the vector returned from this function.
- IGNORED_RECTS.size() == #object_locations.size() - IGNORED_RECTS.size() == #object_locations.size()
- IGNORED_RECTS == a list of the rectangles which have the "ignore" flag set to - IGNORED_RECTS == a list of the rectangles which have the "ignore" flag set to
true in the XML file. true in the input XML file.
- for all valid i: - for all valid i:
- #images[i] == a copy of the i-th image from the dataset. - #images[i] == a copy of the i-th image from the dataset.
- #object_locations[i] == a vector of all the rectangles associated with - #object_locations[i] == a vector of all the rectangles associated with
#images[i]. Note that only rectangles that are not marked as "ignore" #images[i]. These are the rectangles for which source.should_load_box()
are stored into #object_locations. returns true and are also not marked as "ignore" in the XML file.
- IGNORED_RECTS[i] == A vector of all the rectangles associated with - IGNORED_RECTS[i] == A vector of all the rectangles associated with #images[i]
#images[i] that are marked as "ignore". that are marked as "ignore" but not discarded by source.should_load_box().
- if (skip_empty_images == true) then - if (source.should_skip_empty_images() == true) then
- #object_locations[i].size() != 0 - #object_locations[i].size() != 0
(i.e. only images with non-ignored boxes in them will be loaded.) (i.e. we won't load images that don't end up having any object locations)
- if (labels != "") then
- Only boxes with the given label will be loaded into object_locations
and IGNORED_RECTS.
- else
- all boxes in the dataset will be loaded regardless of their labels.
!*/ !*/
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
...@@ -76,22 +175,22 @@ namespace dlib ...@@ -76,22 +175,22 @@ namespace dlib
- image_type == is an implementation of array2d/array2d_kernel_abstract.h - image_type == is an implementation of array2d/array2d_kernel_abstract.h
- pixel_traits<typename image_type::type> is defined - pixel_traits<typename image_type::type> is defined
ensures ensures
- performs: return load_image_dataset(images, object_locations, filename, ""); - performs: return load_image_dataset(images, object_locations, image_dataset_file(filename));
(i.e. it ignores box labels and therefore loads all the boxes in the dataset) (i.e. it ignores box labels and therefore loads all the boxes in the dataset)
!*/ !*/
// ----------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
template < template <
typename image_type, typename image_type,
typename MM typename MM
> >
std::vector<std::string> load_image_dataset ( std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images, array<image_type,MM>& images,
std::vector<std::vector<full_object_detection> >& object_locations, std::vector<std::vector<full_object_detection> >& object_locations,
const std::string& filename, const image_dataset_file& source,
const std::string& label, std::vector<std::string>& parts_list
bool skip_empty_images = false
); );
/*! /*!
requires requires
...@@ -99,35 +198,39 @@ namespace dlib ...@@ -99,35 +198,39 @@ namespace dlib
- pixel_traits<typename image_type::type> is defined - pixel_traits<typename image_type::type> is defined
ensures ensures
- This routine loads the images and their associated object locations from the - This routine loads the images and their associated object locations from the
image metadata file indicated by filename. This metadata file should be in image metadata file indicated by source.get_filename(). This metadata file
the XML format used by the save_image_dataset_metadata() routine. should be in the XML format used by the save_image_dataset_metadata() routine.
- The difference between this function and the version of load_image_dataset() - The difference between this function and the version of load_image_dataset()
defined above is that this version will also load object part information and defined above is that this version will also load object part information and
thus fully populates the full_object_detection objects. thus fully populates the full_object_detection objects.
- #images.size() == The number of images loaded from the metadata file. This - #images.size() == The number of images loaded from the metadata file. This
is all the images listed in the file unless skip_empty_images is set to true. is all the images listed in the file unless source.should_skip_empty_images()
is set to true.
- #images.size() == #object_locations.size() - #images.size() == #object_locations.size()
- This routine is capable of loading any image format which can be read - This routine is capable of loading any image format which can be read
by the load_image() routine. by the load_image() routine.
- returns a vector, call it RETURNED_PARTS, that contains the list of object - #parts_list == a vector that contains the list of object parts found in the
parts found in the input file and loaded into object_locations. input file and loaded into object_locations.
- let IGNORED_RECTS denote the vector returned from this function.
- IGNORED_RECTS.size() == #object_locations.size()
- IGNORED_RECTS == a list of the rectangles which have the "ignore" flag set to
true in the input XML file.
- for all valid i: - for all valid i:
- #images[i] == a copy of the ith image from the dataset. - #images[i] == a copy of the i-th image from the dataset.
- #object_locations[i] == a vector of all the object detections associated - #object_locations[i] == a vector of all the rectangles associated with
with #images[i]. #images[i]. These are the rectangles for which source.should_load_box()
- if (skip_empty_images == true) then returns true and are also not marked as "ignore" in the XML file.
- IGNORED_RECTS[i] == A vector of all the rectangles associated with #images[i]
that are marked as "ignore" but not discarded by source.should_load_box().
- if (source.should_skip_empty_images() == true) then
- #object_locations[i].size() != 0 - #object_locations[i].size() != 0
(i.e. only images with detection boxes in them will be loaded.) (i.e. we won't load images that don't end up having any object locations)
- for all valid j: - for all valid j:
- #object_locations[i][j].num_parts() == RETURNED_PARTS.size() - #object_locations[i][j].num_parts() == #parts_list.size()
- for all valid k: - for all valid k:
- #object_locations[i][j].part(k) == the location of the part - #object_locations[i][j].part(k) == the location of the part
with name RETURNED_PARTS[k] or OBJECT_PART_NOT_PRESENT if the with name #parts_list[k] or OBJECT_PART_NOT_PRESENT if the
part was not indicated for object #object_locations[i][j]. part was not indicated for object #object_locations[i][j].
- if (labels != "") then
- only boxes with the given label will be loaded into object_locations.
- else
- all boxes in the dataset will be loaded into object_locations.
!*/ !*/
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
...@@ -136,7 +239,29 @@ namespace dlib ...@@ -136,7 +239,29 @@ namespace dlib
typename image_type, typename image_type,
typename MM typename MM
> >
std::vector<std::string> load_image_dataset ( std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images,
std::vector<std::vector<full_object_detection> >& object_locations,
const image_dataset_file& source
);
/*!
requires
- image_type == is an implementation of array2d/array2d_kernel_abstract.h
- pixel_traits<typename image_type::type> is defined
ensures
- performs: return load_image_dataset(images, object_locations, source, parts_list);
(i.e. this function simply calls the above function and discards the output
parts_list. So it is just a convenience function you can call if you don't
care about getting the parts list.)
!*/
// ----------------------------------------------------------------------------------------
template <
typename image_type,
typename MM
>
std::vector<std::vector<rectangle> > load_image_dataset (
array<image_type,MM>& images, array<image_type,MM>& images,
std::vector<std::vector<full_object_detection> >& object_locations, std::vector<std::vector<full_object_detection> >& object_locations,
const std::string& filename const std::string& filename
...@@ -146,7 +271,7 @@ namespace dlib ...@@ -146,7 +271,7 @@ namespace dlib
- image_type == is an implementation of array2d/array2d_kernel_abstract.h - image_type == is an implementation of array2d/array2d_kernel_abstract.h
- pixel_traits<typename image_type::type> is defined - pixel_traits<typename image_type::type> is defined
ensures ensures
- performs: return load_image_dataset(images, object_locations, filename, ""); - performs: return load_image_dataset(images, object_locations, image_dataset_file(filename));
(i.e. it ignores box labels and therefore loads all the boxes in the dataset) (i.e. it ignores box labels and therefore loads all the boxes in the dataset)
!*/ !*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment