X-Git-Url: http://git.kpe.io/?p=ctsim.git;a=blobdiff_plain;f=libctsim%2Ffilter.cpp;h=3a0e1c799aad5d156e402653b0050b933890d51a;hp=b2d3062c7dbe17934f1e78d4dbf6ceb55d6a0998;hb=a5139613f6b92aeb8db9daa87f021c78d17d82a5;hpb=44ba9ce559d2d52cbd7bbea6bcd76242840fd3eb diff --git a/libctsim/filter.cpp b/libctsim/filter.cpp index b2d3062..3a0e1c7 100644 --- a/libctsim/filter.cpp +++ b/libctsim/filter.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -** FILE IDENTIFICATION +** File IDENTIFICATION ** ** Name: filter.cpp ** Purpose: Routines for signal-procesing filters @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (C) 1983-2000 Kevin Rosenberg ** -** $Id: filter.cpp,v 1.2 2000/06/20 17:54:51 kevin Exp $ +** $Id: filter.cpp,v 1.27 2000/08/22 16:49:56 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -27,65 +27,306 @@ #include "ct.h" +int SignalFilter::N_INTEGRAL=500; //static member + +const int SignalFilter::FILTER_INVALID = -1 ; +const int SignalFilter::FILTER_ABS_BANDLIMIT = 0; // filter times |x = | +const int SignalFilter::FILTER_ABS_G_HAMMING = 1; +const int SignalFilter::FILTER_ABS_COSINE = 2; +const int SignalFilter::FILTER_ABS_SINC = 3; +const int SignalFilter::FILTER_SHEPP = 4; +const int SignalFilter::FILTER_BANDLIMIT = 5; +const int SignalFilter::FILTER_SINC = 6; +const int SignalFilter::FILTER_G_HAMMING = 7; +const int SignalFilter::FILTER_COSINE = 8; +const int SignalFilter::FILTER_TRIANGLE = 9; + +const char* SignalFilter::s_aszFilterName[] = { + {"abs_bandlimit"}, + {"abs_hamming"}, + {"abs_cosine"}, + {"abs_sinc"}, + {"shepp"}, + {"bandlimit"}, + {"sinc"}, + {"hamming"}, + {"cosine"}, + {"triangle"}, +}; + +const char* SignalFilter::s_aszFilterTitle[] = { + {"Abs(w) * Bandlimit"}, + {"Abs(w) * Hamming"}, + {"Abs(w) * Cosine"}, + {"Abs(w) * Sinc"}, + {"Shepp"}, + {"Bandlimit"}, + {"Sinc"}, + {"Hamming"}, + {"Cosine"}, + {"Triangle"}, +}; + +const int SignalFilter::s_iFilterCount = sizeof(s_aszFilterName) / sizeof(const char*); + + +const int SignalFilter::DOMAIN_INVALID = -1; +const int SignalFilter::DOMAIN_FREQUENCY = 0; +const int SignalFilter::DOMAIN_SPATIAL = 1; + +const char* SignalFilter::s_aszDomainName[] = { + {"frequency"}, + {"spatial"}, +}; + +const char* SignalFilter::s_aszDomainTitle[] = { + {"Frequency"}, + {"Spatial"}, +}; + +const int SignalFilter::s_iDomainCount = sizeof(s_aszDomainName) / sizeof(const char*); + /* NAME - * filter_generate Generate a filter + * SignalFilter::SignalFilter Construct a signal * * SYNOPSIS - * f = filter_generate (filt_type, bw, xmin, xmax, n, param, domain, analytic) - * double f Generated filter vector - * int filt_type Type of filter wanted - * double bw Bandwidth of filter - * double xmin, xmax Filter limits - * int n Number of points in filter - * double param General input parameter to filters - * int domain FREQ or SPATIAL domain wanted - * int numint Number if intervals for calculating - * discrete inverse fourier xform - * for spatial domain filters. For - * ANALYTIC solutions, use numint = 0 + * f = SignalFilter (filt_type, bw, filterMin, filterMax, n, param, domain, analytic) + * double f Generated filter vector + * int filt_type Type of filter wanted + * double bw Bandwidth of filter + * double filterMin, filterMax Filter limits + * int nFilterPoints Number of points in signal + * double param General input parameter to filters + * int domain FREQUENCY or SPATIAL domain wanted */ -SignalFilter::SignalFilter (const FilterType filt_type, double bw, double xmin, double xmax, int n, double param, const DomainType domain, int numint) +SignalFilter::SignalFilter (const char* szFilterName, double dFilterMinimum, double dFilterMaximum, int nFilterPoints, double dBandwidth, double dFilterParam, const char* szDomainName) + : m_adFilter(NULL), m_fail(false) +{ + m_idFilter = convertFilterNameToID (szFilterName); + if (m_idFilter == FILTER_INVALID) { + m_fail = true; + m_failMessage = "Invalid Filter name "; + m_failMessage += szFilterName; + return; + } + m_idDomain = convertDomainNameToID (szDomainName); + if (m_idDomain == DOMAIN_INVALID) { + m_fail = true; + m_failMessage = "Invalid domain name "; + m_failMessage += szDomainName; + return; + } + init (m_idFilter, dFilterMinimum, dFilterMaximum, nFilterPoints, dBandwidth, dFilterParam, m_idDomain); +} + +SignalFilter::SignalFilter (const int idFilter, double dFilterMinimum, double dFilterMaximum, int nFilterPoints, double dBandwidth, double dFilterParam, const int idDomain) + : m_adFilter(NULL), m_fail(false) { - m_vecFilter = new double [n]; - m_filterType = filt_type; - m_bw = bw; + init (idFilter, dFilterMinimum, dFilterMaximum, nFilterPoints, dBandwidth, dFilterParam, idDomain); +} - double xinc = (xmax - xmin) / (n - 1); +SignalFilter::SignalFilter (const char* szFilterName, const char* szDomainName, double dBandwidth, double dFilterParam) + : m_adFilter(NULL), m_fail(false) +{ + m_nFilterPoints = 0; + m_dBandwidth = dBandwidth; + m_dFilterParam = dFilterParam; + m_idFilter = convertFilterNameToID (szFilterName); + if (m_idFilter == FILTER_INVALID) { + m_fail = true; + m_failMessage = "Invalid Filter name "; + m_failMessage += szFilterName; + return; + } + m_idDomain = convertDomainNameToID (szDomainName); + if (m_idDomain == DOMAIN_INVALID) { + m_fail = true; + m_failMessage = "Invalid domain name "; + m_failMessage += szDomainName; + return; + } +} - if (m_filterType == FILTER_SHEPP) { - double a = 2 * m_bw; +void +SignalFilter::init (const int idFilter, double dFilterMinimum, double dFilterMaximum, int nFilterPoints, double dBandwidth, double dFilterParam, const int idDomain) +{ + m_idFilter = idFilter; + m_idDomain = idDomain; + if (m_idFilter == FILTER_INVALID || m_idDomain == DOMAIN_INVALID) { + m_fail = true; + return; + } + if (nFilterPoints < 2) { + m_fail = true; + m_failMessage = "Number of filter points "; + m_failMessage += nFilterPoints; + m_failMessage = " less than 2"; + return; + } + + m_nameFilter = convertFilterIDToName (m_idFilter); + m_nameDomain = convertDomainIDToName (m_idDomain); + m_nFilterPoints = nFilterPoints; + m_dFilterParam = dFilterParam; + m_dBandwidth = dBandwidth; + m_dFilterMin = dFilterMinimum; + m_dFilterMax = dFilterMaximum; + + m_dFilterInc = (m_dFilterMax - m_dFilterMin) / (m_nFilterPoints - 1); + m_adFilter = new double [m_nFilterPoints]; + + if (m_idDomain == DOMAIN_FREQUENCY) + createFrequencyFilter (m_adFilter); + else if (m_idDomain == DOMAIN_SPATIAL) + createSpatialFilter (m_adFilter); +} + + +SignalFilter::~SignalFilter (void) +{ + delete [] m_adFilter; +} + +void +SignalFilter::createFrequencyFilter (double* adFilter) const +{ + double x; + int i; + for (x = m_dFilterMin, i = 0; i < m_nFilterPoints; x += m_dFilterInc, i++) + adFilter[i] = frequencyResponse (x); +} + + +void +SignalFilter::createSpatialFilter (double* adFilter) const +{ + if (m_idFilter == FILTER_SHEPP) { + double a = 2 * m_dBandwidth; double c = - 4. / (a * a); - int center = (n - 1) / 2; + int center = (m_nFilterPoints - 1) / 2; int sidelen = center; - m_vecFilter[center] = 4. / (a * a); - + m_adFilter[center] = 4. / (a * a); + for (int i = 1; i <= sidelen; i++ ) - m_vecFilter [center + i] = m_vecFilter [center - i] = c / (4 * (i * i) - 1); - } else if (domain == D_FREQ) { - double x; - int i; - for (x = xmin, i = 0; i < n; x += xinc, i++) - m_vecFilter[i] = frequencyResponse (x, param); - } else if (domain == D_SPATIAL) { - double x; - int i; - for (x = xmin, i = 0; i < n; x += xinc, i++) - if (numint == 0) - m_vecFilter[i] = spatialResponseAnalytic (x, param); - else - m_vecFilter[i] = spatialResponseCalc (x, param, numint); + m_adFilter [center + i] = m_adFilter [center - i] = c / (4 * (i * i) - 1); } else { - sys_error (ERR_WARNING, "Illegal domain %d [filt_generate]", domain); + double x = m_dFilterMin; + for (int i = 0; i < m_nFilterPoints; i++, x += m_dFilterInc) { + if (haveAnalyticSpatial(m_idFilter)) + m_adFilter[i] = spatialResponseAnalytic (x); + else + m_adFilter[i] = spatialResponseCalc (x); + } } } -SignalFilter::~SignalFilter (void) +int +SignalFilter::convertFilterNameToID (const char *filterName) +{ + int filterID = FILTER_INVALID; + + for (int i = 0; i < s_iFilterCount; i++) + if (strcasecmp (filterName, s_aszFilterName[i]) == 0) { + filterID = i; + break; + } + + return (filterID); +} + +const char * +SignalFilter::convertFilterIDToName (const int filterID) +{ + static const char *name = ""; + + if (filterID >= 0 && filterID < s_iFilterCount) + return (s_aszFilterName [filterID]); + + return (name); +} + +const char * +SignalFilter::convertFilterIDToTitle (const int filterID) +{ + static const char *title = ""; + + if (filterID >= 0 && filterID < s_iFilterCount) + return (s_aszFilterTitle [filterID]); + + return (title); +} + +int +SignalFilter::convertDomainNameToID (const char* const domainName) +{ + int dID = DOMAIN_INVALID; + + for (int i = 0; i < s_iDomainCount; i++) + if (strcasecmp (domainName, s_aszDomainName[i]) == 0) { + dID = i; + break; + } + + return (dID); +} + +const char * +SignalFilter::convertDomainIDToName (const int domainID) +{ + static const char *name = ""; + + if (domainID >= 0 && domainID < s_iDomainCount) + return (s_aszDomainName [domainID]); + + return (name); +} + +const char * +SignalFilter::convertDomainIDToTitle (const int domainID) +{ + static const char *title = ""; + + if (domainID >= 0 && domainID < s_iDomainCount) + return (s_aszDomainTitle [domainID]); + + return (title); +} + + +double +SignalFilter::response (double x) +{ + double response = 0; + + if (m_idDomain == DOMAIN_SPATIAL) + response = spatialResponse (m_idFilter, m_dBandwidth, x, m_dFilterParam); + else if (m_idDomain == DOMAIN_FREQUENCY) + response = frequencyResponse (m_idFilter, m_dBandwidth, x, m_dFilterParam); + + return (response); +} + + +double +SignalFilter::spatialResponse (int filterID, double bw, double x, double param) { - delete m_vecFilter; + if (haveAnalyticSpatial(filterID)) + return spatialResponseAnalytic (filterID, bw, x, param); + else + return spatialResponseCalc (filterID, bw, x, param, N_INTEGRAL); } +void +SignalFilter::copyFilterData (double* pdFilter, const int iStart, const int nPoints) const +{ + int iFirst = clamp (iStart, 0, m_nFilterPoints - 1); + int iLast = clamp (iFirst + nPoints - 1, 0, m_nFilterPoints - 1); + + for (int i = iFirst; i <= iLast; i++) + pdFilter[i - iFirst] = m_adFilter[i]; +} /* NAME * filter_spatial_response_calc Calculate filter by discrete inverse fourier @@ -103,17 +344,17 @@ SignalFilter::~SignalFilter (void) */ double -SignalFilter::spatialResponseCalc (double x, double param, int n) const +SignalFilter::spatialResponseCalc (double x) const { - return (spatialResponseCalc (m_filterType, m_bw, x, param, n)); + return (spatialResponseCalc (m_idFilter, m_dBandwidth, x, m_dFilterParam, N_INTEGRAL)); } double -SignalFilter::spatialResponseCalc (FilterType fType, double bw, double x, double param, int n) +SignalFilter::spatialResponseCalc (int filterID, double bw, double x, double param, int n) { double zmin, zmax; - if (fType == FILTER_TRIANGLE) { + if (filterID == FILTER_TRIANGLE) { zmin = 0; zmax = bw; } else { @@ -125,7 +366,7 @@ SignalFilter::spatialResponseCalc (FilterType fType, double bw, double x, double double z = zmin; double q [n]; for (int i = 0; i < n; i++, z += zinc) - q[i] = frequencyResponse (fType, bw, z, param) * cos (TWOPI * z * x); + q[i] = frequencyResponse (filterID, bw, z, param) * cos (TWOPI * z * x); double y = 2 * integrateSimpson (zmin, zmax, q, n); @@ -146,45 +387,45 @@ SignalFilter::spatialResponseCalc (FilterType fType, double bw, double x, double */ double -SignalFilter::frequencyResponse (double u, double param) const +SignalFilter::frequencyResponse (double u) const { - return frequencyResponse (m_filterType, m_bw, u, param); + return frequencyResponse (m_idFilter, m_dBandwidth, u, m_dFilterParam); } double -SignalFilter::frequencyResponse (FilterType fType, double bw, double u, double param) +SignalFilter::frequencyResponse (int filterID, double bw, double u, double param) { double q; double au = fabs (u); - switch (fType) { + switch (filterID) { case FILTER_BANDLIMIT: - if (au >= bw / 2) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0.; else q = 1; break; case FILTER_ABS_BANDLIMIT: - if (au >= bw / 2) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0.; else q = au; break; case FILTER_TRIANGLE: - if (au >= bw) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0; else q = 1 - au / bw; break; case FILTER_COSINE: - if (au >= bw / 2) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0; else q = cos(PI * u / bw); break; case FILTER_ABS_COSINE: - if (au >= bw / 2) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0; else q = au * cos(PI * u / bw); @@ -196,20 +437,20 @@ SignalFilter::frequencyResponse (FilterType fType, double bw, double u, double p q = au * bw * sinc (PI * bw * u, 1.); break; case FILTER_G_HAMMING: - if (au >= bw / 2) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0; else q = param + (1 - param) * cos (TWOPI * u / bw); break; case FILTER_ABS_G_HAMMING: - if (au >= bw / 2) + if (fabs(au) >= fabs(bw / 2) + F_EPSILON) q = 0; else q = au * (param + (1 - param) * cos(TWOPI * u / bw)); break; default: q = 0; - sys_error (ERR_WARNING, "Frequency response for filter %d not implemented [filter_frequency_response]", fType); + sys_error (ERR_WARNING, "Frequency response for filter %d not implemented [filter_frequency_response]", filterID); break; } return (q); @@ -232,13 +473,37 @@ SignalFilter::frequencyResponse (FilterType fType, double bw, double u, double p */ double -SignalFilter::spatialResponseAnalytic (double x, double param) const +SignalFilter::spatialResponseAnalytic (double x) const { - return spatialResponseAnalytic (m_filterType, m_bw, x, param); + return spatialResponseAnalytic (m_idFilter, m_dBandwidth, x, m_dFilterParam); +} + +const bool +SignalFilter::haveAnalyticSpatial (int filterID) +{ + bool haveAnalytic = false; + + switch (filterID) { + case FILTER_BANDLIMIT: + case FILTER_TRIANGLE: + case FILTER_COSINE: + case FILTER_G_HAMMING: + case FILTER_ABS_BANDLIMIT: + case FILTER_ABS_COSINE: + case FILTER_ABS_G_HAMMING: + case FILTER_SHEPP: + case FILTER_SINC: + haveAnalytic = true; + break; + default: + break; + } + + return (haveAnalytic); } double -SignalFilter::spatialResponseAnalytic (FilterType fType, double bw, double x, double param) +SignalFilter::spatialResponseAnalytic (int filterID, double bw, double x, double param) { double q, temp; double u = TWOPI * x; @@ -246,7 +511,7 @@ SignalFilter::spatialResponseAnalytic (FilterType fType, double bw, double x, do double b = PI / bw; double b2 = TWOPI / bw; - switch (fType) { + switch (filterID) { case FILTER_BANDLIMIT: q = bw * sinc(u * w, 1.0); break; @@ -284,7 +549,7 @@ SignalFilter::spatialResponseAnalytic (FilterType fType, double bw, double x, do break; case FILTER_ABS_SINC: default: - sys_error (ERR_WARNING, "Analytic filter type %d not implemented [filter_spatial_response_analytic]", fType); + sys_error (ERR_WARNING, "Analytic filter type %d not implemented [filter_spatial_response_analytic]", filterID); q = 0; break; } @@ -319,106 +584,4 @@ SignalFilter::spatialResponseAnalytic (FilterType fType, double bw, double x, do * Returns the value of integral of u*cos(u)*dV for V = 0 to w */ -double -SignalFilter::integral_abscos (double u, double w) -{ - if (fabs (u) > F_EPSILON) - return (cos(u * w) - 1) / (u * u) + w / u * sin (u * w); - else - return (w * w / 2); -} - - -/* NAME - * convolve Discrete convolution of two functions - * - * SYNOPSIS - * r = convolve (f1, f2, dx, n, np, func_type) - * double r Convolved result - * double f1[], f2[] Functions to be convolved - * double dx Difference between successive x values - * int n Array index to center convolution about - * int np Number of points in f1 array - * int func_type EVEN or ODD or EVEN_AND_ODD function f2 - * - * NOTES - * f1 is the projection data, its indices range from 0 to np - 1. - * The index for f2, the filter, ranges from -(np-1) to (np-1). - * There are 3 ways to handle the negative vertices of f2: - * 1. If we know f2 is an EVEN function, then f2[-n] = f2[n]. - * All filters used in reconstruction are even. - * 2. If we know f2 is an ODD function, then f2[-n] = -f2[n] - * 3. If f2 is both ODD AND EVEN, then we must store the value of f2 - * for negative indices. Since f2 must range from -(np-1) to (np-1), - * if we add (np - 1) to f2's array index, then f2's index will - * range from 0 to 2 * (np - 1), and the origin, x = 0, will be - * stored at f2[np-1]. - */ - -double -SignalFilter::convolve (const double func[], const double dx, const int n, const int np, const FunctionSymmetry func_type) const -{ - double sum = 0.0; - - if (func_type == FUNC_BOTH) { -#if UNOPTIMIZED_CONVOLUTION - for (int i = 0; i < np; i++) - sum += func[i] * m_vecFilter[n - i + (np - 1)]; -#else - double* f2 = m_vecFilter + n + (np - 1); - for (int i = 0; i < np; i++) - sum += *func++ * *f2--; -#endif - } else if (func_type == FUNC_EVEN) { - for (int i = 0; i < np; i++) { - int k = abs (n - i); - sum += func[i] * m_vecFilter[k]; - } - } else if (func_type == FUNC_ODD) { - for (int i = 0; i < np; i++) { - int k = n - i; - if (k < 0) - sum -= func[i] * m_vecFilter[k]; - else - sum += func[i] * m_vecFilter[k]; - } - } else - sys_error (ERR_WARNING, "Illegal function type %d [convolve]", func_type); - - return (sum * dx); -} - - -double -SignalFilter::convolve (const float func[], const double dx, const int n, const int np, const FunctionSymmetry func_type) const -{ - double sum = 0.0; - - if (func_type == FUNC_BOTH) { -#if UNOPTIMIZED_CONVOLUTION - for (int i = 0; i < np; i++) - sum += func[i] * m_vecFilter[n - i + (np - 1)]; -#else - double* f2 = m_vecFilter + n + (np - 1); - for (int i = 0; i < np; i++) - sum += *func++ * *f2--; -#endif - } else if (func_type == FUNC_EVEN) { - for (int i = 0; i < np; i++) { - int k = abs (n - i); - sum += func[i] * m_vecFilter[k]; - } - } else if (func_type == FUNC_ODD) { - for (int i = 0; i < np; i++) { - int k = n - i; - if (k < 0) - sum -= func[i] * m_vecFilter[k]; - else - sum += func[i] * m_vecFilter[k]; - } - } else - sys_error (ERR_WARNING, "Illegal function type %d [convolve]", func_type); - - return (sum * dx); -}