|
|
|
#include "convert_pascal_xml.h" |
|
#include "dlib/data_io.h" |
|
#include <iostream> |
|
#include <dlib/xml_parser.h> |
|
#include <string> |
|
#include <dlib/dir_nav.h> |
|
#include <dlib/cmd_line_parser.h> |
|
|
|
using namespace std; |
|
using namespace dlib; |
|
|
|
namespace |
|
{ |
|
using namespace dlib::image_dataset_metadata; |
|
|
|
|
|
|
|
class doc_handler : public document_handler |
|
{ |
|
image& temp_image; |
|
std::string& dataset_name; |
|
|
|
std::vector<std::string> ts; |
|
box temp_box; |
|
|
|
public: |
|
|
|
doc_handler( |
|
image& temp_image_, |
|
std::string& dataset_name_ |
|
): |
|
temp_image(temp_image_), |
|
dataset_name(dataset_name_) |
|
{} |
|
|
|
|
|
virtual void start_document ( |
|
) |
|
{ |
|
ts.clear(); |
|
temp_image = image(); |
|
temp_box = box(); |
|
dataset_name.clear(); |
|
} |
|
|
|
virtual void end_document ( |
|
) |
|
{ |
|
} |
|
|
|
virtual void start_element ( |
|
const unsigned long , |
|
const std::string& name, |
|
const dlib::attribute_list& |
|
) |
|
{ |
|
if (ts.size() == 0 && name != "annotation") |
|
{ |
|
std::ostringstream sout; |
|
sout << "Invalid XML document. Root tag must be <annotation>. Found <" << name << "> instead."; |
|
throw dlib::error(sout.str()); |
|
} |
|
|
|
|
|
ts.push_back(name); |
|
} |
|
|
|
virtual void end_element ( |
|
const unsigned long , |
|
const std::string& name |
|
) |
|
{ |
|
ts.pop_back(); |
|
if (ts.size() == 0) |
|
return; |
|
|
|
if (name == "object" && ts.back() == "annotation") |
|
{ |
|
temp_image.boxes.push_back(temp_box); |
|
temp_box = box(); |
|
} |
|
} |
|
|
|
virtual void characters ( |
|
const std::string& data |
|
) |
|
{ |
|
if (ts.size() == 2 && ts[1] == "filename") |
|
{ |
|
temp_image.filename = trim(data); |
|
} |
|
else if (ts.size() == 3 && ts[2] == "database" && ts[1] == "source") |
|
{ |
|
dataset_name = trim(data); |
|
} |
|
else if (ts.size() >= 3) |
|
{ |
|
if (ts[ts.size()-2] == "bndbox" && ts[ts.size()-3] == "object") |
|
{ |
|
if (ts.back() == "xmin") temp_box.rect.left() = string_cast<double>(data); |
|
else if (ts.back() == "ymin") temp_box.rect.top() = string_cast<double>(data); |
|
else if (ts.back() == "xmax") temp_box.rect.right() = string_cast<double>(data); |
|
else if (ts.back() == "ymax") temp_box.rect.bottom() = string_cast<double>(data); |
|
} |
|
else if (ts.back() == "name" && ts[ts.size()-2] == "object") |
|
{ |
|
temp_box.label = trim(data); |
|
} |
|
else if (ts.back() == "difficult" && ts[ts.size()-2] == "object") |
|
{ |
|
if (trim(data) == "0" || trim(data) == "false") |
|
{ |
|
temp_box.difficult = false; |
|
} |
|
else |
|
{ |
|
temp_box.difficult = true; |
|
} |
|
} |
|
else if (ts.back() == "truncated" && ts[ts.size()-2] == "object") |
|
{ |
|
if (trim(data) == "0" || trim(data) == "false") |
|
{ |
|
temp_box.truncated = false; |
|
} |
|
else |
|
{ |
|
temp_box.truncated = true; |
|
} |
|
} |
|
else if (ts.back() == "occluded" && ts[ts.size()-2] == "object") |
|
{ |
|
if (trim(data) == "0" || trim(data) == "false") |
|
{ |
|
temp_box.occluded = false; |
|
} |
|
else |
|
{ |
|
temp_box.occluded = true; |
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
virtual void processing_instruction ( |
|
const unsigned long , |
|
const std::string& , |
|
const std::string& |
|
) |
|
{ |
|
} |
|
}; |
|
|
|
|
|
|
|
class xml_error_handler : public error_handler |
|
{ |
|
public: |
|
virtual void error ( |
|
const unsigned long |
|
) { } |
|
|
|
virtual void fatal_error ( |
|
const unsigned long line_number |
|
) |
|
{ |
|
std::ostringstream sout; |
|
sout << "There is a fatal error on line " << line_number << " so parsing will now halt."; |
|
throw dlib::error(sout.str()); |
|
} |
|
}; |
|
|
|
|
|
|
|
void parse_annotation_file( |
|
const std::string& file, |
|
dlib::image_dataset_metadata::image& img, |
|
std::string& dataset_name |
|
) |
|
{ |
|
doc_handler dh(img, dataset_name); |
|
xml_error_handler eh; |
|
|
|
xml_parser::kernel_1a parser; |
|
parser.add_document_handler(dh); |
|
parser.add_error_handler(eh); |
|
|
|
ifstream fin(file.c_str()); |
|
if (!fin) |
|
throw dlib::error("Unable to open file " + file); |
|
parser.parse(fin); |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
void convert_pascal_xml( |
|
const command_line_parser& parser |
|
) |
|
{ |
|
cout << "Convert from PASCAL XML annotation format..." << endl; |
|
|
|
dlib::image_dataset_metadata::dataset dataset; |
|
|
|
std::string name; |
|
dlib::image_dataset_metadata::image img; |
|
|
|
const std::string filename = parser.option("c").argument(); |
|
|
|
|
|
make_empty_file(filename); |
|
const std::string parent_dir = get_parent_directory(file(filename)).full_name(); |
|
|
|
for (unsigned long i = 0; i < parser.number_of_arguments(); ++i) |
|
{ |
|
try |
|
{ |
|
parse_annotation_file(parser[i], img, name); |
|
const string root = get_parent_directory(get_parent_directory(file(parser[i]))).full_name(); |
|
const string img_path = root + directory::get_separator() + "JPEGImages" + directory::get_separator(); |
|
|
|
dataset.name = name; |
|
img.filename = strip_path(img_path + img.filename, parent_dir); |
|
dataset.images.push_back(img); |
|
|
|
} |
|
catch (exception& ) |
|
{ |
|
cout << "Error while processing file " << parser[i] << endl << endl; |
|
throw; |
|
} |
|
} |
|
|
|
save_image_dataset_metadata(dataset, filename); |
|
} |
|
|
|
|