#include "rl.h" #include #include /* `gsl_vector_convolve(a, b, res)` computes the linear * convolution of two vectors. The resulting vector * has size `a->n + b->n - 1` and is stored in `res`. */ int gsl_vector_convolve(gsl_vector *a, gsl_vector *b, gsl_vector *res) { size_t m = a->size; size_t n = b->size; /* Vector views for the overlap dot product */ gsl_vector_view va, vb; /* Reverse `b` before convolving */ gsl_vector_reverse(b); double dot; size_t start_a, start_b, many; for (size_t i = 0; i < m + n - 1; i++) { /* Calculate the starting indices * of the two vectors a,b overlap. */ if (i < n) start_a = 0; else start_a = i - (n - 1); if (i < n) start_b = (n - 1) - i; else start_b = 0; /* Calculate how many elements overlap */ if (i < n) many = i + 1; else if (i >= m) many = n - (i - m) - 1; else many = n; /* Take the dot product of the overlap * and store it in the resulting histogram */ va = gsl_vector_subvector(a, start_a, many); vb = gsl_vector_subvector(b, start_b, many); gsl_blas_ddot(&va.vector, &vb.vector, &dot); gsl_vector_set(res, i, dot); } /* Put b back together */ gsl_vector_reverse(b); return GSL_SUCCESS; } /* Performs the Richardson-Lucy deconvolution. * In pseudo-python: * * def rl_deconvolve(data, kernel, rounds): * est = np.full(data, 0.5) * for _ in range(rounds): * est_conv = convolve(est, kernel) * est *= convolve(data / est_conv, reversed) * */ gsl_histogram* rl_deconvolve( gsl_histogram *data, gsl_histogram *kernel, size_t rounds) { /* Size of the original data * before being convoluted. * * Notation of sizes: * - original: m * - kernel: n * - "full" convolution: m + n - 1 */ size_t orig_size = data->n - kernel->n + 1; /* Create a histogram with the same edges * as `data`, but with the original size, * in which to return the cleaned result. */ gsl_histogram *hist = gsl_histogram_calloc(orig_size); /* Set the same bin edges as `data`*/ double max = gsl_histogram_max(data); double min = gsl_histogram_min(data); gsl_histogram_set_ranges_uniform(hist, min, max); /* Vector views of the result, kernel * and data. These are used to perform * vectorised operations on histograms. */ gsl_vector est = gsl_vector_view_array(hist->bin, hist->n).vector; gsl_vector vkernel = gsl_vector_view_array(kernel->bin, kernel->n).vector; gsl_vector vdata = gsl_vector_view_array(data->bin, data->n).vector; gsl_vector center; /* Create a flipped copy of the kernel */ gsl_vector *vkernel_flip = gsl_vector_alloc(kernel->n); gsl_vector_memcpy(vkernel_flip, &vkernel); gsl_vector_reverse(vkernel_flip); /* More vectors to store partial * results */ gsl_vector* est_conv = gsl_vector_alloc(data->n); gsl_vector* rel_blur = gsl_vector_alloc(data->n); /* The zero-order estimate is simply * all elements at 0.5 */ gsl_vector_set_all(&est, 0.5); for (size_t iter = 0; iter < rounds; iter++) { /* The current estimated convolution is the * current estimate of the data with * the kernel */ gsl_vector_convolve(&est, &vkernel, est_conv); /* Divide the data by the estimated * convolution to calculate the "relative blur". */ gsl_vector_memcpy(rel_blur, &vdata); gsl_vector_div(rel_blur, est_conv); /* Set NaNs to zero */ for (size_t i = 0; i < rel_blur->size; i++) if (isnan(gsl_vector_get(rel_blur, i))) { fprintf(stderr, "gotcha: %ld!!\n", i); double y = (i > 0)? gsl_vector_get(rel_blur, i - 1) : 1; gsl_vector_set(rel_blur, i, y); } /* Convolve the blur by the kernel * and multiply the current estimate * of the data by it. */ center = gsl_vector_subvector(rel_blur, (kernel->n-1)/2, orig_size).vector; gsl_vector_convolve(¢er, vkernel_flip, est_conv); center = gsl_vector_subvector(est_conv, (kernel->n-1)/2, orig_size).vector; gsl_vector_mul(&est, ¢er); } // free memory gsl_vector_free(est_conv); gsl_vector_free(rel_blur); gsl_vector_free(vkernel_flip); return hist; }