/* file: mfilt.c G. Moody 27 June 1993 Last revised: 24 April 2020 ------------------------------------------------------------------------------- mfilt: General-purpose median filter for database records Copyright (C) 1993-2006 George B. Moody This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . You may contact the author by e-mail (wfdb@physionet.org) or postal mail (MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software, please visit PhysioNet (http://www.physionet.org/). _______________________________________________________________________________ */ #include #include char *pname; /* name by which this program was invoked */ char *nrec; /* name of record to be created */ int flen; /* filter length */ int median; /* offset of median element within vtemp array after sorting */ int nsig; /* number of signals to be filtered */ int **vin; /* pointers to input vectors */ int *vtemp; /* temporary array for calculating medians */ int *vout; /* output vector */ WFDB_Time from = 0L; /* first sample to be processed */ WFDB_Time to = 0L; /* (if > 0) sample following last sample to be processed */ WFDB_Time spm; /* samples per minute */ WFDB_Time tt; /* time to print next progress indicator */ char *prog_name(); void help(), init(), memerr(); int icmp(x, y) int *x, *y; { return (*y - *x); } main(argc, argv) int argc; char *argv[]; { int i = 0, j, s; WFDB_Time t; init(argc, argv); /* read and interpret command line */ if (from < median) { /* preload array with copies of sample 0 */ (void)getvec(vin[0]); for (i = 1; i <= median - from; i++) for (s = 0; s < nsig; s++) vin[i][s] = vin[0][s]; } else if (isigsettime(from - median) < 0) exit(2); for ( ; i < flen; i++) (void)getvec(vin[i]); for (t = from; to <= 0L || t < to; t++) { for (s = 0; s < nsig; s++) { for (j = 0; j < flen; j++) vtemp[j] = vin[j][s]; qsort((char *)vtemp, flen, sizeof(int), icmp); if (flen & 1) /* odd length -- median is middle element */ vout[s] = vtemp[median]; else /* even length -- median is avg. of two middle elements */ vout[s] = (vtemp[median] + vtemp[median-1])/2; } if (++i >= flen) i = 0; if (putvec(vout) < 0 || getvec(vin[i]) < 0) break; if (t > tt) { (void)fprintf(stderr, "."); tt += spm; } } if (to <= 0L) to = t + flen - median; for (; t < to; t++) (void)putvec(vout); /* pad output to length of input record */ (void)fprintf(stderr, "\n"); if (nrec) (void)newheader(nrec); wfdbquit(); exit(0); /*NOTREACHED*/ } void init(argc, argv) int argc; char *argv[]; { char *irec = "16", *ofname, *orec = "16", *p; int format, i; static int gvmode = 0; static WFDB_Siginfo *si, *so; pname = prog_name(argv[0]); for (i = 1; i < argc; i++) { if (*argv[i] == '-') switch (*(argv[i]+1)) { case 'f': /* starting time */ if (++i >= argc) { (void)fprintf(stderr, "%s: time must follow -f\n", pname); exit(1); } from = i; break; case 'h': /* help requested */ help(); exit(0); break; case 'H': /* operate in WFDB_HIGHRES mode */ gvmode = WFDB_HIGHRES; break; case 'i': /* input record name */ if (++i >= argc) { (void)fprintf(stderr, "%s: record name must follow -i\n", pname); exit(1); } irec = argv[i]; break; case 'l': /* filter length */ if (++i >= argc || (flen = atoi(argv[i])) < 1) { (void)fprintf(stderr, "%s: filter length (> 0) must follow -l\n", pname); exit(1); } break; case 'n': /* new record name */ if (++i >= argc) { (void)fprintf(stderr, "%s: record name must follow -n\n", pname); exit(1); } nrec = argv[i]; break; case 'o': /* output record name */ if (++i >= argc) { (void)fprintf(stderr, "%s: record name must follow -o\n", pname); exit(1); } orec = argv[i]; break; case 't': /* end time */ if (++i >= argc) { (void)fprintf(stderr, "%s: time must follow -t\n", pname); exit(1); } to = i; break; default: (void)fprintf(stderr, "%s: unrecognized option %s\n", pname, argv[i]); exit(1); } else { (void)fprintf(stderr, "%s: unrecognized argument %s\n", pname, argv[i]); exit(1); } } if (flen < 1) { help(); exit(1); } median = flen/2; if (gvmode == 0 && (p = getenv("WFDBGVMODE"))) gvmode = atoi(p); setgvmode(gvmode|WFDB_GVPAD); if ((nsig = isigopen(irec, NULL, 0)) <= 0) exit(2); if ((si = (WFDB_Siginfo *)malloc(nsig * sizeof(WFDB_Siginfo))) == NULL || (so = (WFDB_Siginfo *)malloc(nsig * sizeof(WFDB_Siginfo))) == NULL || (vtemp = (int *)calloc((unsigned)flen, sizeof(int))) == NULL || (vin = (int **)calloc((unsigned)flen, sizeof(int *))) == NULL || (vout = (int *)malloc(nsig * sizeof(int))) == NULL) memerr(); for (i = 0; i < flen; i++) if ((vin[i] = (int *)calloc((unsigned)nsig, sizeof(int))) == NULL) memerr(); if (isigopen(irec, si, (unsigned)nsig) != nsig) exit(2); if (nrec) { if ((ofname = (char *)malloc(strlen(nrec)+5)) == NULL) memerr(); (void)sprintf(ofname, "%s.dat", nrec); format = si[0].fmt; for (i = 0; i < nsig; i++) { si[i].fname = ofname; si[i].fmt = format; si[i].spf = 1; } if (osigfopen(si, (unsigned)nsig) < nsig) exit(2); setsampfreq(sampfreq(NULL)); } else if (osigopen(orec, so, (unsigned)nsig) != nsig) exit(2); if (from > 0L) { if ((from = strtim(argv[from])) < 0L) from = -from; } if (to > 0L) { if ((to = strtim(argv[to])) < 0L) to = -to; if (to > 0L && to <= from) { (void)fprintf(stderr, "%s: improper interval specified\n", pname); exit(1); } } spm = strtim("1:0"); /* samples per minute */ tt = from + spm; /* time to print next progress indicator */ } void memerr() { (void)fprintf(stderr, "%s: insufficient memory\n", pname); exit(2); } char *prog_name(s) char *s; { char *p = s + strlen(s); #ifdef MSDOS while (p >= s && *p != '\\' && *p != ':') { if (*p == '.') *p = '\0'; /* strip off extension */ if ('A' <= *p && *p <= 'Z') *p += 'a' - 'A'; /* convert to lower case */ p--; } #else while (p >= s && *p != '/') p--; #endif return (p+1); } static char *help_strings[] = { "usage: %s -l N [OPTIONS ...]\n", "where N is the filter length (output is median of N samples in each signal),", "and OPTIONS may include:", " -f TIME begin at specified time", " -h print this usage summary", " -H read multifrequency signals in high resolution mode", " -i IREC read signals from record IREC (default: 16)", " -n NREC create a header file, using record name NREC and signal", " specifications from IREC", " -o OREC produce output signal file(s) as specified by the header file", " for record OREC (default: 16)", " -t TIME stop at specified time", NULL }; void help() { int i; (void)fprintf(stderr, help_strings[0], pname); for (i = 1; help_strings[i] != NULL; i++) (void)fprintf(stderr, "%s\n", help_strings[i]); }