/* -*- 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/internal/autotest.hh>
#include <mia/core/msgstream.hh>
#include <mia/3d/filter/mlv.hh>

NS_MIA_USE
using namespace std;
using namespace ::boost::unit_test;
using namespace mlv_3dimage_filter;

struct FCompare {
	bool operator () (float a, float b){
		return ::fabs(a-b) < 0.01;
	}
};

BOOST_AUTO_TEST_CASE( test_mlv )
{
#ifndef WIN32
	// the test should always use the same random numbers 
	srand48(0);
#endif
	for (int w = 1; w < 5; ++w) {
		cvdebug() << "test filter of width " << w << "\n";

		C3DMLVImageFilter filter(w);

		int isize = 4 * w + 1;
		int fwidth = 2 * w;
		C3DBounds size(isize, isize, isize);

		C3DFImage *src = new C3DFImage(size);
		for (C3DFImage::iterator i = src->begin(), e = src->end(); i != e; ++i)
#ifndef WIN32
			*i = drand48();
#else
			*i = float(rand())/RAND_MAX;
#endif

               // create the reference image
		C3DFImage ref(src->get_size());

		const C3DBounds exsize(isize+fwidth,isize+fwidth,isize+fwidth);
		C3DFImage sigma(exsize);
		C3DFImage mu(exsize);

		for (int z = 0; z < isize + fwidth; ++z) {
			for (int y = 0; y < isize + fwidth; ++y) {
				for (int x = 0; x < isize + fwidth; ++x) {
				float sum  = 0.0f;
					float sum2 = 0.0f;
					int n = 0;

					for (int iz = max(0, z - fwidth); iz < min(isize, z + 1); ++iz)
						for (int iy = max(0, y - fwidth); iy < min(isize, y + 1); ++iy)
							for (int ix = max(0, x - fwidth); ix < min(isize, x + 1); ++ix) {
								float const& val = (*src)(ix,iy,iz);
								sum += val;
								sum2 += val * val;
								++n;
							}
					float m = mu(x,y,z) = (n > 0) ? sum / n : 0.0f;
					sigma(x,y,z) = (n > 1) ? (sum2 - n * m * m) / (n - 1) : 0.0f;
				}

			}
		}

		C3DFImage::const_iterator i_src = src->begin();
		for (int z = 0; z < isize; ++z) {
			for (int y = 0; y < isize; ++y) {
				for (int x = 0; x < isize; ++x, ++i_src) {
					float best_mu = 0.0;
					float best_sigma = numeric_limits<float>::max();
					for (int iz = z; iz < z + fwidth +1; ++iz)
						for (int iy = y; iy < y + fwidth +1; ++iy)
							for (int ix = x; ix < x + fwidth +1; ++ix) {
								float mu_val = mu(ix, iy, iz);
								if (best_sigma > sigma(ix, iy, iz) ) {
									best_sigma = sigma(ix, iy, iz);
									best_mu = mu_val;
								}else if (best_sigma == sigma(ix, iy, iz) && best_mu != mu_val ) {
									if (::fabs(mu_val - *i_src) < ::fabs(*i_src - best_mu))
										best_mu = mu_val;
								}
							}
					ref(x,y,z) = best_mu;
				}
			}
		}


		P3DImage src_wrap(src);
		P3DImage result = filter.filter(*src_wrap);

		const C3DFImage& presult = dynamic_cast<const C3DFImage&>(*result);
		BOOST_CHECK(equal(presult.begin(), presult.end(), ref.begin(), FCompare()));
	}
}
