Mercurial > forge
view main/signal/medfilt1.cc @ 0:6b33357c7561 octave-forge
Initial revision
author | pkienzle |
---|---|
date | Wed, 10 Oct 2001 19:54:49 +0000 |
parents | |
children | 276d676c91da |
line wrap: on
line source
/* * Copyright 2000 Paul Kienzle, <pkienzle@kienzle.powernet.co.uk> * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Paul Kienzle is not responsible for the consequences of using * this software. * * Mar 2000 - Kai Habel (kahacjde@linux.zrz.tu-berlin.de) * Change: ColumnVector x=arg(i).vector_value(); * to: ColumnVector x=ColumnVector(arg(i).vector_value()); * Oct 2000 - Paul Kienzle (pkienzle@kienzle.powernet.co.uk) * rewrite to ignore NaNs rather than replacing them with zero * extend to handle matrix arguments */ #include <octave/oct.h> #include <octave/lo-ieee.h> #include <octave/lo-mappers.h> #include <math.h> // The median class holds a sorted data window. This window is // intended to slide over the data, so when the window shifts // by one position, the old value from the start of the window must // be removed and the new value from the end of the window must // be added. Since removals and additions generally occur in pairs, // a hole is left in the sorted window when the value is removed so // that on average, fewer values need to be shifted to close the // hole and open a new one in the sorted position. class Median { private: double *window; // window data int max; // length of window used int hole; // position of hole, or max if no hole void close_hole() // close existing hole { // move hole to the end of the window while (hole < max-1) { window[hole] = window[hole+1]; hole++; } // shorten window (if no hole, then hole==max) if (hole == max-1) max--; } void print(); public: Median(int n) { max=hole=0; window = new double[n]; } void add(double v); // add a new value void remove(double v); // remove an existing value void clear() { max=hole=0; } // clear the window double operator() (); // find the median in the window } ; // Print the sorted window, and indicate any hole void Median::print() { cout << "[ "; for (int i=0; i < max; i++) { if (i == hole) cout << "x "; else cout << window[i] << " "; } cout << " ]"; } // Remove a value from the sorted window, leaving a hole. The caller // must promise to only remove values that they have added. void Median::remove(double v) { // NaN's are not added or removed if (xisnan(v)) return; // cout << "Remove " << v << " from "; print(); // only one hole allowed, so close pre-existing ones close_hole(); // binary search to find the value to remove int lo = 0, hi=max-1; hole = hi/2; while (lo <= hi) { if (v > window[hole]) lo = hole+1; else if (v < window[hole]) hi = hole-1; else break; hole = (lo+hi)/2; } // Verify that it is the correct value to replace // Note that we shouldn't need this code since we are always replacing // a value that is already in the window, but for some reason // v==window[hole] occasionally doens't work. if (v != window[hole]) { for (hole = 0; hole < max-1; hole++) if (fabs(v-window[hole]) < fabs(v-window[hole+1])) break; warning ("medfilt1: value %f not found---removing %f instead", v, window[hole]); print(); cout << endl; } // cout << " gives "; print(); cout << endl; } // Insert a new value in the sorted window, plugging any holes, or // extending the window as necessary. The caller must promise not // to add more values than median was created with, without // removing some beforehand. void Median::add(double v) { // NaN's are not added or removed if (xisnan(v)) return; // cout << "Add " << v << " to "; print(); // If no holes, extend the array if (hole == max) max++; // shift the hole up to the beginning as far as it can go. while (hole > 0 && window[hole-1] > v) { window[hole] = window[hole-1]; hole--; } // or shift the hole down to the end as far as it can go. while (hole < max-1 && window[hole+1] < v) { window[hole] = window[hole+1]; hole++; } // plug in the replacement value window[hole] = v; // close the hole hole = max; // cout << " gives "; print(); cout << endl; } // Compute the median value from the sorted window // Return the central value if there is one or the average of the // two central values. Return NaN if there are no values. double Median::operator()() { close_hole(); if (max % 2 == 1) return window[(max-1)/2]; else if (max == 0) return octave_NaN; else return (window[max/2-1]+window[max/2])/2.0; } DEFUN_DLD (medfilt1, args, , "y = medfilt1(x [, n])\n\ \n\ Apply a median filter of length n to the signal x. A sliding window is\n\ applied to the data, and for each step the median value in the window is\n\ returned. If n is odd then the window for y(i) is x(i-(n-1)/2:i+(n-1)/2).\n\ If n is even then the window is x(i-n/2:i+n/2-1) and the two values in the\n\ center of the sorted window are averaged. If n is not given, then 3 is used.\n\ NaNs are ignored, as are values beyond the ends, by taking the median of\n\ the remaining values.") { octave_value_list retval; int nargin = args.length(); if (nargin < 1 || nargin > 3) { print_usage ("medfilt1"); return retval; } if (args(0).is_complex_type()) { error("medfilt1 cannot process complex vectors"); return retval; } int n=3; // length of the filter (default 3) if (nargin > 1) n = NINT(args(1).double_value()); if (n < 1) { error ("medfilt1 filter length must be at least 1"); return retval; } // Create a window to hold the sorted median values Median median(n); int mid = n/2; // mid-point of the window Matrix signal(args(0).matrix_value()); int nr = signal.rows(); // number of points to process int nc = signal.columns(); // number of points to process Matrix filter(nr,nc); // filtered signal to return if (nr == 1) // row vector { int start = -n, end = 0, pos=-(n-mid)+1; while (pos < nc) { if (start >= 0) median.remove(signal(0,start)); if (end < nc) median.add(signal(0,end)); if (pos >= 0) filter(0,pos) = median(); start++, end++, pos++; } } else // column vector or matrix { for (int column=0; column < nc; column++) { median.clear(); int start = -n, end = 0, pos=-(n-mid)+1; while (pos < nr) { if (start >= 0) median.remove(signal(start,column)); if (end < nr) median.add(signal(end,column)); if (pos >= 0) filter(pos,column) = median(); start++, end++, pos++; } } } retval(0) = filter; return retval; }