    // Copyright (C) 2011  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_OBJECT_DeTECTOR_H__
    #define DLIB_OBJECT_DeTECTOR_H__

    #include "object_detector_abstract.h"
    #include "../geometry.h"
    #include <vector>
    #include "box_overlap_testing.h"
    #include "full_object_detection.h"

    namespace dlib
    {

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

        template <
            typename image_scanner_type
            >
        class object_detector
        {
        public:
            typedef typename image_scanner_type::feature_vector_type feature_vector_type;

            object_detector (
            );

            object_detector (
                const object_detector& item
            );

            object_detector (
                const image_scanner_type& scanner_,
                const test_box_overlap& overlap_tester_,
                const feature_vector_type& w_
            );

            const feature_vector_type& get_w (
            ) const { return w; }

            const test_box_overlap& get_overlap_tester (
            ) const;

            const image_scanner_type& get_scanner (
            ) const;

            object_detector& operator= (
                const object_detector& item
            );

            template <
                typename image_type
                >
            std::vector<rectangle> operator() (
                const image_type& img,
                double adjust_threshold = 0
            );

            template <
                typename image_type
                >
            void operator() (
                const image_type& img,
                std::vector<std::pair<double, rectangle> >& final_dets,
                double adjust_threshold = 0
            );

            template <
                typename image_type
                >
            void operator() (
                const image_type& img,
                std::vector<std::pair<double, full_object_detection> >& final_dets,
                double adjust_threshold = 0
            );

            template <
                typename image_type
                >
            void operator() (
                const image_type& img,
                std::vector<full_object_detection>& final_dets,
                double adjust_threshold = 0
            );

            template <typename T>
            friend void serialize (
                const object_detector<T>& item,
                std::ostream& out
            );

            template <typename T>
            friend void deserialize (
                object_detector<T>& item,
                std::istream& in
            );

        private:

            bool overlaps_any_box (
                const std::vector<rectangle>& rects,
                const dlib::rectangle& rect
            ) const
            {
                for (unsigned long i = 0; i < rects.size(); ++i)
                {
                    if (boxes_overlap(rects[i], rect))
                        return true;
                }
                return false;
            }

            bool overlaps_any_box (
                const std::vector<std::pair<double,rectangle> >& rects,
                const dlib::rectangle& rect
            ) const
            {
                for (unsigned long i = 0; i < rects.size(); ++i)
                {
                    if (boxes_overlap(rects[i].second, rect))
                        return true;
                }
                return false;
            }

            test_box_overlap boxes_overlap;
            feature_vector_type w;
            image_scanner_type scanner;
        };

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

        template <typename T>
        void serialize (
            const object_detector<T>& item,
            std::ostream& out
        )
        {
            int version = 1;
            serialize(version, out);

            T scanner;
            scanner.copy_configuration(item.scanner);
            serialize(scanner, out);
            serialize(item.w, out);
            serialize(item.boxes_overlap, out);
        }

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

        template <typename T>
        void deserialize (
            object_detector<T>& item,
            std::istream& in
        )
        {
            int version = 0;
            deserialize(version, in);
            if (version != 1)
                throw serialization_error("Unexpected version encountered while deserializing a dlib::object_detector object.");

            deserialize(item.scanner, in);
            deserialize(item.w, in);
            deserialize(item.boxes_overlap, in);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    //                      object_detector member functions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename image_scanner_type
            >
        object_detector<image_scanner_type>::
        object_detector (
        )
        {
        }

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

        template <
            typename image_scanner_type
            >
        object_detector<image_scanner_type>::
        object_detector (
            const object_detector& item
        )
        {
            boxes_overlap = item.boxes_overlap;
            w = item.w;
            scanner.copy_configuration(item.scanner);
        }

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

        template <
            typename image_scanner_type
            >
        object_detector<image_scanner_type>::
        object_detector (
            const image_scanner_type& scanner_,
            const test_box_overlap& overlap_tester,
            const feature_vector_type& w_
        ) :
            boxes_overlap(overlap_tester),
            w(w_)
        {
            // make sure requires clause is not broken
            DLIB_ASSERT(scanner_.get_num_detection_templates() > 0 &&
                        w_.size() == scanner_.get_num_dimensions() + 1,
                "\t object_detector::object_detector(scanner_,overlap_tester,w_)"
                << "\n\t Invalid inputs were given to this function "
                << "\n\t scanner_.get_num_detection_templates(): " << scanner_.get_num_detection_templates()
                << "\n\t w_.size():                     " << w_.size()
                << "\n\t scanner_.get_num_dimensions(): " << scanner_.get_num_dimensions()
                << "\n\t this: " << this
                );

            scanner.copy_configuration(scanner_);
        }

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

        template <
            typename image_scanner_type
            >
        object_detector<image_scanner_type>& object_detector<image_scanner_type>::
        operator= (
            const object_detector& item
        )
        {
            if (this == &item)
                return *this;

            boxes_overlap = item.boxes_overlap;
            w = item.w;
            scanner.copy_configuration(item.scanner);
            return *this;
        }

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

        template <
            typename image_scanner_type
            >
        template <
            typename image_type
            >
        std::vector<rectangle> object_detector<image_scanner_type>::
        operator() (
            const image_type& img,
            double adjust_threshold
        )
        {
            std::vector<rectangle> final_dets;
            if (w.size() != 0)
            {
                std::vector<std::pair<double, rectangle> > dets;
                const double thresh = w(scanner.get_num_dimensions());

                scanner.load(img);
                scanner.detect(w, dets, thresh + adjust_threshold);

                for (unsigned long i = 0; i < dets.size(); ++i)
                {
                    if (overlaps_any_box(final_dets, dets[i].second))
                        continue;

                    final_dets.push_back(dets[i].second);
                }
            }

            return final_dets;
        }

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

        template <
            typename image_scanner_type
            >
        template <
            typename image_type
            >
        void object_detector<image_scanner_type>::
        operator() (
            const image_type& img,
            std::vector<std::pair<double, rectangle> >& final_dets,
            double adjust_threshold
        )
        {
            final_dets.clear();
            if (w.size() != 0)
            {
                std::vector<std::pair<double, rectangle> > dets;
                const double thresh = w(scanner.get_num_dimensions());

                scanner.load(img);
                scanner.detect(w, dets, thresh + adjust_threshold);

                for (unsigned long i = 0; i < dets.size(); ++i)
                {
                    if (overlaps_any_box(final_dets, dets[i].second))
                        continue;

                    dets[i].first -= thresh;
                    final_dets.push_back(dets[i]);
                }
            }
        }

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

        template <
            typename image_scanner_type
            >
        template <
            typename image_type
            >
        void object_detector<image_scanner_type>::
        operator() (
            const image_type& img,
            std::vector<std::pair<double, full_object_detection> >& final_dets,
            double adjust_threshold
        )
        {
            std::vector<std::pair<double, rectangle> > temp_dets;
            (*this)(img,temp_dets,adjust_threshold);

            final_dets.clear();
            final_dets.reserve(temp_dets.size());

            // convert all the rectangle detections into full_object_detections.
            for (unsigned long i = 0; i < temp_dets.size(); ++i)
            {
                final_dets.push_back(std::make_pair(temp_dets[i].first,
                                                    scanner.get_full_object_detection(temp_dets[i].second, w)));
            }
        }

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

        template <
            typename image_scanner_type
            >
        template <
            typename image_type
            >
        void object_detector<image_scanner_type>::
        operator() (
            const image_type& img,
            std::vector<full_object_detection>& final_dets,
            double adjust_threshold
        )
        {
            std::vector<std::pair<double, rectangle> > temp_dets;
            (*this)(img,temp_dets,adjust_threshold);

            final_dets.clear();
            final_dets.reserve(temp_dets.size());

            // convert all the rectangle detections into full_object_detections.
            for (unsigned long i = 0; i < temp_dets.size(); ++i)
            {
                final_dets.push_back(scanner.get_full_object_detection(temp_dets[i].second, w));
            }
        }

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

        template <
            typename image_scanner_type
            >
        const test_box_overlap& object_detector<image_scanner_type>::
        get_overlap_tester (
        ) const
        {
            return boxes_overlap;
        }

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

        template <
            typename image_scanner_type
            >
        const image_scanner_type& object_detector<image_scanner_type>::
        get_scanner (
        ) const
        {
            return scanner;
        }

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

    }

    #endif // DLIB_OBJECT_DeTECTOR_H__