/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2013, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Peng Xiao, pengxiao@multicorewareinc.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other oclMaterials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors 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 Intel Corporation or contributors 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.
//
//M*/

#ifndef __OCL_RETINA_HPP__
#define __OCL_RETINA_HPP__

#include "precomp.hpp"

#ifdef HAVE_OPENCV_OCL

// please refer to c++ headers for API comments
namespace cv
{
namespace bioinspired
{
namespace ocl
{
void normalizeGrayOutputCentredSigmoide(const float meanValue, const float sensitivity, cv::ocl::oclMat &in, cv::ocl::oclMat &out, const float maxValue = 255.f);
void normalizeGrayOutput_0_maxOutputValue(cv::ocl::oclMat &inputOutputBuffer, const float maxOutputValue = 255.0);
void normalizeGrayOutputNearZeroCentreredSigmoide(cv::ocl::oclMat &inputPicture, cv::ocl::oclMat &outputBuffer, const float sensitivity = 40, const float maxOutputValue = 255.0f);
void centerReductImageLuminance(cv::ocl::oclMat &inputOutputBuffer);

class BasicRetinaFilter
{
public:
    BasicRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns, const unsigned int parametersListSize = 1, const bool useProgressiveFilter = false);
    ~BasicRetinaFilter();
    inline void clearOutputBuffer()
    {
        _filterOutput = 0;
    }
    inline void clearSecondaryBuffer()
    {
        _localBuffer = 0;
    }
    inline void clearAllBuffers()
    {
        clearOutputBuffer();
        clearSecondaryBuffer();
    }
    void  resize(const unsigned int NBrows, const unsigned int NBcolumns);
    const cv::ocl::oclMat &runFilter_LPfilter(const cv::ocl::oclMat &inputFrame, const unsigned int filterIndex = 0);
    void  runFilter_LPfilter(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &outputFrame, const unsigned int filterIndex = 0);
    void  runFilter_LPfilter_Autonomous(cv::ocl::oclMat &inputOutputFrame, const unsigned int filterIndex = 0);
    const cv::ocl::oclMat &runFilter_LocalAdapdation(const cv::ocl::oclMat &inputOutputFrame, const cv::ocl::oclMat &localLuminance);
    void  runFilter_LocalAdapdation(const cv::ocl::oclMat &inputFrame, const cv::ocl::oclMat &localLuminance, cv::ocl::oclMat &outputFrame);
    const cv::ocl::oclMat &runFilter_LocalAdapdation_autonomous(const cv::ocl::oclMat &inputFrame);
    void  runFilter_LocalAdapdation_autonomous(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &outputFrame);
    void  setLPfilterParameters(const float beta, const float tau, const float k, const unsigned int filterIndex = 0);
    inline void setV0CompressionParameter(const float v0, const float maxInputValue, const float)
    {
        _v0 = v0 * maxInputValue;
        _localLuminanceFactor = v0;
        _localLuminanceAddon = maxInputValue * (1.0f - v0);
        _maxInputValue = maxInputValue;
    }
    inline void setV0CompressionParameter(const float v0, const float meanLuminance)
    {
        this->setV0CompressionParameter(v0, _maxInputValue, meanLuminance);
    }
    inline void setV0CompressionParameter(const float v0)
    {
        _v0 = v0 * _maxInputValue;
        _localLuminanceFactor = v0;
        _localLuminanceAddon = _maxInputValue * (1.0f - v0);
    }
    inline void setV0CompressionParameterToneMapping(const float v0, const float maxInputValue, const float meanLuminance = 128.0f)
    {
        _v0 = v0 * maxInputValue;
        _localLuminanceFactor = 1.0f;
        _localLuminanceAddon = meanLuminance * _v0;
        _maxInputValue = maxInputValue;
    }
    inline void updateCompressionParameter(const float meanLuminance)
    {
        _localLuminanceFactor = 1;
        _localLuminanceAddon = meanLuminance * _v0;
    }
    inline float getV0CompressionParameter()
    {
        return _v0 / _maxInputValue;
    }
    inline const cv::ocl::oclMat &getOutput() const
    {
        return _filterOutput;
    }
    inline unsigned int getNBrows()
    {
        return _filterOutput.rows;
    }
    inline unsigned int getNBcolumns()
    {
        return _filterOutput.cols;
    }
    inline unsigned int getNBpixels()
    {
        return _filterOutput.size().area();
    }
    inline void normalizeGrayOutput_0_maxOutputValue(const float maxValue)
    {
        ocl::normalizeGrayOutput_0_maxOutputValue(_filterOutput, maxValue);
    }
    inline void normalizeGrayOutputCentredSigmoide()
    {
        ocl::normalizeGrayOutputCentredSigmoide(0.0, 2.0, _filterOutput, _filterOutput);
    }
    inline void centerReductImageLuminance()
    {
        ocl::centerReductImageLuminance(_filterOutput);
    }
    inline float getMaxInputValue()
    {
        return this->_maxInputValue;
    }
    inline void setMaxInputValue(const float newMaxInputValue)
    {
        this->_maxInputValue = newMaxInputValue;
    }

protected:

