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

#include "../pixel.h"
#include "../matrix.h"

namespace dlib
{

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

    template <
        typename in_image_type,
        typename out_image_type,
        typename EXP,
        typename T
        >
    rectangle spatially_filter_image (
        const in_image_type& in_img,
        out_image_type& out_img,
        const matrix_exp<EXP>& filter,
        T scale = 1,
        bool use_abs = false,
        bool add_to = false
    );
    /*!
        requires
            - in_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - out_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - pixel_traits<typename in_image_type::type>::has_alpha == false
            - pixel_traits<typename out_image_type::type>::has_alpha == false 
            - is_same_object(in_img, out_img) == false 
            - T must be some scalar type
            - scale != 0
            - filter.nr() % 2 == 1  (i.e. must be odd)
            - filter.nc() % 2 == 1  (i.e. must be odd)
        ensures
            - Applies the given spatial filter to in_img and stores the result in out_img (i.e.
              cross-correlates in_img with filter).  Also divides each resulting pixel by scale.  
            - The intermediate filter computations will be carried out using variables of type EXP::type.
              This is whatever scalar type is used inside the filter matrix. 
            - Pixel values are stored into out_img using the assign_pixel() function and therefore
              any applicable color space conversion or value saturation is performed.  Note that if 
              add_to is true then the filtered output value will be added to out_img rather than 
              overwriting the original value.
            - if (pixel_traits<typename in_image_type::type>::grayscale == false) then
                - the pixel values are converted to the HSI color space and the filtering
                  is done on the intensity channel only.
            - if (use_abs == true) then
                - pixel values after filtering that are < 0 are converted to their absolute values.
            - Pixels close enough to the edge of in_img to not have the filter still fit 
              inside the image are always set to zero.
            - #out_img.nc() == in_img.nc()
            - #out_img.nr() == in_img.nr()
            - returns a rectangle which indicates what pixels in #out_img are considered 
              non-border pixels and therefore contain output from the filter.
    !*/

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

    template <
        typename in_image_type,
        typename out_image_type,
        typename EXP1,
        typename EXP2,
        typename T
        >
    rectangle spatially_filter_image_separable (
        const in_image_type& in_img,
        out_image_type& out_img,
        const matrix_exp<EXP1>& row_filter,
        const matrix_exp<EXP2>& col_filter,
        T scale = 1,
        bool use_abs = false,
        bool add_to = false
    );
    /*!
        requires
            - in_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - out_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - pixel_traits<typename in_image_type::type>::has_alpha == false
            - pixel_traits<typename out_image_type::type>::has_alpha == false 
            - is_same_object(in_img, out_img) == false 
            - T must be some scalar type
            - scale != 0
            - is_vector(row_filter) == true
            - is_vector(col_filter) == true
            - row_filter.size() % 2 == 1  (i.e. must be odd)
            - col_filter.size() % 2 == 1  (i.e. must be odd)
        ensures
            - Applies the given separable spatial filter to in_img and stores the result in out_img.  
              Also divides each resulting pixel by scale.  Calling this function has the same
              effect as calling the regular spatially_filter_image() routine with a filter,
              FILT, defined as follows: 
                - FILT(r,c) == col_filter(r)*row_filter(c)
            - The intermediate filter computations will be carried out using variables of type EXP1::type.
              This is whatever scalar type is used inside the row_filter matrix. 
            - Pixel values are stored into out_img using the assign_pixel() function and therefore
              any applicable color space conversion or value saturation is performed.  Note that if 
              add_to is true then the filtered output value will be added to out_img rather than 
              overwriting the original value.
            - if (pixel_traits<typename in_image_type::type>::grayscale == false) then
                - the pixel values are converted to the HSI color space and the filtering
                  is done on the intensity channel only.
            - if (use_abs == true) then
                - pixel values after filtering that are < 0 are converted to their absolute values
            - Pixels close enough to the edge of in_img to not have the filter still fit 
              inside the image are always set to zero.
            - #out_img.nc() == in_img.nc()
            - #out_img.nr() == in_img.nr()
            - returns a rectangle which indicates what pixels in #out_img are considered 
              non-border pixels and therefore contain output from the filter.
    !*/

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

