/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <mia/2d/filter/admean.hh>

NS_BEGIN ( admean_2dimage_filter )
NS_MIA_USE;
using namespace std;

static char const *plugin_name = "admean";

template <class Data2D>
typename C2DAdmean::result_type C2DAdmean::operator () ( const Data2D& data ) const
{
       typedef typename Data2D::value_type out_type;
       Data2D *result = new Data2D ( data.get_size(), data );
       auto i = result->begin();
       auto t = data.begin();
       cvdebug() << "filter with width = " << m_width << endl;
       double sum = 0.0;
       double sum2 = 0.0;
       double n = data.size();

       if ( n == 1 ) {
              *i = *t;
              return P2DImage ( result );
       }

       for ( auto i = data.begin(); i != data.end(); ++i ) {
              sum += *i;
              sum2 += *i * *i;
       }

       double mean = sum / n;
       double var2 = ( sum2 - mean * sum ) / ( n - 1 );

       for ( int y = 0; y < ( int ) data.get_size().y; ++y )
              for ( int x = 0; x < ( int ) data.get_size().x; ++x, ++i, ++t )	{
                     double lsum = 0.0;
                     double lsum2 = 0.0;
                     int num = 0;

                     for ( int iy = max ( 0, y - m_width );
                           iy < min ( y + m_width + 1, ( int ) data.get_size().y );  ++iy )
                            for ( int ix = max ( 0, x - m_width );
                                  ix < min ( x + m_width + 1, ( int ) data.get_size().x );  ++ix )	{
                                   double v = data ( ix, iy );
                                   lsum += v;
                                   lsum2 += v * v;
                                   ++num;
                            }

                     if ( num > 1 )	{
                            float lmean = lsum / num;
                            float lvar2 = ( lsum2 - lsum * lmean ) / ( num - 1 );
                            *i = static_cast<out_type>( lvar2 > var2 ?
                                                        ( *t -  var2 / lvar2 * ( *t - lmean ) ) : lmean );
                     } else
                            *i = static_cast<out_type>( lsum );
              }

       return P2DImage ( result );
}

P2DImage C2DAdmean::do_filter ( const C2DImage& image ) const
{
       return mia::filter ( *this, image );
}

CAdmean2DImageFilterFactory::CAdmean2DImageFilterFactory() :
       C2DFilterPlugin ( plugin_name ),
       m_hwidth ( 1 )
{
       add_parameter("w", make_lc_param(m_hwidth, 1, false, "half filter width"));
}

C2DFilter *CAdmean2DImageFilterFactory::do_create()const
{
       return new C2DAdmean ( m_hwidth );
}

const string CAdmean2DImageFilterFactory::do_get_descr() const
{
       return "An adaptive mean filter that works like a normal mean filter, if the intensity "
              "variation within the filter mask is lower then the intensity variation in the "
              "whole image, that the uses a special formula if the local variation is higher "
              "then the image intensity variation.";
}

extern "C" EXPORT CPluginBase *get_plugin_interface()
{
       return new CAdmean2DImageFilterFactory();
}

NS_END