    int _NBrows;
    int _NBcols;
    unsigned int _halfNBrows;
    unsigned int _halfNBcolumns;

    cv::ocl::oclMat _filterOutput;
    cv::ocl::oclMat _localBuffer;

    std::valarray <float>_filteringCoeficientsTable;
    float _v0;
    float _maxInputValue;
    float _meanInputValue;
    float _localLuminanceFactor;
    float _localLuminanceAddon;

    float _a;
    float _tau;
    float _gain;

    void _spatiotemporalLPfilter(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &LPfilterOutput, const unsigned int coefTableOffset = 0);
    float _squaringSpatiotemporalLPfilter(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &outputFrame, const unsigned int filterIndex = 0);
    void _spatiotemporalLPfilter_Irregular(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &outputFrame, const unsigned int filterIndex = 0);
    void _localSquaringSpatioTemporalLPfilter(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &LPfilterOutput, const unsigned int *integrationAreas, const unsigned int filterIndex = 0);
    void _localLuminanceAdaptation(const cv::ocl::oclMat &inputFrame, const cv::ocl::oclMat &localLuminance, cv::ocl::oclMat &outputFrame, const bool updateLuminanceMean = true);
    void _localLuminanceAdaptation(cv::ocl::oclMat &inputOutputFrame, const cv::ocl::oclMat &localLuminance);
    void _localLuminanceAdaptationPosNegValues(const cv::ocl::oclMat &inputFrame, const cv::ocl::oclMat &localLuminance, float *outputFrame);
    void _horizontalCausalFilter_addInput(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &outputFrame);
    void _horizontalAnticausalFilter(cv::ocl::oclMat &outputFrame);
    void _verticalCausalFilter(cv::ocl::oclMat &outputFrame);
    void _horizontalAnticausalFilter_Irregular(cv::ocl::oclMat &outputFrame, const cv::ocl::oclMat &spatialConstantBuffer);
    void _verticalCausalFilter_Irregular(cv::ocl::oclMat &outputFrame, const cv::ocl::oclMat &spatialConstantBuffer);
    void _verticalAnticausalFilter_multGain(cv::ocl::oclMat &outputFrame);
};

class MagnoRetinaFilter: public BasicRetinaFilter
{
public:
    MagnoRetinaFilter(const unsigned int NBrows, const unsigned int NBcolumns);
    virtual ~MagnoRetinaFilter();
    void clearAllBuffers();
    void resize(const unsigned int NBrows, const unsigned int NBcolumns);
    void setCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float localAdaptIntegration_tau, const float localAdaptIntegration_k);

    const cv::ocl::oclMat &runFilter(const cv::ocl::oclMat &OPL_ON, const cv::ocl::oclMat &OPL_OFF);

