

include <imhdr.h>
include <gset.h>
include <error.h> # for EA_ERROR
include <time.h> # for SZ_TIME
include <tbset.h>
define BW_TYPE_IMAGE 1 # type of input or output
define BW_TYPE_TABLE 2
define BW_NPAR 4 # number of parameters to fit to power spectrum
define SIGNAL_A $1[1] # a + b*x + c*x**2
define SIGNAL_B $1[2] # actually, this is zero
define SIGNAL_C $1[3]
define NOISE $1[4]
# t_bwfilter -- Fourier filter of a 1-D array
# See Brault & White, 1971, Astron & Astrophys, vol 13, p 169.
# This task filters a 1-D array in the following way. After subtracting
# a background from the input, the forward Fourier transform is taken.
# The data in the transform domain are multiplied by a filter of the form:
# signal / (signal + noise). The inverse Fourier transform is then taken,
# the background is added back in, and the result is written to the output.
#
# The input and output may be either an image or a table column, or one of
# each. For either the 'input' or 'output' parameter, if the value is a
# single word it is assumed to be an image name, but if it is two words
# separated by one or more spaces it is assumed to be a table name and a
# column name. If the output table already exists it will be written to
# in-place; if the column exists it will be overwritten.
#
# The background that is subtracted before taking the Fourier transform
# is obtained by fitting a straight line. The linear fit is done to the
# log of the data if all values are positive; otherwise, the fit is done to
# the data values directly.
#
# The signal and noise are estimated as follows. The task fits a constant
# (the noise level) to the higher frequencies, and it fits a quadratic
# (the signal) to the log power spectrum at the lower frequencies. Then
# the filter in the Fourier domain is signal / (signal + noise).
#
# Phil Hodge, 19-Aug-1994 Task created.
# Phil Hodge, 12-Jan-1995 If SIGNAL_C(p) >= 0, change sign and continue
# rather than stopping with an error.
procedure t_bwfilter()
pointer input, output # scratch for names of input & output images
real freq # frequency where signal is comparable to noise
bool interactive # true if interactive mode
pointer device # graphics device (e.g. "stdgraph")
#--
pointer sp
pointer history # for history record for output image
pointer colname # column name for history record
pointer oim # pointer to struct for output image or table
pointer ocp # pointer to column descriptor if output is table
pointer ix, ox # pointers to data for input, output
int npix # size of array
int outtype # type of output (image or table)
real clgetr()
bool clgetb()
pointer datetime # for getting date & time
long old_time, now
long clktime()
begin
call smark (sp)
call salloc (input, SZ_FNAME, TY_CHAR)
call salloc (output, SZ_FNAME, TY_CHAR)
call salloc (device, SZ_FNAME, TY_CHAR)
call salloc (history, SZ_LINE, TY_CHAR)
call salloc (datetime, SZ_TIME, TY_CHAR)
call salloc (colname, SZ_COLNAME, TY_CHAR)
call clgstr ("input", Memc[input], SZ_FNAME)
call clgstr ("output", Memc[output], SZ_FNAME)
interactive = clgetb ("interactive")
if (interactive) {
freq = 0.25 # default
} else {
freq = clgetr ("freq")
if (freq <= 0.)
call error (1, "frequency must be positive")
}
call clgstr ("device", Memc[device], SZ_FNAME)
# Open input and output, and get data.
call bw_get_io (Memc[input], Memc[output], oim, ocp, ix, ox,
npix, outtype)
# Apply the filter
call bwfilter (Memr[ix], Memr[ox], npix,
interactive, "cursor", Memc[device], freq)
# If the output is a table, we must copy the data to it.
if (outtype == BW_TYPE_TABLE)
call tbcptr (oim, ocp, Memr[ox], 1, npix)
# Add two history records to output.
now = clktime (old_time)
call cnvtime (now, Memc[datetime], SZ_TIME)
call sprintf (Memc[history], SZ_LINE, "Filtered by bwfilter on %s;")
call pargstr (Memc[datetime])
if (outtype == BW_TYPE_IMAGE)
call imputh (oim, "history", Memc[history])
else
call tbhadt (oim, "history", Memc[history])
call sprintf (Memc[history], SZ_LINE,
" cutoff frequency = %.5g cycles per pixel")
call pargr (freq)
if (outtype == BW_TYPE_IMAGE) {
call strcat (".", Memc[history], SZ_LINE)
call imputh (oim, "history", Memc[history])
} else {
call strcat (";", Memc[history], SZ_LINE)
call tbhadt (oim, "history", Memc[history])
}
if (outtype == BW_TYPE_TABLE) {
call tbcigt (ocp, TBL_COL_NAME, Memc[colname], SZ_COLNAME)
call sprintf (Memc[history], SZ_LINE,
" column name for filtered data is %s.")
call pargstr (Memc[colname])
call tbhadt (oim, "history", Memc[history])
}
# Close output, free memory.
if (outtype == BW_TYPE_IMAGE) {
call imunmap (oim)
} else {
call tbtclo (oim)
call mfree (ox, TY_REAL)
}
call mfree (ix, TY_REAL)
call sfree (sp)
end
# bw_get_io -- get input data and space for output
# The input and output may be either an image or a table, or one of each.
# For either the input or output, if the value is a single word it is
# assumed to be an image name, but if it is two words it is assumed to be
# a table name and a column name. If the output table already exists it
# will be written to in-place; if the column exists it will be overwritten.
#
# If the output is an image:
# write the data to Memr[ox], and call imunmap (oim).
#
# If the output is a table:
# write the data to Memr[ox], call tbcptr to write it to the table,
# call tbtclo (oim), and call mfree (ox, TY_REAL).
#
# In either case call mfree (ix, TY_REAL).
procedure bw_get_io (input, output, oim, ocp, ix, ox, npix, outtype)
char input[ARB] # i: name of input
char output[ARB] # i: name of output
pointer oim # o: pointer to structure describing output
pointer ocp # o: pointer to output column descriptor
pointer ix # o: pointer to input data
pointer ox # o: pointer to space for output data
int npix # o: size of space pointed to by ix and ox
int outtype # o: type of output (image or table)
#--
pointer sp
pointer fname # scratch for name of image or table
pointer iim # pointer to input image or table structure
pointer iscr # for temporary storage of input image data
pointer icp # pointer to input column descriptor
pointer colname # scratch for table column name
pointer nullflag # array of null flags from table column
int intype # type of input (image or table)
int ip, ctowrd()
pointer immap(), imgl1r(), impl1r()
pointer tbtopn()
int tbpsta(), tbtacc()
errchk immap, tbtopn, tbtcre
begin
call smark (sp)
call salloc (fname, SZ_FNAME, TY_CHAR)
call salloc (colname, SZ_FNAME, TY_CHAR)
# Open the input image or table.
ip = 1
if (ctowrd (input, ip, Memc[fname], SZ_FNAME) < 1)
call error (1, "no input specified")
if (ctowrd (input, ip, Memc[colname], SZ_FNAME) < 1) {
# Only one "word"; this must be an image name.
intype = BW_TYPE_IMAGE
iim = immap (Memc[fname], READ_ONLY, NULL)
npix = IM_LEN(iim,1)
if (IM_NDIM(iim) != 1) {
call imunmap (iim)
call error (1, "bwfilter: image must be 1-D")
}
iscr = imgl1r (iim)
# Allocate memory for input data, and copy image data to it.
call malloc (ix, npix, TY_REAL)
call amovr (Memr[iscr], Memr[ix], npix)
} else {
# Two "words"; these must be table and column names.
intype = BW_TYPE_TABLE
iim = tbtopn (Memc[fname], READ_ONLY, NULL)
npix = tbpsta (iim, TBL_NROWS)
call tbcfnd (iim, Memc[colname], icp, 1) # find column name
# Allocate memory for input data, and read it from the table.
call malloc (ix, npix, TY_REAL)
call salloc (nullflag, npix, TY_BOOL)
call tbcgtr (iim, icp, Memr[ix], Memb[nullflag], 1, npix)
}
# Open the output image or table.
ip = 1
if (ctowrd (output, ip, Memc[fname], SZ_FNAME) < 1)
call error (1, "no output specified")
if (ctowrd (output, ip, Memc[colname], SZ_FNAME) < 1) {
# Only one "word"; this must be an image name.
outtype = BW_TYPE_IMAGE
if (intype == BW_TYPE_IMAGE) {
oim = immap (Memc[fname], NEW_COPY, iim)
IM_PIXTYPE(oim) = TY_REAL
ox = impl1r (oim)
call imunmap (iim) # we're done with input image
} else {
call tbtclo (iim) # we're done with input table
# We can't make a NEW_COPY image from a table.
oim = immap (Memc[fname], NEW_FILE, NULL)
IM_NDIM(oim) = 1
IM_LEN(oim,1) = npix
IM_PIXTYPE(oim) = TY_REAL
ox = impl1r (oim)
}
} else {
# Two "words"; these must be table and column names.
outtype = BW_TYPE_TABLE
# We're done with the input, so close it.
if (intype == BW_TYPE_TABLE)
call tbtclo (iim)
else
call imunmap (iim)
if (tbtacc (Memc[fname]) == YES) {
# Open an existing table. If the column already exists,
# overwrite it; otherwise, create it.
oim = tbtopn (Memc[fname], READ_WRITE, NULL)
call tbcfnd (oim, Memc[colname], ocp, 1)
if (ocp == NULL) # not found; create it
call tbcdef (oim, ocp, Memc[colname], "", "", TY_REAL, 1, 1)
} else {
# Create a new table.
oim = tbtopn (Memc[fname], NEW_FILE, NULL)
call tbcdef (oim, ocp, Memc[colname], "", "", TY_REAL, 1, 1)
call tbtcre (oim)
}
# Allocate memory for output data.
call malloc (ox, npix, TY_REAL)
}
call sfree (sp)
end
# bwfilter -- Brault & White optimal filter
procedure bwfilter (inr, outr, npix, interactive, cursor, device, freq)
real inr[npix] # i: input data
real outr[npix] # o: output data
int npix # i: size of arrays
bool interactive # i: true if interactive mode
char cursor[ARB] # i: graphics cursor parameter
char device[ARB] # i: graphics device
real freq # io: frequency where signal is comparable to noise
#--
pointer sp
pointer xdata # complex array containing data or Fourier transform
pointer bkg # complex array containing background data
pointer pspec # real array containing log power spectrum
real p[BW_NPAR] # parameters describing fit to power spectrum
real cd1_1 # frequency interval per pixel
real ampl # Y value of cursor position (ignored)
int half # size of power spectrum
bool append # append to current plot?
errchk bw_forward, bw_inverse, bw_plot, bw_clgcur,
bw_get_filt, bw_apply, bw_pspec
begin
call smark (sp)
if (freq <= 0. && !interactive)
call error (1, "frequency must be positive")
# Subtract one because we ignore zero frequency for the power spectrum.
half = (npix + 1) / 2 - 1
# Pixel spacing in the Fourier domain. Just pixel coordinates.
cd1_1 = 1. / real(npix)
# Allocate space for the power spectrum.
call salloc (pspec, half, TY_REAL)
# Read the input data, and do the forward transform, putting
# the result in Memx[xdata].
# Also subtract a background, which is saved in Memr[bkg].
call bw_forward (inr, xdata, bkg, npix)
# Compute the log power spectrum.
call bw_pspec (Memx[xdata], Memr[pspec], half)
if (interactive) {
# Get frequency interactively.
append = false
call bw_plot (Memr[pspec], half, device, append, cd1_1)
call bw_clgcur (cursor, freq, ampl)
}
# Find the filter by fitting to the power spectrum.
call bw_get_filt (Memr[pspec], half, freq, p, cd1_1)
# Apply the filter to xdata.
call bw_apply (Memx[xdata], npix, p)
if (interactive) {
# Compute and plot the power spectrum of the filtered xdata.
call bw_pspec (Memx[xdata], Memr[pspec], half)
append = true
call bw_plot (Memr[pspec], half, device, append, cd1_1)
}
# Take the inverse transform, add the background back in,
# and write the result to the output image.
call bw_inverse (Memx[xdata], Memr[bkg], outr, npix)
# Deallocate memory allocated by bw_forward.
call mfree (bkg, TY_REAL)
call mfree (xdata, TY_COMPLEX)
call sfree (sp)
end
# bw_forward -- take forward Fourier transform
# Note that this routine also gets the "ftpairs" cl parameter and
# reads that file.
procedure bw_forward (inr, xdata, bkg, npix)
real inr[npix] # i: input array
pointer xdata # o: pointer to input data (minus background)
pointer bkg # o: pointer to background subtracted from input
int npix # i: size of arrays
#--
complex z # for normalizing the forward transform
bool fwd # forward transform? (true)
errchk bw_sub_bkg
begin
fwd = true
call malloc (xdata, npix, TY_COMPLEX)
call malloc (bkg, npix, TY_REAL)
# Fit and subtract a background.
call bw_sub_bkg (inr, Memx[xdata], Memr[bkg], npix)
# Do the forward Fourier transform in-place in the complex array.
call bw_cmplx (Memx[xdata], npix, fwd)
z = cmplx (npix, 0)
call adivkx (Memx[xdata], z, Memx[xdata], npix) # normalize
end
# bw_inverse -- take inverse Fourier transform
procedure bw_inverse (xdata, bkg, outr, npix)
complex xdata[npix] # i: input data (minus background)
real bkg[npix] # i: background to be added to output
real outr[npix] # o: output array
int npix # i: size of arrays
#--
int i
bool fwd # forward transform? (false)
begin
fwd = false
# Do the inverse Fourier transform in-place in the complex array.
call bw_cmplx (xdata, npix, fwd)
# Add the background back in, converting to real.
do i = 1, npix
outr[i] = real (xdata[i]) + bkg[i]
end
# bw_sub_bkg -- subtract background
# Subtract a straight line. If all the data are positive, we'll fit
# to the log of the data; otherwise, we'll fit to the data directly.
procedure bw_sub_bkg (inr, xdata, bkg, npix)
real inr[npix] # i: input data
complex xdata[npix] # o: data with background subtracted
real bkg[npix] # o: background that was subtracted
int npix # i: size of arrays
#--
pointer sp
pointer scr # either a copy of inr or log(inr)
real minval, maxval # min and max of data values
real x, y # i, inr[i]
real sumx, sumxx, sumy, sumxy # for fitting
real d # for fitting
real a, b # coefficients of fit: a + b*i
int i # loop index
bool loglinear # true if we fit to the log of the data
begin
call smark (sp)
call salloc (scr, npix, TY_REAL)
call alimr (inr, npix, minval, maxval)
# If all the values are greater than zero, take the log before
# fitting a line; otherwise, just copy.
loglinear = (minval > 0.)
if (loglinear) {
do i = 1, npix
Memr[scr+i-1] = log10 (inr[i])
} else {
do i = 1, npix
Memr[scr+i-1] = inr[i]
}
# Fit a straight line.
sumx = 0.
sumxx = 0.
sumy = 0.
sumxy = 0.
do i = 1, npix {
x = i
y = Memr[scr+i-1]
sumx = sumx + x
sumxx = sumxx + x * x
sumy = sumy + y
sumxy = sumxy + x * y
}
x = sumx / npix
y = sumy / npix
d = sumxx - sumx**2 / npix
if (d == 0.) {
call eprintf (
"warning: can only fit constant background\n")
b = 0.
a = y
} else {
b = (sumxy - x * y * npix) / d
a = y - x * b
}
# Subtract the background, and convert to complex.
do i = 1, npix {
bkg[i] = a + b * i
if (loglinear)
bkg[i] = 10. ** bkg[i]
xdata[i] = cmplx (inr[i] - bkg[i], 0.)
}
call sfree (sp)
end
# bw_plot -- plot data
procedure bw_plot (pspec, half, device, append, cd1_1)
real pspec[half] # i: array to plot
int half # i: size of array
char device[ARB] # i: graphics device
bool append # i: true if we should append to current plot
real cd1_1 # i: frequency interval per pixel
#--
pointer sp
pointer title # title for plot
pointer gp
pointer x # scratch
real xmin, xmax, ymin, ymax
int i
pointer gopen()
errchk gopen, gswind, glabax, gpline, gclose
begin
call smark (sp)
call salloc (x, half, TY_REAL)
call salloc (title, SZ_LINE, TY_CHAR)
# Assign the X values. (Assumes zero frequency is NOT included.)
do i = 1, half
Memr[x+i-1] = real(i) * cd1_1
if (append) {
gp = gopen (device, APPEND, STDGRAPH)
call gseti (gp, G_PLTYPE, GL_DASHED)
call gpline (gp, Memr[x], pspec, half)
call gclose (gp)
} else {
call sprintf (Memc[title], SZ_LINE, "Power Spectrum")
# Set the range of X values for the plot.
xmin = -Memr[x+half-1] * 0.05
xmax = Memr[x+half-1] * 1.05
# Get the range of Y values for the plot.
call alimr (pspec, half, ymin, ymax)
ymin = ymin - (ymax - ymin) / 20. # extend the range
ymax = ymax + (ymax - ymin) / 20.
gp = gopen (device, NEW_FILE, STDGRAPH)
call gswind (gp, xmin, xmax, ymin, ymax)
call gseti (gp, G_XNMINOR, 5)
call glabax (gp, Memc[title], "Frequency", "Log Power")
call gpline (gp, Memr[x], pspec, half)
call gclose (gp)
}
call sfree (sp)
end
# bw_clgcur -- read cursor
procedure bw_clgcur (param, freq, ampl)
char param[ARB] # i: name of cursor parameter to get
real freq # o: frequency where signal is comparable to noise
real ampl # o: amplitude where signal is comparable to noise
#--
pointer sp
pointer strval # string value (if any) returned by clgcur
real xc, yc # cursor position
int wcs, key # wcs for coords; keystroke key
int clgcur()
begin
call smark (sp)
call salloc (strval, SZ_FNAME, TY_CHAR)
# Read cursor position.
if (clgcur (param, xc, yc, wcs, key, Memc[strval],
SZ_FNAME) == EOF) {
call eprintf ("warning: no cursor position read\n")
} else {
freq = xc
ampl = yc
if (freq <= 0.)
call error (1, "frequency must be positive")
}
call sfree (sp)
end
# bw_get_filt -- fit to the power spectrum
procedure bw_get_filt (pspec, half, freq, p, cd1_1)
real pspec[half] # i: log power spectrum
int half # i: size of array
real freq # i: frequency where signal is comparable to noise
real p[ARB] # o: coefficients of fit
real cd1_1 # i: frequency interval per pixel
#--
real x, y, w # a point and weight
real sumw, sumx2, sumx4, sumy, sumx2y
int n
int i
int fpix # pixel corresponding to freq
int first # beginning of loop for noise
begin
fpix = nint (freq / cd1_1) + 1
if (fpix < 1 || fpix > half)
call error (1, "frequency is out of range")
# Average the noise.
n = 0
sumw = 0.
sumy = 0.
first = fpix + (half - fpix) / 5
if (half - first < max (10, half/10))
first = fpix + 1
do i = fpix+1, half {
w = 1.
#*** w = real (i - fpix) / real (half - fpix)
y = pspec[i]
sumw = sumw + w
sumy = sumy + w * y
n = n + 1
}
if (n < 1 || sumw == 0.)
call error (1, "not enough data at higher frequencies")
y = sumy / sumw
NOISE(p) = y
# Average the signal.
n = 0
sumw = 0.
sumy = 0.
sumx2 = 0.
sumx4 = 0.
sumx2y = 0.
do i = 1, fpix-1 {
w = real (fpix - i) / real (fpix - 1)
# Add 1 to offset for zero freq, since pspec doesn't include it.
x = i + 1
y = pspec[i]
sumw = sumw + w
sumx2 = sumx2 + w * x * x
sumx4 = sumx4 + w * x ** 4
sumy = sumy + w * y
sumx2y = sumx2y + w * x * x * y
n = n + 1
}
if (n < 1 || sumw == 0.)
call error (1, "not enough data near zero frequency")
SIGNAL_A(p) = (sumy * sumx4 - sumx2y * sumx2) /
(sumw * sumx4 - sumx2 ** 2)
SIGNAL_B(p) = 0.
SIGNAL_C(p) = (sumy * sumx2 - sumx2y * sumw) /
(sumx2 ** 2 - sumx4 * sumw)
# If the signal is concave upward, simply change the sign.
if (SIGNAL_C(p) >= 0.) {
call eprintf (
"warning: quadratic fit to low frequencies is concave upward\n")
SIGNAL_C(p) = -SIGNAL_C(p)
}
end
# bw_apply -- apply the filter
procedure bw_apply (xdata, npix, p)
complex xdata[npix] # io: Fourier transform of image
int npix # i: size of array
real p[ARB] # i: coefficients of fit
#--
real filt # filter value at a pixel
real signal, noise # for making filter
int i
begin
noise = 10. ** NOISE(p)
do i = 1, npix/2 {
signal = SIGNAL_A(p) + SIGNAL_C(p) * i*i
#*** signal = SIGNAL_A(p) + SIGNAL_B(p) * i + SIGNAL_C(p) * i*i
signal = 10. ** signal
filt = signal / (signal + noise)
xdata[i] = xdata[i] * filt
xdata[npix-i+1] = xdata[npix-i+1] * filt
}
i = (npix + 1) / 2
if (i > npix/2) { # odd number of pixels
signal = SIGNAL_A(p) + SIGNAL_C(p) * i*i
signal = 10. ** signal
filt = signal / (signal + noise)
xdata[i] = xdata[i] * filt
}
end
# bw_pspec -- compute power spectrum
procedure bw_pspec (xdata, pspec, half)
complex xdata[ARB] # i: input data
real pspec[half] # o: log power spectrum of input
int half # i: size of pspec array
#--
real x, y # real and imaginary parts
real minps, maxps # min & max of power spectrum
int i
begin
# Take the power spectrum of the first half. Ignore the first
# element; since we have subtracted a background, the Fourier
# transform at zero frequency should be nearly zero.
do i = 1, half {
x = real (xdata[i+1]) # skip first element
y = aimag (xdata[i+1])
pspec[i] = x**2 + y**2
}
# Get the minimum non-zero value of the power spectrum.
call alimr (pspec, half, minps, maxps)
if (maxps <= 0.)
call error (1, "power spectrum is entirely zero")
minps = maxps # initialize with maximum
do i = 1, half {
if (pspec[i] > 0.)
minps = min (minps, pspec[i])
}
# Take log (base 10) power spectrum.
do i = 1, half {
if (pspec[i] > 0.)
pspec[i] = log10 (pspec[i])
else
pspec[i] = log10 (minps)
}
end
procedure bw_cmplx (xdata, npix, fwd)
complex xdata[npix] # io: array to be transformed
int npix # i: length of first axis
bool fwd # i: forward transform?
#--
pointer trig # scratch for array of cosines & sines
begin
# Allocate scratch space, and initialize the Fourier transform.
call calloc (trig, 4*npix + 15, TY_REAL)
call cffti (npix, Memr[trig])
# Do complex transform in-place.
if (fwd) # forward transform
call cfftf (npix, xdata, Memr[trig])
else # backward transform
call cfftb (npix, xdata, Memr[trig])
call mfree (trig, TY_REAL)
end
Source Code · Search Form · STSDAS
Maintained by the Science Software Group at STScI
This file last updated on 24 Feb 2011