/* im_measure.c
 *
 * Modified:
 * 19/8/94 JC
 *	- now uses doubles for addressing
 *	- could miss by up to h pixels previously!
 *	- ANSIfied
 *	- now issues warning if any deviations are greater than 20% of the
 *	  mean
 * 31/10/95 JC
 *	- more careful about warning for averages <0, or averages near zero
 *	- can get these cases with im_measure() of IM_TYPE_LAB images
 * 28/10/02 JC
 *	- number bands from zero in error messages
 * 7/7/04
 *	- works on labq
 * 18/8/08
 * 	- add gtkdoc comments
 * 	- remove deprecated im_extract()
 * 30/11/09
 * 	- changes for im_extract() broke averaging
 * 9/11/11
 * 	- moved to deprecated, the new VipsMeasure does not have the
 * 	  select-patches thing, so we have to keep this around
 */

/*

	This file is part of VIPS.

	VIPS is free software; you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation; either version 2 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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
	02110-1301  USA

 */

/*

	These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>

#include <stdio.h>
#include <math.h>

#include <vips/vips.h>
#include <vips/vips7compat.h>

/* Measure into array.
 */
static int
measure_patches(IMAGE *im, double *coeff,
	int left, int top, int width, int height,
	int u, int v, int *sel, int nsel)
{
	IMAGE *tmp;
	int patch;
	int i, j;
	int m, n;
	double avg, dev;
	int x, y, w, h;

	/* How large are the patches we are to measure?
	 */
	double pw = (double) width / (double) u;
	double ph = (double) height / (double) v;

	/* Set up sub to be the size we need for a patch.
	 */
	w = (pw + 1) / 2;
	h = (ph + 1) / 2;

	/* Loop through sel, picking out areas to measure.
	 */
	for (j = 0, patch = 0; patch < nsel; patch++) {
		/* Sanity check. Is the patch number sensible?
		 */
		if (sel[patch] <= 0 || sel[patch] > u * v) {
			im_error("im_measure",
				_("patch %d is out of range"),
				sel[patch]);
			return 1;
		}

		/* Patch coordinates.
		 */
		m = (sel[patch] - 1) % u;
		n = (sel[patch] - 1) / u;

		/* Move sub to correct position.
		 */
		x = left + m * pw + (pw + 2) / 4;
		y = top + n * ph + (ph + 2) / 4;

		/* Loop through bands.
		 */
		for (i = 0; i < im->Bands; i++, j++) {
			/* Make temp buffer to extract to.
			 */
			if (!(tmp = im_open("patch", "t")))
				return -1;

			/* Extract and measure.
			 */
			if (im_extract_areabands(im, tmp, x, y, w, h, i, 1) ||
				im_avg(tmp, &avg) ||
				im_deviate(tmp, &dev)) {
				im_close(tmp);
				return -1;
			}
			im_close(tmp);

			/* Is the deviation large compared with the average?
			 * This could be a clue that our parameters have
			 * caused us to miss the patch. Look out for averages
			 * <0, or averages near zero (can get these if use
			 * im_measure() on IM_TYPE_LAB images).
			 */
			if (dev * 5 > fabs(avg) && fabs(avg) > 3)
				im_warn("im_measure",
					_("patch %d, band %d: "
					  "avg = %g, sdev = %g"),
					patch, i, avg, dev);

			/* Save results.
			 */
			coeff[j] = avg;
		}
	}

	return 0;
}

static DOUBLEMASK *
internal_im_measure_area(IMAGE *im,
	int left, int top, int width, int height,
	int u, int v,
	int *sel, int nsel, const char *name)
{
	DOUBLEMASK *mask;

	if (im_check_uncoded("im_measure", im) ||
		im_check_noncomplex("im_measure", im))
		return NULL;

	/* Default to all patches if sel == NULL.
	 */
	if (sel == NULL) {
		int i;

		nsel = u * v;
		if (!(sel = IM_ARRAY(im, nsel, int)))
			return NULL;
		for (i = 0; i < nsel; i++)
			sel[i] = i + 1;
	}

	/* What size mask do we need?
	 */
	if (!(mask = im_create_dmask(name, im->Bands, nsel)))
		return NULL;

	/* Perform measure and return.
	 */
	if (measure_patches(im, mask->coeff, left, top, width, height,
			u, v, sel, nsel)) {
		im_free_dmask(mask);
		return NULL;
	}

	return mask;
}

DOUBLEMASK *
im_measure_area(IMAGE *im,
	int left, int top, int width, int height,
	int u, int v,
	int *sel, int nsel, const char *name)
{
	DOUBLEMASK *mask;
	VipsImage *t;

	/* The old im_measure() worked on labq.
	 */
	if (im->Coding == IM_CODING_LABQ) {
		if (!(t = im_open("measure-temp", "p")))
			return NULL;
		if (im_LabQ2Lab(im, t) ||
			!(mask = im_measure_area(t,
				  left, top, width, height,
				  u, v,
				  sel, nsel, name))) {
			g_object_unref(t);
			return NULL;
		}
		g_object_unref(t);

		return mask;
	}

	if (sel)
		return internal_im_measure_area(im,
			left, top, width, height, u, v, sel, nsel, name);
	else {
		if (vips_measure(im, &t, u, v,
				"left", left,
				"top", top,
				"width", width,
				"height", height,
				NULL))
			return NULL;
		if (!(mask = im_vips2mask(t, name))) {
			g_object_unref(t);
			return NULL;
		}
		g_object_unref(t);

		return mask;
	}
}