    inline const cv::ocl::oclMat &getMagnoON() const
    {
        return _magnoXOutputON;
    }
    inline const cv::ocl::oclMat &getMagnoOFF() const
    {
        return _magnoXOutputOFF;
    }
    inline const cv::ocl::oclMat &getMagnoYsaturated() const
    {
        return _magnoYsaturated;
    }
    inline void normalizeGrayOutputNearZeroCentreredSigmoide()
    {
        ocl::normalizeGrayOutputNearZeroCentreredSigmoide(_magnoYOutput, _magnoYsaturated);
    }
    inline float getTemporalConstant()
    {
        return this->_filteringCoeficientsTable[2];
    }
private:
    cv::ocl::oclMat _previousInput_ON;
    cv::ocl::oclMat _previousInput_OFF;
    cv::ocl::oclMat _amacrinCellsTempOutput_ON;
    cv::ocl::oclMat _amacrinCellsTempOutput_OFF;
    cv::ocl::oclMat _magnoXOutputON;
    cv::ocl::oclMat _magnoXOutputOFF;
    cv::ocl::oclMat _localProcessBufferON;
    cv::ocl::oclMat _localProcessBufferOFF;
    cv::ocl::oclMat _magnoYOutput;
    cv::ocl::oclMat _magnoYsaturated;

    float _temporalCoefficient;
    void _amacrineCellsComputing(const cv::ocl::oclMat &OPL_ON,  const cv::ocl::oclMat &OPL_OFF);
};

class ParvoRetinaFilter: public BasicRetinaFilter
{
public:
    ParvoRetinaFilter(const unsigned int NBrows = 480, const unsigned int NBcolumns = 640);
    virtual ~ParvoRetinaFilter();
    void resize(const unsigned int NBrows, const unsigned int NBcolumns);
    void clearAllBuffers();
    void setOPLandParvoFiltersParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2);

    inline void setGanglionCellsLocalAdaptationLPfilterParameters(const float tau, const float k)
    {
        BasicRetinaFilter::setLPfilterParameters(0, tau, k, 2);
    }
    const cv::ocl::oclMat &runFilter(const cv::ocl::oclMat &inputFrame, const bool useParvoOutput = true);

    inline const cv::ocl::oclMat &getPhotoreceptorsLPfilteringOutput() const
    {
        return _photoreceptorsOutput;
    }

    inline const cv::ocl::oclMat &getHorizontalCellsOutput() const
    {
        return _horizontalCellsOutput;
    }

    inline const cv::ocl::oclMat &getParvoON() const
    {
        return _parvocellularOutputON;
    }

    inline const cv::ocl::oclMat &getParvoOFF() const
    {
        return _parvocellularOutputOFF;
    }

    inline const cv::ocl::oclMat &getBipolarCellsON() const
    {
        return _bipolarCellsOutputON;
    }

    inline const cv::ocl::oclMat &getBipolarCellsOFF() const
    {
        return _bipolarCellsOutputOFF;
    }

    inline float getPhotoreceptorsTemporalConstant()
    {
        return this->_filteringCoeficientsTable[2];
    }

    inline float getHcellsTemporalConstant()
    {
        return this->_filteringCoeficientsTable[5];
    }
private:
    cv::ocl::oclMat _photoreceptorsOutput;
    cv::ocl::oclMat _horizontalCellsOutput;
    cv::ocl::oclMat _parvocellularOutputON;
    cv::ocl::oclMat _parvocellularOutputOFF;
    cv::ocl::oclMat _bipolarCellsOutputON;
    cv::ocl::oclMat _bipolarCellsOutputOFF;
    cv::ocl::oclMat _localAdaptationOFF;
    cv::ocl::oclMat _localAdaptationON;
    cv::ocl::oclMat _parvocellularOutputONminusOFF;
    void _OPL_OnOffWaysComputing();
};
class RetinaColor: public BasicRetinaFilter
{
public:
    RetinaColor(const unsigned int NBrows, const unsigned int NBcolumns, const int samplingMethod = RETINA_COLOR_DIAGONAL);
    virtual ~RetinaColor();

    void clearAllBuffers();
    void resize(const unsigned int NBrows, const unsigned int NBcolumns);
    inline void runColorMultiplexing(const cv::ocl::oclMat &inputRGBFrame)
    {
        runColorMultiplexing(inputRGBFrame, _multiplexedFrame);
    }
    void runColorMultiplexing(const cv::ocl::oclMat &demultiplexedInputFrame, cv::ocl::oclMat &multiplexedFrame);
    void runColorDemultiplexing(const cv::ocl::oclMat &multiplexedColorFrame, const bool adaptiveFiltering = false, const float maxInputValue = 255.0);

