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

    #include <vector>
    #include "../matrix.h"
    #include "../geometry.h"
    #include "../image_processing/full_object_detection_abstract.h"

    namespace dlib
    {

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

        class example_feature_extractor
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object defines the interface a feature extractor must implement if it
                    is to be used with the scan_image_custom object defined at the bottom of
                    this file.

                    In this case, the purpose of a feature extractor is to associated a
                    complete feature vector with each rectangle in an image.  In particular,
                    each rectangle is scored by taking the dot product between this feature
                    vector and a weight vector.  If this score is greater than a threshold then
                    the rectangle is output as a detection.
            !*/

        public:

            template <
                typename image_type
                >
            void load (
                const image_type& image,
                std::vector<rectangle>& candidate_objects
            );
            /*!
                ensures
                    - Loads the given image into this feature extractor.  This means that
                      subsequent calls to get_feature_vector() will return the feature vector
                      corresponding to locations in the image given to load().
                    - #candidate_objects == a set of bounding boxes in the given image that
                      might contain objects of interest.  These are the locations that will be
                      checked for the presents of objects when this feature extractor is used
                      with the scan_image_custom object.

            !*/

            void copy_configuration (
                const feature_extractor& item
            );
            /*!
                ensures
                    - Copies all the state information of item into *this, except for state
                      information populated by load().  More precisely, given two
                      feature extractor objects S1 and S2, the following sequence of
                      instructions should always result in both of them having the exact same
                      state:
                        S2.copy_configuration(S1);
                        S1.load(img, temp);
                        S2.load(img, temp);
            !*/

            unsigned long get_num_dimensions (
            ) const;
            /*!
                ensures
                    - returns the dimensionality of the feature vectors output by this object.
            !*/

            void get_feature_vector (
                const rectangle& obj,
                matrix<double,0,1>& psi
            ) const;
            /*!
                requires
                    - psi.size() >= get_num_dimensions()
                      (i.e. psi must have preallocated its memory before this function is called)
                ensures
                    - This function computes the feature vector associated with the given rectangle
                      in obj.  This rectangle is interpreted as a bounding box within the last image
                      given to this->load() and a feature vector describing that bounding box is
                      output into psi.
                    - The feature vector is added into psi.  That is, it does not overwrite the
                      previous contents of psi, but instead, it adds the vector to psi.
                    - The dimensionality of the vector added to psi is get_num_dimensions().  This
                      means that elements of psi after psi(get_num_dimensions()-1) are not modified.
                    - #psi.size() == psi.size()
                      (i.e. this function does not change the size of the psi vector)
            !*/

            double compute_object_score (
                const matrix<double,0,1>& w,
                const rectangle& obj
            ) const;
            /*!
                requires
                    - w.size() >= get_num_dimensions()
                ensures
                    - This function returns the dot product between the feature vector for
                      object box obj and the given w vector.  That is, this function computes
                      the same number as the following code snippet:
                         matrix<double,0,1> psi(w.size());
                         psi = 0;
                         get_feature_vector(obj, psi);
                         return dot(psi, w);
                      The point of the compute_object_score() routine is to compute this dot
                      product in a much more efficient way than directly calling
                      get_feature_vector() and dot().  Therefore, compute_object_score() is an
                      optional function.  If you can't think of a faster way to compute these
                      scores then do not implement compute_object_score() and the
                      scan_image_custom object will simply compute these scores for you.
                      However, it is often the case that there is something clever you can do
                      to make this computation faster.  If that is the case, then you can
                      provide an implementation of this function with your feature extractor
                      and then scan_image_custom will use it instead of using the default
                      calculation method shown in the above code snippet.
            !*/

        };

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

        void serialize(
            const feature_extractor& item,
            std::ostream& out
        );
        /*!
            provides serialization support
        !*/

        void deserialize(
            feature_extractor& item,
            std::istream& in
        );
        /*!
            provides deserialization support
        !*/

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

        template <
            typename Feature_extractor_type
            >
        class scan_image_custom : noncopyable
        {
            /*!
                REQUIREMENTS ON Feature_extractor_type
                    - must be an object with an interface compatible with the
                      example_feature_extractor defined at the top of this file.

                INITIAL VALUE
                    - is_loaded_with_image() == false

                WHAT THIS OBJECT REPRESENTS
                    This object is a tool for running a classifier over an image with the goal
                    of localizing each object present.  The localization is in the form of the
                    bounding box around each object of interest.

                    Unlike the scan_image_pyramid and scan_image_boxes objects, this image
                    scanner delegates all the work of constructing the object feature vector to
                    its Feature_extractor_type template argument.  That is, scan_image_custom
                    simply asks the supplied feature extractor what boxes in the image we
                    should investigate and then asks the feature extractor for the complete
                    feature vector for each box.  That is, scan_image_custom does not apply any
                    kind of pyramiding or other higher level processing to the features coming
                    out of the feature extractor.  That means that when you use
                    scan_image_custom it is completely up to you to define the feature vector
                    used with each image box.

                THREAD SAFETY
                    Concurrent access to an instance of this object is not safe and should be
                    protected by a mutex lock except for the case where you are copying the
                    configuration (via copy_configuration()) of a scan_image_custom object to
                    many other threads.  In this case, it is safe to copy the configuration of
                    a shared object so long as no other operations are performed on it.
            !*/

        public:

            typedef matrix<double,0,1> feature_vector_type;
            typedef Feature_extractor_type feature_extractor_type;

            scan_image_custom (
            );
            /*!
                ensures
                    - this object is properly initialized
            !*/

            template <
                typename image_type
                >
            void load (
                const image_type& img
            );
            /*!
                requires
                    - image_type must be a type with the following properties:
                        - image_type objects can be loaded into Feature_extractor_type
                          objects via Feature_extractor_type::load().
                ensures
                    - #is_loaded_with_image() == true
                    - Calls get_feature_extractor().load() on the given image.  That is, we
                      will have loaded the image into the feature extractor in this
                      scan_image_custom object.  We will also have stored the candidate
                      object locations generated by the feature extractor and will scan
                      over them when this->detect() is called.
                    - This object is ready to run a classifier over img to detect object
                      locations.  Call detect() to do this.
            !*/

            bool is_loaded_with_image (
            ) const;
            /*!
                ensures
                    - returns true if this object has been loaded with an image to process and
                      false otherwise.
            !*/

            const feature_extractor_type& get_feature_extractor (
            ) const;
            /*!
                ensures
                    - returns a const reference to the feature_extractor_type object used
                      internally for local feature extraction.
            !*/

            void copy_configuration(
                const feature_extractor_type& fe
            );
            /*!
                ensures
                    - This function performs the equivalent of
                      get_feature_extractor().copy_configuration(fe) (i.e. this function allows
                      you to configure the parameters of the underlying feature extractor used
                      by a scan_image_custom object)
            !*/

            void copy_configuration (
                const scan_image_custom& item
            );
            /*!
                ensures
                    - Copies all the state information of item into *this, except for state
                      information populated by load().  More precisely, given two
                      scan_image_custom objects S1 and S2, the following sequence of
                      instructions should always result in both of them having the exact same
                      state:
                        S2.copy_configuration(S1);
                        S1.load(img);
                        S2.load(img);
            !*/

            long get_num_dimensions (
            ) const;
            /*!
                ensures
                    - returns the number of dimensions in the feature vector for a candidate
                      object location.  That is, this function returns get_feature_extractor().get_num_dimensions().
            !*/

            void detect (
                const feature_vector_type& w,
                std::vector<std::pair<double, rectangle> >& dets,
                const double thresh
            ) const;
            /*!
                requires
                    - w.size() >= get_num_dimensions()
                    - is_loaded_with_image() == true
                ensures
                    - Scans over all the candidate object locations produced by the feature
                      extractor during image loading and stores all detections into #dets.
                    - for all valid i:
                        - #dets[i].second == The candidate object location which produced this
                          detection.  This rectangle gives the location of the detection.
                        - #dets[i].first == The score for this detection.  This value is equal
                          to dot(w, feature vector for this candidate object location).
                        - #dets[i].first >= thresh
                    - #dets will be sorted in descending order.
                      (i.e.  #dets[i].first >= #dets[j].first for all i, and j>i)
                    - Elements of w beyond index get_num_dimensions()-1 are ignored.  I.e. only
                      the first get_num_dimensions() are used.
                    - Note that no form of non-max suppression is performed.  If a locations
                      has a score >= thresh then it is reported in #dets.
            !*/

            void get_feature_vector (
                const full_object_detection& obj,
                feature_vector_type& psi
            ) const;
            /*!
                requires
                    - obj.num_parts() == 0
                    - is_loaded_with_image() == true
                    - psi.size() >= get_num_dimensions()
                      (i.e. psi must have preallocated its memory before this function is called)
                ensures
                    - This function allows you to determine the feature vector used for a
                      candidate object location output from detect().  Note that this vector is
                      added to psi.  Note also that you must use get_full_object_detection() to
                      convert a rectangle from detect() into the needed full_object_detection.
                    - The dimensionality of the vector added to psi is get_num_dimensions().  This
                      means that elements of psi after psi(get_num_dimensions()-1) are not modified.
                    - Since scan_image_custom only searches a limited set of object locations,
                      not all possible rectangles can be output by detect().  So in the case
                      where obj.get_rect() could not arise from a call to detect(), this
                      function will map obj.get_rect() to the nearest possible rectangle and
                      then add the feature vector for the mapped rectangle into #psi.
                    - get_best_matching_rect(obj.get_rect()) == the rectangle obj.get_rect()
                      gets mapped to for feature extraction.
            !*/

            full_object_detection get_full_object_detection (
                const rectangle& rect,
                const feature_vector_type& w
            ) const;
            /*!
                ensures
                    - returns full_object_detection(rect)
                      (This function is here only for compatibility with the scan_image_pyramid
                      object)
            !*/

            const rectangle get_best_matching_rect (
                const rectangle& rect
            ) const;
            /*!
                requires
                    - is_loaded_with_image() == true
                ensures
                    - Since scan_image_custom only searches a limited set of object locations,
                      not all possible rectangles can be represented.  Therefore, this function
                      allows you to supply a rectangle and obtain the nearest possible
                      candidate object location rectangle.
            !*/

            unsigned long get_num_detection_templates (
            ) const { return 1; }
            /*!
                ensures
                    - returns 1.  Note that this function is here only for compatibility with
                      the scan_image_pyramid object.  Notionally, its return value indicates
                      that a scan_image_custom object is always ready to detect objects once an
                      image has been loaded.
            !*/

            unsigned long get_num_movable_components_per_detection_template (
            ) const { return 0; }
            /*!
                ensures
                    - returns 0.  Note that this function is here only for compatibility with
                      the scan_image_pyramid object.  Its return value means that this object
                      does not support using movable part models.
            !*/

        };

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

        template <typename T>
        void serialize (
            const scan_image_custom<T>& item,
            std::ostream& out
        );
        /*!
            provides serialization support
        !*/

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

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

    }

    #endif // DLIB_SCAN_IMAGE_CuSTOM_ABSTRACT_Hh_