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

    #include "hessian_pyramid_abstract.h"
    #include "../geometry/vector_abstract.h"
    #include "../matrix/matrix_abstract.h"

    namespace dlib
    {
        /*
            The functions in this file implement the components of the SURF algorithm
            for extracting scale invariant feature descriptors from images.

            For the full story on what this algorithm does and how it works
            you should refer to the following papers.

            This is the original paper which introduced the algorithm:
                SURF: Speeded Up Robust Features
                By Herbert Bay, Tinne Tuytelaars, and Luc Van Gool

            This paper provides a nice detailed overview of how the algorithm works:
                Notes on the OpenSURF Library by Christopher Evans
        */

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

        double gaussian (
            double x,
            double y,
            double sig
        );
        /*!
            requires
                - sig > 0
            ensures
                - computes and returns the value of a 2D Gaussian function with mean 0
                  and standard deviation sig at the given (x,y) point.
        !*/

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

        template <typename integral_image_type, typename T>
        double compute_dominant_angle (
            const integral_image_type& img,
            const dlib::vector<T,2>& center,
            const double& scale
        );
        /*!
            requires
                - integral_image_type == an object such as dlib::integral_image or another
                  type that implements the interface defined in image_transforms/integral_image_abstract.h
                - scale > 0
                - get_rect(img).contains(centered_rect(center, 17*scale, 17*scale)) == true
                  (i.e. center can't be within 17*scale pixels of the edge of the image)
            ensures
                - computes and returns the dominant angle (i.e. the angle of the dominant gradient)
                  at the given center point and scale in img.
                - The returned angle is in radians.  Specifically, if the angle is described by
                  a vector vect then the angle is exactly the value of std::atan2(vect.y(), vect.x())
        !*/

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

        template <typename integral_image_type, typename T, typename MM, typename L>
        void compute_surf_descriptor (
            const integral_image_type& img,
            const dlib::vector<T,2>& center,
            const double scale,
            const double angle,
            matrix<double,64,1,MM,L>& des
        )
        /*!
            requires
                - integral_image_type == an object such as dlib::integral_image or another
                  type that implements the interface defined in image_transforms/integral_image_abstract.h
                - scale > 0
                - get_rect(img).contains(centered_rect(center, 32*scale, 32*scale)) == true
                  (i.e. center can't be within 32*scale pixels of the edge of the image)
            ensures
                - computes the 64 dimensional SURF descriptor vector of a box centered
                  at the given center point, tilted at an angle determined by the given
                  angle, and sized according to the given scale.
                - #des == the computed SURF descriptor vector extracted from the img object.
                - The angle is measured in radians and measures the degree of counter-clockwise
                  rotation around the center point.  This is the same kind of rotation as is
                  performed by the dlib::rotate_point() function.
        !*/

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

        struct surf_point
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object represents a detected SURF point.  The meanings of
                    its fields are defined below in the get_surf_points() function.
            !*/

            interest_point p;
            matrix<double,64,1> des;
            double angle;
        };

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

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

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

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

        template <typename image_type>
        const std::vector<surf_point> get_surf_points (
            const image_type& img,
            long max_points = 10000,
            double detection_threshold = 30.0
        );
        /*!
            requires
                - max_points > 0
                - detection_threshold >= 0
                - image_type == a type that implements the array2d/array2d_kernel_abstract.h interface
                - pixel_traits<image_type::type> must be defined
            ensures
                - This function runs the complete SURF algorithm on the given input image and
                  returns the points it found.
                - returns a vector V such that:
                    - V.size() <= max_points
                    - for all valid i:
                        - V[i] == a SURF point found in the given input image img
                        - V[i].p == the interest_point extracted from the hessian pyramid for this
                          SURF point.
                        - V[i].des == the SURF descriptor for this point (calculated using
                          compute_surf_descriptor())
                        - V[i].angle == the angle of the SURF box at this point (calculated using
                          compute_dominant_angle())
                        - V[i].p.score >= detection_threshold
        !*/

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

    }

    #endif // DLIB_SURf_ABSTRACT_H_