    void setColorSaturation(const bool saturateColors = true, const float colorSaturationValue = 4.0)
    {
        _saturateColors = saturateColors;
        _colorSaturationValue = colorSaturationValue;
    }

    void setChrominanceLPfilterParameters(const float beta, const float tau, const float k)
    {
        setLPfilterParameters(beta, tau, k);
    }

    bool applyKrauskopfLMS2Acr1cr2Transform(cv::ocl::oclMat &result);
    bool applyLMS2LabTransform(cv::ocl::oclMat &result);
    inline const cv::ocl::oclMat &getMultiplexedFrame() const
    {
        return _multiplexedFrame;
    }

    inline const cv::ocl::oclMat &getDemultiplexedColorFrame() const
    {
        return _demultiplexedColorFrame;
    }

    inline const cv::ocl::oclMat &getLuminance() const
    {
        return _luminance;
    }
    inline const cv::ocl::oclMat &getChrominance() const
    {
        return _chrominance;
    }
    void clipRGBOutput_0_maxInputValue(cv::ocl::oclMat &inputOutputBuffer, const float maxOutputValue = 255.0);
    void normalizeRGBOutput_0_maxOutputValue(const float maxOutputValue = 255.0);
    inline void setDemultiplexedColorFrame(const cv::ocl::oclMat &demultiplexedImage)
    {
        _demultiplexedColorFrame = demultiplexedImage;
    }
protected:
    inline unsigned int bayerSampleOffset(unsigned int index)
    {
        return index + ((index / getNBcolumns()) % 2) * getNBpixels() + ((index % getNBcolumns()) % 2) * getNBpixels();
    }
    inline Rect getROI(int idx)
    {
        return Rect(0, idx * _NBrows, _NBcols, _NBrows);
    }
    int _samplingMethod;
    bool _saturateColors;
    float _colorSaturationValue;
    cv::ocl::oclMat _luminance;
    cv::ocl::oclMat _multiplexedFrame;
    cv::ocl::oclMat _RGBmosaic;
    cv::ocl::oclMat _tempMultiplexedFrame;
    cv::ocl::oclMat _demultiplexedTempBuffer;
    cv::ocl::oclMat _demultiplexedColorFrame;
    cv::ocl::oclMat _chrominance;
    cv::ocl::oclMat _colorLocalDensity;
    cv::ocl::oclMat _imageGradient;

    float _pR, _pG, _pB;
    bool _objectInit;

    void _initColorSampling();
    void _adaptiveSpatialLPfilter(const cv::ocl::oclMat &inputFrame, const cv::ocl::oclMat &gradient, cv::ocl::oclMat &outputFrame);
    void _adaptiveHorizontalCausalFilter_addInput(const cv::ocl::oclMat &inputFrame, const cv::ocl::oclMat &gradient, cv::ocl::oclMat &outputFrame);
    void _adaptiveVerticalAnticausalFilter_multGain(const cv::ocl::oclMat &gradient, cv::ocl::oclMat &outputFrame);
    void _computeGradient(const cv::ocl::oclMat &luminance, cv::ocl::oclMat &gradient);
    void _normalizeOutputs_0_maxOutputValue(void);
    void _applyImageColorSpaceConversion(const cv::ocl::oclMat &inputFrame, cv::ocl::oclMat &outputFrame, const float *transformTable);
};
class RetinaFilter
{
public:
    RetinaFilter(const unsigned int sizeRows, const unsigned int sizeColumns, const bool colorMode = false, const int samplingMethod = RETINA_COLOR_BAYER, const bool useRetinaLogSampling = false, const double reductionFactor = 1.0, const double samplingStrenght = 10.0);
    ~RetinaFilter();

    void clearAllBuffers();
    void resize(const unsigned int NBrows, const unsigned int NBcolumns);
    bool checkInput(const cv::ocl::oclMat &input, const bool colorMode);
    bool runFilter(const cv::ocl::oclMat &imageInput, const bool useAdaptiveFiltering = true, const bool processRetinaParvoMagnoMapping = false, const bool useColorMode = false, const bool inputIsColorMultiplexed = false);