    template <
        typename in_image_type,
        typename out_image_type,
        typename EXP1,
        typename EXP2,
        typename T
        >
    rectangle spatially_filter_image_separable_down (
        const unsigned long downsample,
        const in_image_type& in_img,
        out_image_type& out_img,
        const matrix_exp<EXP1>& row_filter,
        const matrix_exp<EXP2>& col_filter,
        T scale = 1,
        bool use_abs = false,
        bool add_to = false
    );
    /*!
        requires
            - in_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - out_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - pixel_traits<typename in_image_type::type>::has_alpha == false
            - pixel_traits<typename out_image_type::type>::has_alpha == false 
            - is_same_object(in_img, out_img) == false 
            - T must be some scalar type
            - scale != 0
            - is_vector(row_filter) == true
            - is_vector(col_filter) == true
            - row_filter.size() % 2 == 1  (i.e. must be odd)
            - col_filter.size() % 2 == 1  (i.e. must be odd)
            - downsample > 0
        ensures
            - This function is equivalent to calling 
              spatially_filter_image_separable(in_img,out_img,row_filter,col_filter,scale,use_abs,add_to)
              and then downsampling the output image by a factor of downsample.  Therefore, 
              we will have that:
                - #out_img.nr() == ceil((double)in_img.nr()/downsample)
                - #out_img.nc() == ceil((double)in_img.nc()/downsample)
                - #out_img[r][c] == filtered pixel corresponding to in_img[r*downsample][c*downsample]
            - returns a rectangle which indicates what pixels in #out_img are considered 
              non-border pixels and therefore contain output from the filter.
            - Note that the first row and column of non-zero padded data are the following
                - first_row == ceil(floor(col_filter.size()/2.0)/downsample)
                - first_col == ceil(floor(row_filter.size()/2.0)/downsample)
    !*/

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

    template <
        long NR,
        long NC,
        typename T,
        typename U,
        typename in_image_type
        >
    inline void separable_3x3_filter_block_grayscale (
        T (&block)[NR][NC],
        const in_image_type& img,
        const long& r,
        const long& c,
        const U& fe1, 
        const U& fm,  
        const U& fe2 
    );
    /*!
        requires
            - in_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - pixel_traits<typename in_image_type::type> must be defined 
            - T and U should be scalar types
            - shrink_rect(get_rect(img),1).contains(c,r)
            - shrink_rect(get_rect(img),1).contains(c+NC-1,r+NR-1)
        ensures
            - Filters the image in the sub-window of img defined by a rectangle 
              with its upper left corner at (c,r) and lower right at (c+NC-1,r+NR-1).
            - The output of the filter is stored in #block.  Note that img will be 
              interpreted as a grayscale image.
            - The filter used is defined by the separable filter [fe1 fm fe2].  So the
              spatial filter is thus:
                fe1*fe1  fe1*fm  fe2*fe1
                fe1*fm   fm*fm   fe2*fm
                fe1*fe2  fe2*fm  fe2*fe2
    !*/

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

    template <
        long NR,
        long NC,
        typename T,
        typename U,
        typename in_image_type
        >
    inline void separable_3x3_filter_block_rgb (
        T (&block)[NR][NC],
        const in_image_type& img,
        const long& r,
        const long& c,
        const U& fe1, 
        const U& fm, 
        const U& fe2
    );
    /*!
        requires
            - in_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - pixel_traits<typename in_image_type::type>::rgb == true
            - T should be a struct with .red .green and .blue members.
            - U should be a scalar type
            - shrink_rect(get_rect(img),1).contains(c,r)
            - shrink_rect(get_rect(img),1).contains(c+NC-1,r+NR-1)
        ensures
            - Filters the image in the sub-window of img defined by a rectangle 
              with its upper left corner at (c,r) and lower right at (c+NC-1,r+NR-1).
            - The output of the filter is stored in #block.  Note that the filter is applied
              to each color component independently.
            - The filter used is defined by the separable filter [fe1 fm fe2].  So the
              spatial filter is thus:
                fe1*fe1  fe1*fm  fe2*fe1
                fe1*fm   fm*fm   fe2*fm
                fe1*fe2  fe2*fm  fe2*fe2
    !*/

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

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

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

    template <
        typename T
        >
    matrix<T,0,1> create_gaussian_filter (
        double sigma,
        int size 
    );
    /*!
        requires
            - sigma > 0
            - size > 0 
            - size is an odd number
        ensures
            - returns a separable Gaussian filter F such that:
                - is_vector(F) == true 
                - F.size() == size 
                - F is suitable for use with the spatially_filter_image_separable() routine
                  and its use with this function corresponds to running a Gaussian filter 
                  of sigma width over an image.
    !*/

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

