# Copyright (C) 2003 Peter J. Verveer
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met: 
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following
#    disclaimer in the documentation and/or other materials provided
#    with the distribution.
#
# 3. The name of the author may not be used to endorse or promote
#    products derived from this software without specific prior
#    written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.      

import types
import math
import numarray
import _ni_support
import _nd_image
import morphology
from numarray.generic import _broadcast

def label(input, structure = None, output = None):
    """Label an array of objects.

    The structure that defines the object connections must be
    symmetric.  If no structuring element is provided an element is
    generated with a squared connectivity equal to one. An optional
    output can be given. This function ret"""

    if structure == None:
        rank = _ni_support._array_rank(input)
        structure = morphology.generate_binary_structure(rank, 1)
    return _nd_image.label(input, structure, output)


def find_objects(input, max_label = 0):
    """Find objects in a labeled array.

    The input must be an array with labeled objects. A list of slices
    into the array is returned that contain the objects. The list
    represents a sequence of the numbered objects. If a number is
    missing, None is returned instead of a slice. If max_label > 0, it
    gives the largest object number that is searched for, otherwise
    all are returned."""

    if max_label < 1:
        input = numarray.inputarray(input)
        if len(input.shape) < 1:
            max_label = int(input);
        else:
            max_label = input.max()
    return _nd_image.find_objects(input, max_label)

def sum(input, labels = None, index = None):
    """Calculate the sum of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    if labels != None:
        input = numarray.inputarray(input)
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    return _nd_image.sum(input, labels, index)


def mean(input, labels = None, index = None):
    """Calculate the mean of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    if labels != None:
        labels = _broadcast(labels, input.shape)
    return _nd_image.mean(input, labels, index)


def variance(input, labels = None, index = None):
    """Calculate the variance of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    if labels != None:
        input = numarray.inputarray(input)
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    return _nd_image.variance(input, labels, index)


def standard_deviation(input, labels = None, index = None):
    """Calculate the standard deviation of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    var = variance(input, labels, index)
    if (isinstance(var, types.ListType)):
        return [math.sqrt(x) for x in var]
    else:
        return math.sqrt(var)


def minimum(input, labels = None, index = None):
    """Calculate the minimum of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    if labels != None:
        input = numarray.inputarray(input)
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    return _nd_image.minimum(input, labels, index)


def maximum(input, labels = None, index = None):
    """Calculate the maximum of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    if labels != None:
        input = numarray.inputarray(input)
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    return _nd_image.maximum(input, labels, index)


def _index_to_position(index, shape):
    """Convert a linear index to a position"""
    if len(shape) > 0:
        pos = []
        stride = numarray.multiply.reduce(shape)
        for size in shape:
            stride = stride // size
            pos.append(index // stride)
            index -= pos[-1] * stride
        return tuple(pos)
    else:
        return 0
    

def minimum_position(input, labels = None, index = None):
    """Find the position of the minimum of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    input = numarray.inputarray(input)
    if labels != None:
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    pos = _nd_image.minimum_position(input, labels, index)
    if (isinstance(pos, types.ListType)):
        return [_index_to_position(x, input.shape) for x in pos]
    else:
        return _index_to_position(pos, input.shape)


def maximum_position(input, labels = None, index = None):
    """Find the position of the maximum of the values of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    input = numarray.inputarray(input)
    if labels != None:
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    pos = _nd_image.maximum_position(input, labels, index)
    if (isinstance(pos, types.ListType)):
        return [_index_to_position(x, input.shape) for x in pos]
    else:
        return _index_to_position(pos, input.shape)


def center_of_mass(input, labels = None, index = None):
    """Calculate the center of mass of of the array.

    The index parameter is a single label number or a sequence of
    label numbers of the objects to be measured. If index is None, all
    values are used where labels is larger than zero.

    """
    if labels != None:
        input = numarray.inputarray(input)
        labels = numarray.inputarray(labels)
        labels = _broadcast(labels, input.shape)
    return _nd_image.center_of_mass(input, labels, index)
