/** @file  psImageConvolve.h
 *
 *  @brief image convolution functionality
 *
 *  @ingroup Transform
 *
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-02-17 19:26:24 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */

#ifndef PS_IMAGE_CONVOLVE_H
#define PS_IMAGE_CONVOLVE_H

#include "psImage.h"
#include "psVector.h"
#include "psType.h"

#define PS_TYPE_KERNEL PS_TYPE_F32     /**< the data member to use for kernel image */
#define PS_TYPE_KERNEL_DATA F32        /**< the data member to use for kernel image */
#define PS_TYPE_KERNEL_NAME "psF32"    /**< the data type for kernel as a string */

typedef psF32 psKernelType;

/** A convolution kernel */
typedef struct
{
    psImage* image;                    ///< Kernel data, in the form of an image
    psS32 xMin;                          ///< Most negative x index
    psS32 yMin;                          ///< Most negative y index
    psS32 xMax;                          ///< Most positive x index
    psS32 yMax;                          ///< Most positive y index
    psKernelType** kernel;             ///< Pointer to the kernel data
    psKernelType** p_kernelRows;       ///< Pointer to the rows of the kernel data; not intended for user use.
}
psKernel;

/** Allocates a convolution kernel of the given range
 *
 *  In order to perform a convolution, we need to define the convolution 
 *  kernel. We need a more general object than a psImage so that we can 
 *  incorporate the offset from the (0, 0) pixel to the (0, 0) value of the 
 *  kernel. It might be convenient to allow both positive and negative 
 *  indices to convey the positive and negative shifts. One might consider 
 *  setting the x0 and y0 members of a psImage to the appropriate offsets, 
 *  but this is not the purpose of these members, and doing so may affect the 
 *  behavior of other psImage operations.
 *
 *  This construction allows the kernel member to use negative indices, while 
 *  preserving the location of psMemBlocks relative to allocated memory.
 *
 *  The maximum extent of the kernel shifts shall be defined by the xMin, 
 *  xMax, yMin and yMax members. Note that xMin and yMin, under normal 
 *  circumstances, should be negative numbers. That is, 
 *  myKernel->kernel[-3][-2] may be defined if yMin and xMin are equal to or 
 *  more negative than -3 and -2, respectively.
 *
 *  In the event that one of the minimum values is greater than the 
 *  corresponding maximum value, the function shall generate a warning, and 
 *  the offending values shall be exchanged.
 *
 *  @return psKernel*          A new kernel object
 */
psKernel* psKernelAlloc(
    psS32 xMin,                          ///< Most negative x index
    psS32 xMax,                          ///< Most positive x index
    psS32 yMin,                          ///< Most negative y index
    psS32 yMax                           ///< Most positive y index
);

/** Generates a kernel given a list of shift values
 *
 *  Given a list of values (e.g., shifts made in the course of OT guiding), 
 *  psKernelGenerate shall return the appropriate kernel.  The vectors xShifts 
 *  and yShifts, which are a list of shifts relative to some starting point, 
 *  will be supplied by the user. The elements of the vectors should be of an 
 *  integer type; otherwise the values shall be truncated to integers. The 
 *  output kernel shall be normalized such that the sum over the kernel is 
 *  unity. 
 *
 *  If the vectors are not of the same number of elements, then the function 
 *  shall generate a warning shall be generated, following which, the longer 
 *  vector trimmed to the length of the shorter, and the function shall continue.
 *
 *  @return psKernel*    new Kernel object
 */
psKernel* psKernelGenerate(
    const psVector* tShifts,           ///< list of time shifts
    const psVector* xShifts,           ///< list of x-axis shifts
    const psVector* yShifts,           ///< list of y-axis shifts
    psBool relative
);

/** convolve an image with a kernel
 *
 *  Given an input image and the convolution kernel, psImageConvolve shall 
 *  convolve the input image, in, with the kernel, kernel and return the 
 *  convolved image, out.
 * 
 *  Two methods shall be available for the convolution: if direct is true, 
 *  then the convolution shall be performed in real space (appropriate for 
 *  small kernels); otherwise, the convolution shall be performed using Fast 
 *  Fourier Transforms (FFTs; appropriate for larger kernels). The latter 
 *  option involves padding the input image, copying the kernel into an image 
 *  of the same size as the padded input image, performing an FFT on each, 
 *  multiplying the FFTs, and performing an inverse FFT before trimming the 
 *  image back to the original size.
 *
 *  @return psImage*  resulting image 
 */
psImage* psImageConvolve(
    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
    const psImage* in,                 ///< the psImage to convolve
    const psKernel* kernel,            ///< kernel to colvolve with
    psBool direct                        ///< specifies method, true=direct convolution, false=fourier
);

#endif
