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

    #include "../geometry.h"
    #include <vector>
    #include "box_overlap_testing_abstract.h"
    #include "full_object_detection_abstract.h"

    namespace dlib
    {

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

        template <
            typename image_scanner_type
            >
        class object_detector
        {
            /*!
                REQUIREMENTS ON image_scanner_type
                    image_scanner_type must be an implementation of
                    dlib/image_processing/scan_image_pyramid_abstract.h or
                    dlib/image_processing/scan_image_boxes_abstract.h

                WHAT THIS OBJECT REPRESENTS
                    This object is a tool for detecting the positions of objects in an image.
                    In particular, it is a simple container to aggregate an instance of the
                    scan_image_pyramid or scan_image_boxes classes, the weight vector needed by
                    one of these image scanners, and finally an instance of test_box_overlap.
                    The test_box_overlap object is used to perform non-max suppression on the
                    output of the image scanner object.
            !*/
        public:
            typedef typename image_scanner_type::feature_vector_type feature_vector_type;

            object_detector (
            );
            /*!
                ensures
                    - This detector won't generate any detections when
                      presented with an image.
            !*/

            object_detector (
                const object_detector& item
            );
            /*!
                ensures
                    - #*this is a copy of item
                    - #get_scanner() == item.get_scanner()
                      (note that only the "configuration" of item.get_scanner() is copied.
                      I.e. the copy is done using copy_configuration())
            !*/

            object_detector (
                const image_scanner_type& scanner,
                const test_box_overlap& overlap_tester,
                const feature_vector_type& w
            );
            /*!
                requires
                    - w.size() == scanner.get_num_dimensions() + 1
                    - scanner.get_num_detection_templates() > 0
                ensures
                    - When the operator() member function is called it will
                      invoke scanner.detect(w,dets,w(w.size()-1)), suppress
                      overlapping detections, and then report the results.
                    - when #*this is used to detect objects, the set of
                      output detections will never contain any overlaps
                      with respect to overlap_tester.  That is, for all
                      pairs of returned detections A and B, we will always
                      have: overlap_tester(A,B) == false
                    - #get_w() == w
                    - #get_overlap_tester() == overlap_tester
                    - #get_scanner() == scanner
                      (note that only the "configuration" of scanner is copied.
                      I.e. the copy is done using copy_configuration())
            !*/

            const feature_vector_type& get_w (
            ) const;
            /*!
                ensures
                    - returns the weight vector used by this object
            !*/

            const test_box_overlap& get_overlap_tester (
            ) const;
            /*!
                ensures
                    - returns the overlap tester used by this object
            !*/

            const image_scanner_type& get_scanner (
            ) const;
            /*!
                ensures
                    - returns the image scanner used by this object.
            !*/

            object_detector& operator= (
                const object_detector& item
            );
            /*!
                ensures
                    - #*this is a copy of item
                    - #get_scanner() == item.get_scanner()
                      (note that only the "configuration" of item.get_scanner() is
                      copied.  I.e. the copy is done using copy_configuration())
                    - returns #*this
            !*/

            template <
                typename image_type
                >
            std::vector<rectangle> operator() (
                const image_type& img,
                const adjust_threshold = 0
            );
            /*!
                requires
                    - img == an object which can be accepted by image_scanner_type::load()
                ensures
                    - performs object detection on the given image and returns a
                      vector which indicates the locations of all detected objects.
                    - The returned vector will be sorted in the sense that the highest
                      confidence detections come first.  E.g. element 0 is the best detection,
                      element 1 the next best, and so on.
                    - #get_scanner() will have been loaded with img. Therefore, you can call
                      #get_scanner().get_feature_vector() to obtain the feature vectors or
                      #get_scanner().get_full_object_detection() to get the
                      full_object_detections for the resulting object detection boxes.
                    - The detection threshold is adjusted by having adjust_threshold added to
                      it.  Therefore, an adjust_threshold value > 0 makes detecting objects
                      harder while a negative value makes it easier.  This means that, for
                      example, you can obtain the maximum possible number of detections by
                      setting adjust_threshold equal to negative infinity.
            !*/

            template <
                typename image_type
                >
            void operator() (
                const image_type& img,
                std::vector<std::pair<double, rectangle> >& dets,
                double adjust_threshold = 0
            );
            /*!
                requires
                    - img == an object which can be accepted by image_scanner_type::load()
                ensures
                    - performs object detection on the given image and stores the
                      detected objects into #dets.  In particular, we will have that:
                        - #dets is sorted such that the highest confidence detections
                          come first.  E.g. element 0 is the best detection, element 1
                          the next best, and so on.
                        - #dets.size() == the number of detected objects.
                        - #dets[i].first gives the "detection confidence", of the i-th
                          detection.  This is the detection value output by the scanner minus
                          the threshold value stored at the end of the weight vector in get_w().
                        - #dets[i].second == the bounding box for the i-th detection.
                    - #get_scanner() will have been loaded with img. Therefore, you can call
                      #get_scanner().get_feature_vector() to obtain the feature vectors or
                      #get_scanner().get_full_object_detection() to get the
                      full_object_detections for the resulting object detection boxes.
                    - The detection threshold is adjusted by having adjust_threshold added to
                      it.  Therefore, an adjust_threshold value > 0 makes detecting objects
                      harder while a negative value makes it easier.  Moreover, the following
                      will be true for all valid i:
                        - #dets[i].first >= adjust_threshold
                      This means that, for example, you can obtain the maximum possible number
                      of detections by setting adjust_threshold equal to negative infinity.
            !*/

            template <
                typename image_type
                >
            void operator() (
                const image_type& img,
                std::vector<std::pair<double, full_object_detection> >& final_dets,
                double adjust_threshold = 0
            );
            /*!
                requires
                    - img == an object which can be accepted by image_scanner_type::load()
                ensures
                    - This function is identical to the above operator() routine, except that
                      it outputs full_object_detections instead of rectangles.  This means that
                      the output includes part locations.  In particular, calling this function
                      is the same as calling the above operator() routine and then using
                      get_scanner().get_full_object_detection() to resolve all the rectangles
                      into full_object_detections.  Therefore, this version of operator() is
                      simply a convenience function for performing this set of operations.
            !*/

            template <
                typename image_type
                >
            void operator() (
                const image_type& img,
                std::vector<full_object_detection>& final_dets,
                double adjust_threshold = 0
            );
            /*!
                requires
                    - img == an object which can be accepted by image_scanner_type::load()
                ensures
                    - This function is identical to the above operator() routine, except that
                      it doesn't include a double valued score.  That is, it just outputs the
                      full_object_detections.
            !*/
        };

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

        template <typename T>
        void serialize (
            const object_detector<T>& item,
            std::ostream& out
        );
        /*!
            provides serialization support.  Note that this function only saves the
            configuration part of item.get_scanner().  That is, we use the scanner's
            copy_configuration() function to get a copy of the scanner that doesn't contain any
            loaded image data and we then save just the configuration part of the scanner.
            This means that any serialized object_detectors won't remember any images they have
            processed but will otherwise contain all their state and be able to detect objects
            in new images.
        !*/

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

        template <typename T>
        void deserialize (
            object_detector<T>& item,
            std::istream& in
        );
        /*!
            provides deserialization support
        !*/

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

    }

    #endif // DLIB_OBJECT_DeTECTOR_ABSTRACT_H__