    void setGlobalParameters(const float OPLspatialResponse1 = 0.7, const float OPLtemporalresponse1 = 1, const float OPLassymetryGain = 0, const float OPLspatialResponse2 = 5, const float OPLtemporalresponse2 = 1, const float LPfilterSpatialResponse = 5, const float LPfilterGain = 0, const float LPfilterTemporalresponse = 0, const float MovingContoursExtractorCoefficient = 5, const bool normalizeParvoOutput_0_maxOutputValue = false, const bool normalizeMagnoOutput_0_maxOutputValue = false, const float maxOutputValue = 255.0, const float maxInputValue = 255.0, const float meanValue = 128.0);

    inline void setPhotoreceptorsLocalAdaptationSensitivity(const float V0CompressionParameter)
    {
        _photoreceptorsPrefilter.setV0CompressionParameter(1 - V0CompressionParameter);
        _setInitPeriodCount();
    }

    inline void setParvoGanglionCellsLocalAdaptationSensitivity(const float V0CompressionParameter)
    {
        _ParvoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
        _setInitPeriodCount();
    }

    inline void setGanglionCellsLocalAdaptationLPfilterParameters(const float spatialResponse, const float temporalResponse)
    {
        _ParvoRetinaFilter.setGanglionCellsLocalAdaptationLPfilterParameters(temporalResponse, spatialResponse);
        _setInitPeriodCount();
    };

    inline void setMagnoGanglionCellsLocalAdaptationSensitivity(const float V0CompressionParameter)
    {
        _MagnoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
        _setInitPeriodCount();
    }

    void setOPLandParvoParameters(const float beta1, const float tau1, const float k1, const float beta2, const float tau2, const float k2, const float V0CompressionParameter)
    {
        _ParvoRetinaFilter.setOPLandParvoFiltersParameters(beta1, tau1, k1, beta2, tau2, k2);
        _ParvoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
        _setInitPeriodCount();
    }

    void setMagnoCoefficientsTable(const float parasolCells_beta, const float parasolCells_tau, const float parasolCells_k, const float amacrinCellsTemporalCutFrequency, const float V0CompressionParameter, const float localAdaptintegration_tau, const float localAdaptintegration_k)
    {
        _MagnoRetinaFilter.setCoefficientsTable(parasolCells_beta, parasolCells_tau, parasolCells_k, amacrinCellsTemporalCutFrequency, localAdaptintegration_tau, localAdaptintegration_k);
        _MagnoRetinaFilter.setV0CompressionParameter(V0CompressionParameter);
        _setInitPeriodCount();
    }

    inline void activateNormalizeParvoOutput_0_maxOutputValue(const bool normalizeParvoOutput_0_maxOutputValue)
    {
        _normalizeParvoOutput_0_maxOutputValue = normalizeParvoOutput_0_maxOutputValue;
    }

    inline void activateNormalizeMagnoOutput_0_maxOutputValue(const bool normalizeMagnoOutput_0_maxOutputValue)
    {
        _normalizeMagnoOutput_0_maxOutputValue = normalizeMagnoOutput_0_maxOutputValue;
    }

    inline void setMaxOutputValue(const float maxOutputValue)
    {
        _maxOutputValue = maxOutputValue;
    }

    void setColorMode(const bool desiredColorMode)
    {
        _useColorMode = desiredColorMode;
    }
    inline void setColorSaturation(const bool saturateColors = true, const float colorSaturationValue = 4.0)
    {
        _colorEngine.setColorSaturation(saturateColors, colorSaturationValue);
    }
    inline const cv::ocl::oclMat &getLocalAdaptation() const
    {
        return _photoreceptorsPrefilter.getOutput();
    }
    inline const cv::ocl::oclMat &getPhotoreceptors() const
    {
        return _ParvoRetinaFilter.getPhotoreceptorsLPfilteringOutput();
    }

    inline const cv::ocl::oclMat &getHorizontalCells() const
    {
        return _ParvoRetinaFilter.getHorizontalCellsOutput();
    }
    inline bool areContoursProcessed()
    {
        return _useParvoOutput;
    }
    bool getParvoFoveaResponse(cv::ocl::oclMat &parvoFovealResponse);
    inline void activateContoursProcessing(const bool useParvoOutput)
    {
        _useParvoOutput = useParvoOutput;
    }

