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

#include "../geometry.h"
#include <vector>
#include "../image_processing/generic_image.h"

namespace dlib
{

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

    struct neighbors_24
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a pixel neighborhood generating functor for 
                use with the label_connected_blobs() routine defined below.
        !*/

        void operator() (
            const point& p,
            std::vector<point>& neighbors
        ) const;
        /*!
            ensures
                - adds the 24 neighboring pixels surrounding p into neighbors
        !*/
    };

    struct neighbors_8 
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a pixel neighborhood generating functor for 
                use with the label_connected_blobs() routine defined below.
        !*/

        void operator() (
            const point& p,
            std::vector<point>& neighbors
        ) const;
        /*!
            ensures
                - adds the 8 neighboring pixels surrounding p into neighbors
        !*/
    };

    struct neighbors_4 
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a pixel neighborhood generating functor for 
                use with the label_connected_blobs() routine defined below.
        !*/

        void operator() (
            const point& p,
            std::vector<point>& neighbors
        ) const;
        /*!
            ensures
                - adds the 4 neighboring pixels of p into neighbors.  These
                  are the ones immediately to the left, top, right, and bottom.
        !*/
    };

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

    struct connected_if_both_not_zero
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a pixel connection testing functor for use
                with the label_connected_blobs() routine defined below.
        !*/

        template <typename image_view_type>
        bool operator() (
            const image_view_type& img,
            const point& a,
            const point& b
        ) const
        {
            return (img[a.y()][a.x()] != 0 && img[b.y()][b.x()] != 0);
        }
    };

    struct connected_if_equal
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a pixel connection testing functor for use
                with the label_connected_blobs() routine defined below.
        !*/

        template <typename image_view_type>
        bool operator() (
            const image_view_type& img,
            const point& a,
            const point& b
        ) const
        {
            return (img[a.y()][a.x()] == img[b.y()][b.x()]);
        }
    };

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

    struct zero_pixels_are_background
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a background testing functor for use
                with the label_connected_blobs() routine defined below.
        !*/

        template <typename image_view_type>
        bool operator() (
            const image_view_type& img,
            const point& p
        ) const
        {
            return img[p.y()][p.x()] == 0;
        }

    };

    struct nothing_is_background 
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a background testing functor for use
                with the label_connected_blobs() routine defined below.
        !*/

        template <typename image_view_type>
        bool operator() (
            const image_view_type&, 
            const point& 
        ) const
        {
            return false;
        }

    };

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

    template <
        typename image_type,
        typename label_image_type,
        typename background_functor_type,
        typename neighbors_functor_type,
        typename connected_functor_type
        >
    unsigned long label_connected_blobs (
        const image_type& img,
        const background_functor_type& is_background,
        const neighbors_functor_type&  get_neighbors,
        const connected_functor_type&  is_connected,
        label_image_type& label_img
    );
    /*!
        requires
            - image_type == an image object that implements the interface defined in
              dlib/image_processing/generic_image.h 
            - label_image_type == an image object that implements the interface defined in
              dlib/image_processing/generic_image.h and it must contain integer pixels.
            - is_background(img, point(c,r)) is a legal expression that evaluates to a bool.
            - is_connected(img, point(c,r), point(c2,r2)) is a legal expression that
              evaluates to a bool.
            - get_neighbors(point(c,r), neighbors) is a legal expression where neighbors 
              is of type std::vector<point>.
            - is_same_object(img, label_img) == false
        ensures
            - This function labels each of the connected blobs in img with a unique integer 
              label.  
            - An image can be thought of as a graph where pixels A and B are connected if 
              and only if the following two statements are satisfied:
                - is_connected(img,A,B) == true
                - get_neighbors(A, neighbors) results in neighbors containing B or
                  get_neighbors(B, neighbors) results in neighbors containing A.
              Then this function can be understood as labeling all the connected components 
              of this pixel graph such that all pixels in a component get the same label while
              pixels in different components get different labels.  Note that there is a 
              special "background" component determined by is_background().  Any pixels which 
              are "background" always get a blob id of 0 regardless of any other considerations.
            - #label_img.nr() == img.nr()
            - #label_img.nc() == img.nc()
            - for all valid r and c:
                - #label_img[r][c] == the blob label number for pixel img[r][c].  
                - #label_img[r][c] >= 0
                - if (is_background(img, point(c,r))) then
                    - #label_img[r][c] == 0
                - else
                    - #label_img[r][c] != 0
            - if (img.size() != 0) then 
                - returns max(mat(#label_img))+1
                  (i.e. returns a number one greater than the maximum blob id number, 
                  this is the number of blobs found.)
            - else
                - returns 0
            - blob labels are contiguous, therefore, the number returned by this function is
              the number of blobs in the image (including the background blob).
            - It is guaranteed that is_connected() and is_background() will never be 
              called with points outside the image.
    !*/

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

    template <
        typename in_image_type,
        typename out_image_type
        >
    unsigned long label_connected_blobs_watershed (
        const in_image_type& img,
        out_image_type& labels,
        typename pixel_traits<typename image_traits<in_image_type>::pixel_type>::basic_pixel_type background_thresh,
        const double smoothing = 0
    );
    /*!
        requires
            - in_image_type == an image object that implements the interface defined in
              dlib/image_processing/generic_image.h 
            - out_image_type == an image object that implements the interface defined in
              dlib/image_processing/generic_image.h 
            - in_image_type must contain a grayscale pixel type. 
            - out_image_type must contain an unsigned integer pixel type.
            - is_same_object(img, labels) == false
            - smoothing >= 0
        ensures
            - This routine performs a watershed segmentation of the given input image and
              labels each resulting flooding region with a unique integer label. It does
              this by marking the brightest pixels as sources of flooding and then flood
              fills the image outward from those sources.  Each flooded area is labeled
              with the identity of the source pixel and flooding stops when another flooded
              area is reached or pixels with values < background_thresh are encountered.  
            - The flooding will also overrun a source pixel if that source pixel has yet to
              label any neighboring pixels.  This behavior helps to mitigate spurious
              splits of objects due to noise.  You can further control this behavior by
              setting the smoothing parameter.  The flooding will take place on an image
              that has been Gaussian blurred with a sigma==smoothing.  So setting smoothing
              to a larger number will in general cause more regions to be merged together.
              Note that the smoothing parameter has no effect on the interpretation of
              background_thresh since the decision of "background or not background" is
              always made relative to the unsmoothed input image.
            - #labels.nr() == img.nr()
            - #labels.nc() == img.nc()
            - for all valid r and c:
                - if (img[r][c] < background_thresh) then
                    - #labels[r][c] == 0, (i.e. the pixel is labeled as background)
                - else
                    - #labels[r][c] == an integer value indicating the identity of the segment
                      containing the pixel img[r][c].  
            - returns the number of labeled segments, including the background segment.
              Therefore, the returned number is 1+(the max value in #labels).
    !*/

    template <
        typename in_image_type,
        typename out_image_type
        >
    unsigned long label_connected_blobs_watershed (
        const in_image_type& img,
        out_image_type& labels
    );
    /*!
        simply invokes: return label_connected_blobs_watershed(img, labels, partition_pixels(img));
    !*/

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

}

#endif // DLIB_LABEL_CONNeCTED_BLOBS_ABSTRACT_H_