    // The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
    /*
        Helper definitions for working with the PASCAL VOC2012 dataset.
    */

    #ifndef PASCAL_VOC_2012_H_
    #define PASCAL_VOC_2012_H_

    #include <dlib/pixel.h>
    #include <dlib/dnn.h>

    // ----------------------------------------------------------------------------------------

    // The PASCAL VOC2012 dataset contains 20 ground-truth classes + background.  Each class
    // is represented using an RGB color value.  We associate each class also to an index in the
    // range [0, 20], used internally by the network. To convert the ground-truth data to
    // something that the network can efficiently digest, we need to be able to map the RGB
    // values to the corresponding indexes.

    struct Voc2012class {
        Voc2012class(uint16_t index, const dlib::rgb_pixel& rgb_label, const std::string& classlabel)
            : index(index), rgb_label(rgb_label), classlabel(classlabel)
        {}

        // The index of the class. In the PASCAL VOC 2012 dataset, indexes from 0 to 20 are valid.
        const uint16_t index = 0;

        // The corresponding RGB representation of the class.
        const dlib::rgb_pixel rgb_label;

        // The label of the class in plain text.
        const std::string classlabel;
    };

    namespace {
        constexpr int class_count = 21; // background + 20 classes

        const std::vector<Voc2012class> classes = {
            Voc2012class(0, dlib::rgb_pixel(0, 0, 0), ""), // background

            // The cream-colored `void' label is used in border regions and to mask difficult objects
            // (see http://host.robots.ox.ac.uk/pascal/VOC/voc2012/htmldoc/devkit_doc.html)
            Voc2012class(dlib::loss_multiclass_log_per_pixel_::label_to_ignore,
                dlib::rgb_pixel(224, 224, 192), "border"),

            Voc2012class(1,  dlib::rgb_pixel(128,   0,   0), "aeroplane"),
            Voc2012class(2,  dlib::rgb_pixel(  0, 128,   0), "bicycle"),
            Voc2012class(3,  dlib::rgb_pixel(128, 128,   0), "bird"),
            Voc2012class(4,  dlib::rgb_pixel(  0,   0, 128), "boat"),
            Voc2012class(5,  dlib::rgb_pixel(128,   0, 128), "bottle"),
            Voc2012class(6,  dlib::rgb_pixel(  0, 128, 128), "bus"),
            Voc2012class(7,  dlib::rgb_pixel(128, 128, 128), "car"),
            Voc2012class(8,  dlib::rgb_pixel( 64,   0,   0), "cat"),
            Voc2012class(9,  dlib::rgb_pixel(192,   0,   0), "chair"),
            Voc2012class(10, dlib::rgb_pixel( 64, 128,   0), "cow"),
            Voc2012class(11, dlib::rgb_pixel(192, 128,   0), "diningtable"),
            Voc2012class(12, dlib::rgb_pixel( 64,   0, 128), "dog"),
            Voc2012class(13, dlib::rgb_pixel(192,   0, 128), "horse"),
            Voc2012class(14, dlib::rgb_pixel( 64, 128, 128), "motorbike"),
            Voc2012class(15, dlib::rgb_pixel(192, 128, 128), "person"),
            Voc2012class(16, dlib::rgb_pixel(  0,  64,   0), "pottedplant"),
            Voc2012class(17, dlib::rgb_pixel(128,  64,   0), "sheep"),
            Voc2012class(18, dlib::rgb_pixel(  0, 192,   0), "sofa"),
            Voc2012class(19, dlib::rgb_pixel(128, 192,   0), "train"),
            Voc2012class(20, dlib::rgb_pixel(  0,  64, 128), "tvmonitor"),
        };
    }

    template <typename Predicate>
    const Voc2012class& find_voc2012_class(Predicate predicate)
    {
        const auto i = std::find_if(classes.begin(), classes.end(), predicate);

        if (i != classes.end())
        {
            return *i;
        }
        else
        {
            throw std::runtime_error("Unable to find a matching VOC2012 class");
        }
    }

    // ----------------------------------------------------------------------------------------

    // The names of the input image and the associated RGB label image in the PASCAL VOC 2012
    // data set.
    struct image_info
    {
        std::string image_filename;
        std::string class_label_filename;
        std::string instance_label_filename;
    };

    // Read the list of image files belonging to either the "train", "trainval", or "val" set
    // of the PASCAL VOC2012 data.
    std::vector<image_info> get_pascal_voc2012_listing(
        const std::string& voc2012_folder,
        const std::string& file = "train" // "train", "trainval", or "val"
    )
    {
        std::ifstream in(voc2012_folder + "/ImageSets/Segmentation/" + file + ".txt");

        std::vector<image_info> results;

        while (in)
        {
            std::string basename;
            in >> basename;

            if (!basename.empty())
            {
                image_info info;
                info.image_filename          = voc2012_folder + "/JPEGImages/"         + basename + ".jpg";
                info.class_label_filename    = voc2012_folder + "/SegmentationClass/"  + basename + ".png";
                info.instance_label_filename = voc2012_folder + "/SegmentationObject/" + basename + ".png";
                results.push_back(info);
            }
        }

        return results;
    }

    // Read the list of image files belong to the "train" set of the PASCAL VOC2012 data.
    std::vector<image_info> get_pascal_voc2012_train_listing(
        const std::string& voc2012_folder
    )
    {
        return get_pascal_voc2012_listing(voc2012_folder, "train");
    }

    // Read the list of image files belong to the "val" set of the PASCAL VOC2012 data.
    std::vector<image_info> get_pascal_voc2012_val_listing(
        const std::string& voc2012_folder
    )
    {
        return get_pascal_voc2012_listing(voc2012_folder, "val");
    }

    // Given an RGB representation, find the corresponding PASCAL VOC2012 class
    // (e.g., 'dog').
    const Voc2012class& find_voc2012_class(const dlib::rgb_pixel& rgb_label)
    {
        return find_voc2012_class(
            [&rgb_label](const Voc2012class& voc2012class)
            {
                return rgb_label == voc2012class.rgb_label;
            }
        );
    }

    // ----------------------------------------------------------------------------------------

    // Convert an RGB class label to an index in the range [0, 20].
    inline uint16_t rgb_label_to_index_label(const dlib::rgb_pixel& rgb_label)
    {
        return find_voc2012_class(rgb_label).index;
    }

    // Convert an image containing RGB class labels to a corresponding
    // image containing indexes in the range [0, 20].
    void rgb_label_image_to_index_label_image(
        const dlib::matrix<dlib::rgb_pixel>& rgb_label_image,
        dlib::matrix<uint16_t>& index_label_image
    )
    {
        const long nr = rgb_label_image.nr();
        const long nc = rgb_label_image.nc();

        index_label_image.set_size(nr, nc);

        for (long r = 0; r < nr; ++r)
        {
            for (long c = 0; c < nc; ++c)
            {
                index_label_image(r, c) = rgb_label_to_index_label(rgb_label_image(r, c));
            }
        }
    }

    #endif // PASCAL_VOC_2012_H_