    const cv::ocl::oclMat &getContours();

    inline const cv::ocl::oclMat &getContoursON() const
    {
        return _ParvoRetinaFilter.getParvoON();
    }

    inline const cv::ocl::oclMat &getContoursOFF() const
    {
        return _ParvoRetinaFilter.getParvoOFF();
    }

    inline bool areMovingContoursProcessed()
    {
        return _useMagnoOutput;
    }

    inline void activateMovingContoursProcessing(const bool useMagnoOutput)
    {
        _useMagnoOutput = useMagnoOutput;
    }

    inline const cv::ocl::oclMat &getMovingContours() const
    {
        return _MagnoRetinaFilter.getOutput();
    }

    inline const cv::ocl::oclMat &getMovingContoursSaturated() const
    {
        return _MagnoRetinaFilter.getMagnoYsaturated();
    }

    inline const cv::ocl::oclMat &getMovingContoursON() const
    {
        return _MagnoRetinaFilter.getMagnoON();
    }

    inline const cv::ocl::oclMat &getMovingContoursOFF() const
    {
        return _MagnoRetinaFilter.getMagnoOFF();
    }

    inline const cv::ocl::oclMat &getRetinaParvoMagnoMappedOutput() const
    {
        return _retinaParvoMagnoMappedFrame;
    }

    inline const cv::ocl::oclMat &getParvoContoursChannel() const
    {
        return _colorEngine.getLuminance();
    }

    inline const cv::ocl::oclMat &getParvoChrominance() const
    {
        return _colorEngine.getChrominance();
    }
    inline const cv::ocl::oclMat &getColorOutput() const
    {
        return _colorEngine.getDemultiplexedColorFrame();
    }

    inline bool isColorMode()
    {
        return _useColorMode;
    }
    bool getColorMode()
    {
        return _useColorMode;
    }

    inline bool isInitTransitionDone()
    {
        if (_ellapsedFramesSinceLastReset < _globalTemporalConstant)
        {
            return false;
        }
        return true;
    }
    inline float getRetinaSamplingBackProjection(const float projectedRadiusLength)
    {
        return projectedRadiusLength;
    }

    inline unsigned int getInputNBrows()
    {
        return _photoreceptorsPrefilter.getNBrows();
    }

    inline unsigned int getInputNBcolumns()
    {
        return _photoreceptorsPrefilter.getNBcolumns();
    }

    inline unsigned int getInputNBpixels()
    {
        return _photoreceptorsPrefilter.getNBpixels();
    }

    inline unsigned int getOutputNBrows()
    {
        return _photoreceptorsPrefilter.getNBrows();
    }

    inline unsigned int getOutputNBcolumns()
    {
        return _photoreceptorsPrefilter.getNBcolumns();
    }

    inline unsigned int getOutputNBpixels()
    {
        return _photoreceptorsPrefilter.getNBpixels();
    }
private:
    bool _useParvoOutput;
    bool _useMagnoOutput;

    unsigned int _ellapsedFramesSinceLastReset;
    unsigned int _globalTemporalConstant;

    cv::ocl::oclMat _retinaParvoMagnoMappedFrame;
    BasicRetinaFilter _photoreceptorsPrefilter;
    ParvoRetinaFilter _ParvoRetinaFilter;
    MagnoRetinaFilter _MagnoRetinaFilter;
    RetinaColor       _colorEngine;

    bool _useMinimalMemoryForToneMappingONLY;
    bool _normalizeParvoOutput_0_maxOutputValue;
    bool _normalizeMagnoOutput_0_maxOutputValue;
    float _maxOutputValue;
    bool _useColorMode;

    void _setInitPeriodCount();
    void _processRetinaParvoMagnoMapping();
    void _runGrayToneMapping(const cv::ocl::oclMat &grayImageInput, cv::ocl::oclMat &grayImageOutput , const float PhotoreceptorsCompression = 0.6, const float ganglionCellsCompression = 0.6);
};

}  /* namespace ocl */
}  /* namespace bioinspired */
}  /* namespace cv */

#endif  /* HAVE_OPENCV_OCL */
#endif  /* __OCL_RETINA_HPP__ */