    template <
        typename in_image_type,
        typename out_image_type
        >
    void gaussian_blur (
        const in_image_type& in_img,
        out_image_type& out_img,
        double sigma = 1,
        int max_size = 1001
    );
    /*!
        requires
            - in_image_type  == is an implementation of array2d/array2d_kernel_abstract.h
            - out_image_type == is an implementation of array2d/array2d_kernel_abstract.h
            - pixel_traits<typename in_image_type::type>::has_alpha == false
            - pixel_traits<typename out_image_type::type>::has_alpha == false 
            - is_same_object(in_img, out_img) == false 
            - sigma > 0
            - max_size > 0
            - max_size is an odd number
        ensures
            - Filters in_img with a Gaussian filter of sigma width.  The actual spatial filter will
              be applied to pixel blocks that are at most max_size wide and max_size tall (note that
              this function will automatically select a smaller block size as appropriate).  The 
              results are stored into #out_img.
            - Pixel values are stored into out_img using the assign_pixel() function and therefore
              any applicable color space conversion or value saturation is performed.
            - if (pixel_traits<typename in_image_type::type>::grayscale == false) then
                - the pixel values are converted to the HSI color space and the filtering
                  is done on the intensity channel only.
            - Pixels close enough to the edge of in_img to not have the filter still fit 
              inside the image are set to zero.
            - #out_img.nc() == in_img.nc()
            - #out_img.nr() == in_img.nr()
    !*/

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

    template <
        typename image_type1, 
        typename image_type2
        >
    void sum_filter (
        const image_type1& img,
        image_type2& out,
        const rectangle& rect
    );
    /*!
        requires
            - out.nr() == img.nr() 
            - out.nc() == img.nc()
            - image_type1 == an implementation of array2d/array2d_kernel_abstract.h
              and it must contain a scalar type
            - image_type2 == an implementation of array2d/array2d_kernel_abstract.h
              and it must contain a scalar type
            - is_same_object(img,out) == false
        ensures
            - for all valid r and c:
                - let SUM(r,c) == sum of pixels from img which are inside the rectangle 
                  translate_rect(rect, point(c,r)).
                - #out[r][c] == out[r][c] + SUM(r,c)
    !*/

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

    template <
        typename image_type1, 
        typename image_type2
        >
    void sum_filter_assign (
        const image_type1& img,
        image_type2& out,
        const rectangle& rect
    );
    /*!
        requires
            - out.nr() == img.nr() 
            - out.nc() == img.nc()
            - image_type1 == an implementation of array2d/array2d_kernel_abstract.h
              and it must contain a scalar type
            - image_type2 == an implementation of array2d/array2d_kernel_abstract.h
              and it must contain a scalar type
            - is_same_object(img,out) == false
        ensures
            - for all valid r and c:
                - let SUM(r,c) == sum of pixels from img which are inside the rectangle 
                  translate_rect(rect, point(c,r)).
                - #out[r][c] == SUM(r,c)
    !*/

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

    template <
        typename image_type1, 
        typename image_type2
        >
    void max_filter (
        image_type1& img,
        image_type2& out,
        const long width,
        const long height,
        const typename image_type1::type& thresh
    );
    /*!
        requires
            - out.nr() == img.nr() 
            - out.nc() == img.nc()
            - image_type1 == an implementation of array2d/array2d_kernel_abstract.h
              and it must contain a scalar type
            - image_type2 == an implementation of array2d/array2d_kernel_abstract.h
              and it must contain a scalar type
            - is_same_object(img,out) == false
            - width > 0 && height > 0
        ensures
            - for all valid r and c:
                - let MAX(r,c) == maximum of pixels from img which are inside the rectangle 
                  centered_rect(point(c,r), width, height)
                - if (MAX(r,c) >= thresh)
                    - #out[r][c] == out[r][c] + MAX(r,c)
                - else
                    - #out[r][c] == out[r][c] + thresh 
            - Does not change the size of img.
            - Uses img as scratch space.  Therefore, the pixel values in img will have
              been modified by this function.  That is, max_filter() destroys the contents
              of img. 
    !*/

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

}

#endif // DLIB_SPATIAL_FILTERINg_ABSTRACT_