|
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 |
};
|