Blame filter/rmedian.c

Packit 67cb25
/* filter/rmedian.c
Packit 67cb25
 *
Packit 67cb25
 * Copyright (C) 2018 Patrick Alken
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
 * This module contains routines related to the recursive median filter. The
Packit 67cb25
 * algorithm is based on this paper,
Packit 67cb25
 *
Packit 67cb25
 * [1] S-J Ko, Y. H. Lee, and A. T. Fam, Efficient Implementation of One-Dimensional
Packit 67cb25
 * Recursive Median Filters, IEEE Transactions on Circuits and Systems, Vol 37,
Packit 67cb25
 * No 11, 1990.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <stdlib.h>
Packit 67cb25
#include <math.h>
Packit 67cb25
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_filter.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_statistics.h>
Packit 67cb25
#include <gsl/gsl_movstat.h>
Packit 67cb25
Packit 67cb25
typedef struct
Packit 67cb25
{
Packit 67cb25
  const gsl_movstat_accum * minmax_acc; /* minimum/maximum accumulator */
Packit 67cb25
  void *minmax_state;                   /* minimum/maximum accumulator workspace */
Packit 67cb25
} rmedian_state_t;
Packit 67cb25
Packit 67cb25
static size_t rmedian_size(const size_t n);
Packit 67cb25
static int rmedian_init(const size_t n, void * vstate);
Packit 67cb25
static int rmedian_insert(const double x, void * vstate);
Packit 67cb25
static int rmedian_delete(void * vstate);
Packit 67cb25
static int rmedian_get(void * params, double * result, const void * vstate);
Packit 67cb25
Packit 67cb25
static const gsl_movstat_accum rmedian_accum_type;
Packit 67cb25
Packit 67cb25
gsl_filter_rmedian_workspace *
Packit 67cb25
gsl_filter_rmedian_alloc(const size_t K)
Packit 67cb25
{
Packit 67cb25
  gsl_filter_rmedian_workspace *w;
Packit 67cb25
  size_t state_size;
Packit 67cb25
Packit 67cb25
  w = calloc(1, sizeof(gsl_filter_rmedian_workspace));
Packit 67cb25
  if (w == 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->H = K / 2;
Packit 67cb25
  w->K = 2*w->H + 1;
Packit 67cb25
  w->minmaxacc = gsl_movstat_accum_minmax;
Packit 67cb25
Packit 67cb25
  w->window = malloc(w->K * sizeof(double));
Packit 67cb25
  if (w->window == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_filter_rmedian_free(w);
Packit 67cb25
      GSL_ERROR_NULL ("failed to allocate space for window", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state_size = rmedian_size(w->H + 1);
Packit 67cb25
Packit 67cb25
  w->state = malloc(state_size);
Packit 67cb25
  if (w->state == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_filter_rmedian_free(w);
Packit 67cb25
      GSL_ERROR_NULL ("failed to allocate space for min/max state", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->movstat_workspace_p = gsl_movstat_alloc_with_size(state_size, 0, w->H);
Packit 67cb25
  if (!w->movstat_workspace_p)
Packit 67cb25
    {
Packit 67cb25
      gsl_filter_rmedian_free(w);
Packit 67cb25
      GSL_ERROR_NULL ("failed to allocate space for movstat workspace", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return w;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_filter_rmedian_free(gsl_filter_rmedian_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (w->state)
Packit 67cb25
    free(w->state);
Packit 67cb25
Packit 67cb25
  if (w->window)
Packit 67cb25
    free(w->window);
Packit 67cb25
Packit 67cb25
  if (w->movstat_workspace_p)
Packit 67cb25
    gsl_movstat_free(w->movstat_workspace_p);
Packit 67cb25
Packit 67cb25
  free(w);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_filter_rmedian()
Packit 67cb25
  Recursive median filter
Packit 67cb25
Packit 67cb25
Inputs: endtype - end point handling
Packit 67cb25
        x       - input vector
Packit 67cb25
        y       - output vector
Packit 67cb25
        w       - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_filter_rmedian(const gsl_filter_end_t endtype, const gsl_vector * x, gsl_vector * y, gsl_filter_rmedian_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (x->size != y->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("input and output vectors must have same length", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status = GSL_SUCCESS;
Packit 67cb25
      const size_t n = x->size;
Packit 67cb25
      const int H = (int) w->H;
Packit 67cb25
      double yprev;
Packit 67cb25
      int wsize;
Packit 67cb25
Packit 67cb25
      /* find median of first window to initialize filter */
Packit 67cb25
      wsize = gsl_movstat_fill(endtype, x, 0, H, H, w->window);
Packit 67cb25
      yprev = gsl_stats_median(w->window, 1, wsize);
Packit 67cb25
      gsl_vector_set(y, 0, yprev);
Packit 67cb25
Packit 67cb25
      if (x->size > 1)
Packit 67cb25
        {
Packit 67cb25
          gsl_vector_const_view xv = gsl_vector_const_subvector(x, 1, n - 1);
Packit 67cb25
          gsl_vector_view yv = gsl_vector_subvector(y, 1, n - 1);
Packit 67cb25
Packit 67cb25
          /* apply recursive median filter to x[2:end] */
Packit 67cb25
          status = gsl_movstat_apply_accum(endtype, &xv.vector, &rmedian_accum_type, (void *) &yprev, &yv.vector,
Packit 67cb25
                                           NULL, w->movstat_workspace_p);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static size_t
Packit 67cb25
rmedian_size(const size_t n)
Packit 67cb25
{
Packit 67cb25
  size_t size = 0;
Packit 67cb25
  const gsl_movstat_accum * acc = gsl_movstat_accum_minmax;
Packit 67cb25
Packit 67cb25
  size += sizeof(rmedian_state_t);
Packit 67cb25
  size += (acc->size)(n);
Packit 67cb25
Packit 67cb25
  return size;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
rmedian_init(const size_t n, void * vstate)
Packit 67cb25
{
Packit 67cb25
  rmedian_state_t * state = (rmedian_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  state->minmax_acc = gsl_movstat_accum_minmax;
Packit 67cb25
  state->minmax_state = (void *) ((unsigned char *) vstate + sizeof(rmedian_state_t));
Packit 67cb25
Packit 67cb25
  (state->minmax_acc->init)(n, state->minmax_state);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
rmedian_insert(const double x, void * vstate)
Packit 67cb25
{
Packit 67cb25
  rmedian_state_t * state = (rmedian_state_t *) vstate;
Packit 67cb25
  return (state->minmax_acc->insert)(x, state->minmax_state);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
rmedian_delete(void * vstate)
Packit 67cb25
{
Packit 67cb25
  rmedian_state_t * state = (rmedian_state_t *) vstate;
Packit 67cb25
  return (state->minmax_acc->delete)(state->minmax_state);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
rmedian_get(void * params, double * result, const void * vstate)
Packit 67cb25
{
Packit 67cb25
  const rmedian_state_t * state = (const rmedian_state_t *) vstate;
Packit 67cb25
  double *yprev = (double *) params; /* previous filter output */
Packit 67cb25
  double y;                          /* new filter output */
Packit 67cb25
  double xminmax[2];
Packit 67cb25
Packit 67cb25
  /* get minimum/maximum values of {x_i,...,x_{i+H}} */
Packit 67cb25
  (state->minmax_acc->get)(NULL, xminmax, state->minmax_state);
Packit 67cb25
Packit 67cb25
  /* y = median [ yprev, xmin, xmax ] */
Packit 67cb25
  if (*yprev <= xminmax[0])
Packit 67cb25
    y = xminmax[0];
Packit 67cb25
  else if (*yprev <= xminmax[1])
Packit 67cb25
    y = *yprev;
Packit 67cb25
  else
Packit 67cb25
    y = xminmax[1];
Packit 67cb25
Packit 67cb25
  *result = y;
Packit 67cb25
  *yprev = y;
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static const gsl_movstat_accum rmedian_accum_type =
Packit 67cb25
{
Packit 67cb25
  rmedian_size,
Packit 67cb25
  rmedian_init,
Packit 67cb25
  rmedian_insert,
Packit 67cb25
  rmedian_delete,
Packit 67cb25
  rmedian_get
Packit 67cb25
};