aboutsummaryrefslogtreecommitdiff
path: root/src/itrans-lsq.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/itrans-lsq.c')
-rw-r--r--src/itrans-lsq.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/itrans-lsq.c b/src/itrans-lsq.c
new file mode 100644
index 0000000..1bf3d80
--- /dev/null
+++ b/src/itrans-lsq.c
@@ -0,0 +1,335 @@
+/*
+ * itrans-lsq.c
+ *
+ * Peak detection by least-squares fitting
+ *
+ * (c) 2007 Thomas White <taw27@cam.ac.uk>
+ *
+ * dtr - Diffraction Tomography Reconstruction
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gsl/gsl_multifit_nlin.h>
+#include <stdint.h>
+
+#include "control.h"
+#include "imagedisplay.h"
+#include "reflections.h"
+
+#define MAX_LSQ_ITERATIONS 100
+
+typedef struct struct_sampledpoints {
+ gsl_matrix *coordinates;
+ gsl_vector *values;
+ unsigned int n_samples;
+} SampledPoints;
+
+static int itrans_peaksearch_lsq_f(const gsl_vector *gaussian, void *params, gsl_vector *residuals) {
+
+ double cal;
+ double obs;
+ SampledPoints *samples = params;
+ double a, b, c, d, e, f;
+ unsigned int sample;
+
+ a = gsl_vector_get(gaussian, 0); b = gsl_vector_get(gaussian, 1); c = gsl_vector_get(gaussian, 2);
+ d = gsl_vector_get(gaussian, 3); e = gsl_vector_get(gaussian, 4); f = gsl_vector_get(gaussian, 5);
+
+ //printf("Trial parameters: a=%f b=%f c=%f d=%f e=%f f=%f\n", a, b, c, d, e, f);
+
+ for ( sample=0; sample<samples->n_samples; sample++ ) {
+ int dx = gsl_matrix_get(samples->coordinates, sample, 0);
+ int dy = gsl_matrix_get(samples->coordinates, sample, 1);
+ cal = exp(a + b*dx + c*dy + d*dx*dx + e*dy*dy + f*dx*dy);
+ obs = gsl_vector_get(samples->values, sample);
+ gsl_vector_set(residuals, sample, (cal-obs));
+ //printf("At %3i,%3i: residual=%f - %f = %f\n", dx, dy, cal, obs, cal-obs);
+ }
+
+ return GSL_SUCCESS;
+
+}
+
+static int itrans_peaksearch_lsq_df(const gsl_vector *gaussian, void *params, gsl_matrix *J) {
+
+ double a, b, c, d, e, f;
+ unsigned int sample;
+ SampledPoints *samples = params;
+
+ a = gsl_vector_get(gaussian, 0); b = gsl_vector_get(gaussian, 1); c = gsl_vector_get(gaussian, 2);
+ d = gsl_vector_get(gaussian, 3); e = gsl_vector_get(gaussian, 4); f = gsl_vector_get(gaussian, 5);
+
+ //printf("------a------------b------------c------------d------------e------------f-------\n");
+ for ( sample=0; sample<samples->n_samples; sample++ ) {
+
+ double ex;
+ int dx, dy;
+ double derivative;
+
+ dx = gsl_matrix_get(samples->coordinates, sample, 0);
+ dy = gsl_matrix_get(samples->coordinates, sample, 1);
+ ex = exp(a + b*dx + c*dy + d*dx*dx + e*dy*dy + f*dx*dy);
+
+ derivative = ex; /* wrt a */
+ gsl_matrix_set(J, sample, 0, derivative);
+ derivative = dx * ex; /* wrt b */
+ gsl_matrix_set(J, sample, 1, derivative);
+ derivative = dy * ex; /* wrt c */
+ gsl_matrix_set(J, sample, 2, derivative);
+ derivative = dx * dx * ex; /* wrt d */
+ gsl_matrix_set(J, sample, 3, derivative);
+ derivative = dy * dy * ex; /* wrt e */
+ gsl_matrix_set(J, sample, 4, derivative);
+ derivative = dx * dy * ex; /* wrt f */
+ gsl_matrix_set(J, sample, 5, derivative);
+
+ /*if ( (dx == 0) && (dy == 0) ) {
+ printf("> ");
+ } else {
+ printf("| ");
+ }
+ printf("%10.2e | %10.2e | %10.2e | %10.2e | %10.2e | %10.2e", gsl_matrix_get(J, sample, 0), gsl_matrix_get(J, sample, 1),
+ gsl_matrix_get(J, sample, 2), gsl_matrix_get(J, sample, 3), gsl_matrix_get(J, sample, 4), gsl_matrix_get(J, sample, 5));
+ if ( (dx == 0) && (dy == 0) ) {
+ printf(" <\n");
+ } else {
+ printf(" |\n");
+ }*/
+
+ }
+ //printf("-------------------------------------------------------------------------------\n");
+
+ return GSL_SUCCESS;
+
+}
+
+static int itrans_peaksearch_lsq_fdf(const gsl_vector *gaussian, void *params, gsl_vector *f, gsl_matrix *J) {
+
+ itrans_peaksearch_lsq_f(gaussian, params, f);
+ itrans_peaksearch_lsq_df(gaussian, params, J);
+
+ return GSL_SUCCESS;
+
+}
+
+static void itrans_interpolate(int16_t *image, int width, int x, int y) {
+
+ int a, b, c, d;
+ double av_horiz, av_vert, av;
+ printf("Interpolating...\n");
+ a = image[(x-1) + width*y]; b = image[(x+1) + width*y];
+ c = image[x + width*(y-1)]; d = image[x + width*(y+1)];
+ av_horiz = (a+b)/2; av_vert = (c+d)/2;
+ av = (av_horiz+av_vert) / 2;
+ image[x + width*y] = av;
+
+}
+
+unsigned int itrans_peaksearch_lsq(int16_t *image, ControlContext *ctx, double tilt_degrees, ImageDisplay *imagedisplay) {
+
+ int16_t max_val = 0;
+ int width, height;
+ unsigned int n_reflections = 0;
+
+ width = ctx->width; height = ctx->height;
+
+ /* Least-Squares Craziness. NB Doesn't quite work... */
+ do {
+
+ int max_x = 0;
+ int max_y = 0;;
+ int x, y;
+ gsl_multifit_fdfsolver *s;
+ gsl_multifit_function_fdf f;
+ unsigned int iter;
+ gsl_vector *gaussian;
+ int iter_status, conv_status;
+ gsl_matrix *covar;
+ SampledPoints samples;
+ int sample;
+ double ga, gb, gc, gd, ge, gf;
+ gboolean sanity;
+ int dx, dy;
+ int bb_lh, bb_rh, bb_top, bb_bot;
+ int16_t ival;
+ double sx, sy;
+
+ /* Locate the highest point */
+ max_val = 0;
+ for ( y=10; y<height-10; y++ ) {
+ for ( x=10; x<width-10; x++ ) {
+
+ if ( image[x + width*y] > max_val ) {
+ max_val = image[x + width*y];
+ max_x = x; max_y = y;
+ }
+
+ }
+ }
+ x = max_x; y = max_y;
+
+ /* Try fitting a Gaussian to this region and see what happens... */
+ f.p = 6;
+
+ /* Set initial estimate of the fit parameters. I = exp(a + bx + cy + dx^2 + ey^2 + fxy) */
+ gaussian = gsl_vector_alloc(f.p);
+ gsl_vector_set(gaussian, 0, log(max_val)); /* a */
+ gsl_vector_set(gaussian, 1, -0.01); /* b */
+ gsl_vector_set(gaussian, 2, -0.01); /* c */
+ gsl_vector_set(gaussian, 3, -0.01); /* d */
+ gsl_vector_set(gaussian, 4, -0.01); /* e */
+ gsl_vector_set(gaussian, 5, -0.01); /* f */
+
+ /* Determine the bounding box which contains most of the peak */
+ /* Left */ dx = 0; do { ival = image[(x+dx) + width*(y+0)]; dx--; } while ( (ival>max_val/300) && (x+dx>0) ); bb_lh = dx;
+ /* Right */ dx = 0; do { ival = image[(x+dx) + width*(y+0)]; dx++; } while ( (ival>max_val/300) && (x+dx<width) ); bb_rh = dx;
+ /* Top */ dy = 0; do { ival = image[(x+0) + width*(y+dy)]; dy++; } while ( (ival>max_val/300) && (y+dy<height) ); bb_top = dy;
+ /* Bottom */ dy = 0; do { ival = image[(x+0) + width*(y+dy)]; dy--; } while ( (ival>max_val/300) && (y+dy>0) ); bb_bot = dy;
+ printf("Peak %i,%i: bounding box %i<dx<%i, %i<dy<%i\n", x, y, bb_lh, bb_rh, bb_bot, bb_top);
+ sanity = TRUE;
+ if ( (bb_rh-bb_lh) > 300 ) sanity = FALSE;
+ if ( (bb_top-bb_bot) > 300 ) sanity = FALSE;
+ if ( sanity ) {
+
+ /* Choose which points inside this bounding box to use for curve fitting */
+ samples.n_samples = ((bb_top-bb_bot)+1)*((bb_rh-bb_lh)+1);
+ samples.coordinates = gsl_matrix_alloc(samples.n_samples, 2);
+ sample = 0;
+ for ( sx=bb_lh; sx<=bb_rh; sx++ ) {
+ for ( sy=bb_bot; sy<=bb_top; sy++ ) {
+ gsl_matrix_set(samples.coordinates, sample, 0, sx); gsl_matrix_set(samples.coordinates, sample, 1, sy);
+ sample++;
+ }
+ }
+
+ samples.values = gsl_vector_alloc(samples.n_samples);
+ f.n = samples.n_samples;
+ for ( sample=0; sample<samples.n_samples; sample++ ) {
+ int dx = gsl_matrix_get(samples.coordinates, sample, 0);
+ int dy = gsl_matrix_get(samples.coordinates, sample, 1);
+ gsl_vector_set(samples.values, sample, image[(x+dx) + width*(y+dy)]);
+ //printf("At %3i,%3i: value=%i\n", dx, dy, image[(x+dx) + width*(y+dy)]);
+ }
+ f.params = &samples;
+
+ /* Execute the LSQ fitting procedure */
+ s = gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder, f.n, f.p);
+ f.f = &itrans_peaksearch_lsq_f; f.df = &itrans_peaksearch_lsq_df; f.fdf = &itrans_peaksearch_lsq_fdf;
+ gsl_multifit_fdfsolver_set(s, &f, gaussian);
+ iter = 0; conv_status = GSL_CONTINUE;
+ do {
+
+ /* Iterate */
+ iter_status = gsl_multifit_fdfsolver_iterate(s);
+ iter++;
+
+ /* Check for error */
+ if ( iter_status ) {
+ break;
+ }
+
+ /* Test for convergence */
+ conv_status = gsl_multifit_test_delta(s->dx, s->x, 1e-6, 1e-6);
+
+ } while ( (conv_status == GSL_CONTINUE) && (iter < MAX_LSQ_ITERATIONS) );
+
+ /* See how good the fit is */
+ covar = gsl_matrix_alloc(f.p, f.p);
+ gsl_multifit_covar(s->J, 0.0, covar);
+ ga = gsl_vector_get(s->x, 0); gb = gsl_vector_get(s->x, 1); gc = gsl_vector_get(s->x, 2);
+ gd = gsl_vector_get(s->x, 3); ge = gsl_vector_get(s->x, 4); gf = gsl_vector_get(s->x, 5);
+ if ( fabs(exp(ga + gb*100 + gc*100 + gd*100*100 + ge*100*100 + gf*100*100)) > 10 ) {
+ printf("Failed sanity check: %3i,%3i, a=%f b=%f c=%f d=%f e=%f f=%f\n", x, y, ga, gb, gc, gd, ge, gf);
+ sanity = FALSE;
+ } else {
+ sanity = TRUE;
+ }
+ if ( (conv_status == GSL_SUCCESS) && (!iter_status) && (sanity) ) {
+
+ /* Good fit! */
+ int dx, dy;
+ int bb_top, bb_bot, bb_lh, bb_rh;
+ double frac;
+
+ printf("Fit converged after %i iterations: Centre %3i,%3i, a=%f b=%f c=%f d=%f e=%f f=%f\n", iter, x, y, ga, gb, gc, gd, ge, gf);
+ if ( ctx->fmode == FORMULATION_PIXELSIZE ) {
+ reflection_add_from_reciprocal(ctx, (x-ctx->x_centre)*ctx->pixel_size, (y-ctx->y_centre)*ctx->pixel_size,
+ tilt_degrees, image[x + width*y]);
+ } else {
+ reflection_add_from_dp(ctx, (x-ctx->x_centre), (y-ctx->y_centre), tilt_degrees, image[x + width*y]);
+ }
+ if ( ctx->first_image ) {
+ imagedisplay_mark_point(imagedisplay, x, y);
+ }
+ n_reflections++;
+
+ /* Remove this peak from the image */
+ /* Find right-hand edge of bounding box */
+ dx = 0; do {
+ double pval, ival;
+ pval = exp(ga + gb*dx + gc*0 + gd*dx*dx + ge*0*0 + gf*dx*-0);
+ ival = image[(x+dx) + width*(y+0)];
+ frac = pval / ival;
+ dx++;
+ } while ( (frac > 0.1) && (x+dx<width) );
+ bb_rh = dx;
+ /* Find left-hand edge of bounding box */
+ dx = 0; do {
+ double pval, ival;
+ pval = exp(ga + gb*dx + gc*0 + gd*dx*dx + ge*0*0 + gf*dx*-0);
+ ival = image[(x+dx) + width*(y+0)];
+ frac = pval / ival;
+ dx--;
+ } while ( (frac > 0.1) && (x+dx>0) );
+ bb_lh = dx;
+ /* Find top edge of bounding box */
+ dy = 0; do {
+ double pval, ival;
+ pval = exp(ga + gb*0 + gc*dy + gd*0*0 + ge*dy*dy + gf*0*dy);
+ ival = image[(x+0) + width*(y+dy)];
+ frac = pval / ival;
+ dy++;
+ } while ( (frac > 0.1) && (y+dy<height) );
+ bb_top = dy;
+ /* Find bottom edge of bounding box */
+ dy = 0; do {
+ double pval, ival;
+ pval = exp(ga + gb*0 + gc*dy + gd*0*0 + ge*dy*dy + gf*0*dy);
+ ival = image[(x+0) + width*(y+dy)];
+ frac = pval / ival;
+ dy--;
+ } while ( (frac > 0.1) && (y+dy>0) );
+ bb_bot = dy;
+ printf("Fitted peak bounding box %i<dx<%i, %i<dy<%i\n", bb_lh, bb_rh, bb_bot, bb_top);
+ for ( dx=bb_lh; dx<bb_rh; dx++ ) {
+ for ( dy=bb_bot; dy<bb_top; dy++ ) {
+ double pval;
+ pval = exp(ga + gb*dx + gc*dy + gd*dx*dx + ge*dy*dy + gf*dx*dy);
+ image[(x+dx) + width*(y+dy)] -= pval;
+ }
+ }
+
+ } else {
+ itrans_interpolate(image, width, x, y);
+ }
+
+ gsl_matrix_free(covar);
+ gsl_multifit_fdfsolver_free(s);
+ gsl_vector_free(gaussian);
+ gsl_matrix_free(samples.coordinates);
+ gsl_vector_free(samples.values);
+
+ } else {
+ printf("Failed bounding box sanity check\n");
+ itrans_interpolate(image, width, x, y);
+ }
+
+
+ } while ( max_val > 50 );
+
+ return n_reflections;
+}