// ----------------------------------------------------------------------- // param.cc: Dealing with the set of parameters for Duchamp. // ----------------------------------------------------------------------- // Copyright (C) 2006, Matthew Whiting, ATNF // // 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. // // Duchamp 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 Duchamp; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA // // Correspondence concerning Duchamp may be directed to: // Internet email: Matthew.Whiting [at] atnf.csiro.au // Postal address: Dr. Matthew Whiting // Australia Telescope National Facility, CSIRO // PO Box 76 // Epping NSW 1710 // AUSTRALIA // ----------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace duchamp { /****************************************************************/ /////////////////////////////////////////////////// //// Accessor Functions for Parameter class: /////////////////////////////////////////////////// Param::~Param() { /** * Deletes the offsets array if the sizeOffsets parameter is * positive. */ if(this->sizeOffsets>0) delete [] this->offsets; } Param::Param() { this->defaultValues(); } void Param::defaultValues() { /** * Provides default intial values for the parameters. Note that * imageFile has no default value! */ std::string baseSection = "[*,*,*]"; // Input files this->imageFile = ""; this->flagSubsection = false; this->pixelSec.setSection(baseSection); this->flagReconExists = false; this->reconFile = ""; this->flagSmoothExists = false; this->smoothFile = ""; // Output files this->flagLog = false; this->logFile = "duchamp-Logfile.txt"; this->outFile = "duchamp-Results.txt"; this->flagSeparateHeader= false; this->headerFile = "duchamp-Results.hdr"; this->spectraFile = "duchamp-Spectra.ps"; this->flagOutputMask = false; this->flagOutputSmooth = false; this->flagOutputRecon = false; this->flagOutputResid = false; this->flagVOT = false; this->votFile = "duchamp-Results.xml"; this->flagKarma = false; this->karmaFile = "duchamp-Results.ann"; this->flagMaps = true; this->detectionMap = "duchamp-DetectionMap.ps"; this->momentMap = "duchamp-MomentMap.ps"; this->flagXOutput = true; // Cube related parameters this->flagBlankPix = true; this->blankPixValue = -8.00061; this->blankKeyword = 1; this->bscaleKeyword = -8.00061; this->bzeroKeyword = 0.; // Milky-Way parameters this->flagMW = false; this->maxMW = 112; this->minMW = 75; this->numPixBeam = 10.; this->flagUsingBeam = false; // Trim-related this->flagTrim = false; this->hasBeenTrimmed = false; this->borderLeft = 0; this->borderRight = 0; this->borderBottom = 0; this->borderTop = 0; // Subsection offsets this->sizeOffsets = 0; this->xSubOffset = 0; this->ySubOffset = 0; this->zSubOffset = 0; // Baseline related this->flagBaseline = false; // Detection-related this->flagNegative = false; // Object growth this->flagGrowth = false; this->growthCut = 2.; // FDR analysis this->flagFDR = false; this->alphaFDR = 0.01; // Other detection this->flagStatSec = false; this->statSec.setSection(baseSection); this->flagRobustStats = true; this->snrCut = 3.; this->threshold = 0.; this->flagUserThreshold = false; // Smoothing this->flagSmooth = false; this->smoothType = "spectral"; this->hanningWidth = 5; this->kernMaj = 3.; this->kernMin = -1.; this->kernPA = 0.; // A trous reconstruction parameters this->flagATrous = false; this->reconDim = 1; this->scaleMin = 1; this->scaleMax = 0; this->snrRecon = 4.; this->filterCode = 1; this->reconFilter.define(this->filterCode); // Volume-merging parameters this->flagAdjacent = true; this->threshSpatial = 3.; this->threshVelocity = 7.; this->minChannels = 3; this->minPix = 2; // Input-Output related this->spectralMethod = "peak"; this->spectralUnits = "km/s"; this->pixelCentre = "centroid"; this->borders = true; this->blankEdge = true; this->verbose = true; }; Param::Param (const Param& p) { operator=(p); } Param& Param::operator= (const Param& p) { if(this == &p) return *this; this->imageFile = p.imageFile; this->flagSubsection = p.flagSubsection; this->pixelSec = p.pixelSec; this->flagReconExists = p.flagReconExists; this->reconFile = p.reconFile; this->flagSmoothExists = p.flagSmoothExists; this->smoothFile = p.smoothFile; this->flagLog = p.flagLog; this->logFile = p.logFile; this->outFile = p.outFile; this->flagSeparateHeader= p.flagSeparateHeader; this->headerFile = p.headerFile; this->spectraFile = p.spectraFile; this->flagOutputMask = p.flagOutputMask; this->flagOutputSmooth = p.flagOutputSmooth; this->flagOutputRecon = p.flagOutputRecon; this->flagOutputResid = p.flagOutputResid; this->flagVOT = p.flagVOT; this->votFile = p.votFile; this->flagKarma = p.flagKarma; this->karmaFile = p.karmaFile; this->flagMaps = p.flagMaps; this->detectionMap = p.detectionMap; this->momentMap = p.momentMap; this->flagXOutput = p.flagXOutput; this->flagBlankPix = p.flagBlankPix; this->blankPixValue = p.blankPixValue; this->blankKeyword = p.blankKeyword; this->bscaleKeyword = p.bscaleKeyword; this->bzeroKeyword = p.bzeroKeyword; this->flagMW = p.flagMW; this->maxMW = p.maxMW; this->minMW = p.minMW; this->numPixBeam = p.numPixBeam; this->flagTrim = p.flagTrim; this->hasBeenTrimmed = p.hasBeenTrimmed; this->borderLeft = p.borderLeft; this->borderRight = p.borderRight; this->borderBottom = p.borderBottom; this->borderTop = p.borderTop; if(this->sizeOffsets>0) delete [] this->offsets; this->sizeOffsets = p.sizeOffsets; if(this->sizeOffsets>0){ this->offsets = new long[this->sizeOffsets]; for(int i=0;isizeOffsets;i++) this->offsets[i] = p.offsets[i]; } this->xSubOffset = p.xSubOffset; this->ySubOffset = p.ySubOffset; this->zSubOffset = p.zSubOffset; this->flagBaseline = p.flagBaseline; this->flagNegative = p.flagNegative; this->flagGrowth = p.flagGrowth; this->growthCut = p.growthCut; this->flagFDR = p.flagFDR; this->alphaFDR = p.alphaFDR; this->flagStatSec = p.flagStatSec; this->statSec = p.statSec; this->flagRobustStats = p.flagRobustStats; this->snrCut = p.snrCut; this->threshold = p.threshold; this->flagUserThreshold = p.flagUserThreshold; this->flagSmooth = p.flagSmooth; this->smoothType = p.smoothType; this->hanningWidth = p.hanningWidth; this->kernMaj = p.kernMaj; this->kernMin = p.kernMin; this->kernPA = p.kernPA; this->flagATrous = p.flagATrous; this->reconDim = p.reconDim; this->scaleMin = p.scaleMin; this->scaleMax = p.scaleMax; this->snrRecon = p.snrRecon; this->filterCode = p.filterCode; this->reconFilter = p.reconFilter; this->flagAdjacent = p.flagAdjacent; this->threshSpatial = p.threshSpatial; this->threshVelocity = p.threshVelocity; this->minChannels = p.minChannels; this->minPix = p.minPix; this->spectralMethod = p.spectralMethod; this->spectralUnits = p.spectralUnits; this->pixelCentre = p.pixelCentre; this->borders = p.borders; this->verbose = p.verbose; return *this; } //-------------------------------------------------------------------- int Param::getopts(int argc, char ** argv) { /** * A function that reads in the command-line options, in a manner * tailored for use with the main Duchamp program. * * \param argc The number of command line arguments. * \param argv The array of command line arguments. */ int returnValue = FAILURE; if(argc==1){ std::cout << ERR_USAGE_MSG; returnValue = FAILURE; } else { std::string file; bool changeX = false; this->defaultValues(); char c; while( ( c = getopt(argc,argv,"p:f:hvx") )!=-1){ switch(c) { case 'p': file = optarg; if(this->readParams(file)==FAILURE){ std::stringstream errmsg; errmsg << "Could not open parameter file " << file << ".\n"; duchampError("Duchamp",errmsg.str()); } else returnValue = SUCCESS; break; case 'f': file = optarg; this->imageFile = file; returnValue = SUCCESS; break; case 'v': std::cout << PROGNAME << " version " << VERSION << std::endl; break; case 'x': changeX = true; break; case 'h': default : std::cout << ERR_USAGE_MSG; break; } } if(changeX){ if(returnValue == SUCCESS) this->setFlagXOutput(false); else { duchampError("Duchamp", "You need to specify either a parameter file or FITS image.\n"); std::cout << "\n" << ERR_USAGE_MSG; } } } return returnValue; } //-------------------------------------------------------------------- bool Param::isBlank(float &value) { /** * Tests whether the value passed as the argument is BLANK or not. * \param value Pixel value to be tested. * \return False if flagBlankPix is false. Else, compare to the * relevant FITS keywords, using integer comparison. */ return this->flagBlankPix && (this->blankKeyword == int((value-this->bzeroKeyword)/this->bscaleKeyword)); }; bool *Param::makeBlankMask(float *array, int size) { /** * This returns an array of bools, saying whether each pixel in the * given array is BLANK or not. If the pixel is BLANK, set mask to * false, else set to true. The array is allocated by the function. */ bool *mask = new bool[size]; for(int i=0;iisBlank(array[i]); return mask; } bool Param::isInMW(int z) { /** * Tests whether we are flagging Milky Way channels, and if so * whether the given channel number is in the Milky Way range. The * channels are assumed to start at number 0. * \param z The channel number * \return True if we are flagging Milky Way channels and z is in * the range. */ return ( flagMW && (z>=minMW) && (z<=maxMW) ); } bool Param::isStatOK(int x, int y, int z) { /** * Test whether a given pixel position lies within the subsection * given by the statSec parameter. Only tested if the flagSubsection * parameter is true -- if it isn't, we just return true since all * pixels are therefore available for statstical calculations. * \param x X-value of pixel being tested. * \param y Y-value of pixel being tested. * \param z Z-value of pixel being tested. * \return True if pixel is able to be used for statistical * calculations. False otherwise. */ int xval=x,yval=y,zval=z; if(flagSubsection){ xval += pixelSec.getStart(0); yval += pixelSec.getStart(1); zval += pixelSec.getStart(2); } return !flagStatSec || statSec.isInside(xval,yval,zval); }; /****************************************************************/ /////////////////////////////////////////////////// //// Other Functions using the Parameter class: /////////////////////////////////////////////////// std::string makelower( std::string s ) { // "borrowed" from Matt Howlett's 'fred' std::string out = ""; for( int i=0; i> val; return val; } inline bool readFlag(std::stringstream& ss) { std::string val; ss >> val; return boolify(val); } inline float readFval(std::stringstream& ss) { float val; ss >> val; return val; } inline int readIval(std::stringstream& ss) { int val; ss >> val; return val; } int Param::readParams(std::string paramfile) { /** * The parameters are read in from a disk file, on the assumption that each * line of the file has the format "parameter value" (eg. alphafdr 0.1) * * The case of the parameter name does not matter, nor does the * formatting of the spaces (it can be any amount of whitespace or * tabs). * * \param paramfile A std::string containing the parameter filename. * * \return FAILURE if the parameter file does not exist. SUCCESS if * it is able to read it. */ std::ifstream fin(paramfile.c_str()); if(!fin.is_open()) return FAILURE; std::string line; while( !std::getline(fin,line,'\n').eof()){ if(line[0]!='#'){ std::stringstream ss; ss.str(line); std::string arg; ss >> arg; arg = makelower(arg); if(arg=="imagefile") this->imageFile = readSval(ss); if(arg=="flagsubsection") this->flagSubsection = readFlag(ss); if(arg=="subsection") this->pixelSec.setSection(readSval(ss)); if(arg=="flagreconexists") this->flagReconExists = readFlag(ss); if(arg=="reconfile") this->reconFile = readSval(ss); if(arg=="flagsmoothexists")this->flagSmoothExists = readFlag(ss); if(arg=="smoothfile") this->smoothFile = readSval(ss); if(arg=="flaglog") this->flagLog = readFlag(ss); if(arg=="logfile") this->logFile = readSval(ss); if(arg=="outfile") this->outFile = readSval(ss); if(arg=="flagseparateheader") this->flagSeparateHeader = readFlag(ss); if(arg=="headerfile") this->headerFile = readSval(ss); if(arg=="spectrafile") this->spectraFile = readSval(ss); if(arg=="flagoutputmask") this->flagOutputMask = readFlag(ss); if(arg=="flagoutputsmooth")this->flagOutputSmooth = readFlag(ss); if(arg=="flagoutputrecon") this->flagOutputRecon = readFlag(ss); if(arg=="flagoutputresid") this->flagOutputResid = readFlag(ss); if(arg=="flagvot") this->flagVOT = readFlag(ss); if(arg=="votfile") this->votFile = readSval(ss); if(arg=="flagkarma") this->flagKarma = readFlag(ss); if(arg=="karmafile") this->karmaFile = readSval(ss); if(arg=="flagmaps") this->flagMaps = readFlag(ss); if(arg=="detectionmap") this->detectionMap = readSval(ss); if(arg=="momentmap") this->momentMap = readSval(ss); if(arg=="flagxoutput") this->flagXOutput = readFlag(ss); if(arg=="flagnegative") this->flagNegative = readFlag(ss); if(arg=="flagtrim") this->flagTrim = readFlag(ss); if(arg=="flagmw") this->flagMW = readFlag(ss); if(arg=="maxmw") this->maxMW = readIval(ss); if(arg=="minmw") this->minMW = readIval(ss); if(arg=="beamsize") this->numPixBeam = readFval(ss); if(arg=="flagbaseline") this->flagBaseline = readFlag(ss); if(arg=="minpix") this->minPix = readIval(ss); if(arg=="flaggrowth") this->flagGrowth = readFlag(ss); if(arg=="growthcut") this->growthCut = readFval(ss); if(arg=="flagfdr") this->flagFDR = readFlag(ss); if(arg=="alphafdr") this->alphaFDR = readFval(ss); if(arg=="flagstatsec") this->flagStatSec = readFlag(ss); if(arg=="statsec") this->statSec.setSection(readSval(ss)); if(arg=="flagrobuststats") this->flagRobustStats = readFlag(ss); if(arg=="snrcut") this->snrCut = readFval(ss); if(arg=="threshold"){ this->threshold = readFval(ss); this->flagUserThreshold = true; } if(arg=="flagsmooth") this->flagSmooth = readFlag(ss); if(arg=="smoothtype") this->smoothType = readSval(ss); if(arg=="hanningwidth") this->hanningWidth = readIval(ss); if(arg=="kernmaj") this->kernMaj = readFval(ss); if(arg=="kernmin") this->kernMin = readFval(ss); if(arg=="kernpa") this->kernPA = readFval(ss); if(arg=="flagatrous") this->flagATrous = readFlag(ss); if(arg=="recondim") this->reconDim = readIval(ss); if(arg=="scalemin") this->scaleMin = readIval(ss); if(arg=="scalemax") this->scaleMax = readIval(ss); if(arg=="snrrecon") this->snrRecon = readFval(ss); if(arg=="filtercode"){ this->filterCode = readIval(ss); this->reconFilter.define(this->filterCode); } if(arg=="flagadjacent") this->flagAdjacent = readFlag(ss); if(arg=="threshspatial") this->threshSpatial = readFval(ss); if(arg=="threshvelocity") this->threshVelocity = readFval(ss); if(arg=="minchannels") this->minChannels = readIval(ss); if(arg=="spectralmethod") this->spectralMethod=makelower(readSval(ss)); if(arg=="spectralunits") this->spectralUnits = makelower(readSval(ss)); if(arg=="pixelcentre") this->pixelCentre = makelower(readSval(ss)); if(arg=="drawborders") this->borders = readFlag(ss); if(arg=="drawblankedges") this->blankEdge = readFlag(ss); if(arg=="verbose") this->verbose = readFlag(ss); // Dealing with deprecated parameters. if(arg=="flagblankpix"){ this->flagTrim = readFlag(ss); std::stringstream errmsg; errmsg <<"The parameter flagBlankPix is deprecated. " <<"Please use the flagTrim parameter in future.\n" <<"Setting flagTrim = " << stringize(this->flagTrim) << ".\n"; duchampWarning("Reading parameters",errmsg.str()); } if(arg=="blankpixvalue"){ std::stringstream errmsg; errmsg <<"The parameter blankPixValue is deprecated.\n" <<"This value is only taken from the FITS header.\n"; duchampWarning("Reading parameters",errmsg.str()); } } } // The wavelet reconstruction takes precendence over the smoothing. if(this->flagATrous) this->flagSmooth = false; // Make sure smoothType is an acceptable type -- default is "spectral" if((this->smoothType!="spectral")&& (this->smoothType!="spatial")){ std::stringstream errmsg; errmsg << "The requested value of the parameter smoothType, \"" << this->smoothType << "\" is invalid.\n" << "Changing to \"spectral\".\n"; duchampWarning("Reading parameters",errmsg.str()); this->smoothType = "spectral"; } // If kernMin has not been given, or is negative, make it equal to kernMaj if(this->kernMin < 0) this->kernMin = this->kernMaj; // Make sure spectralMethod is an acceptable type -- default is "peak" if((this->spectralMethod!="peak")&& (this->spectralMethod!="sum")){ std::stringstream errmsg; errmsg << "The requested value of the parameter spectralMethod, \"" << this->spectralMethod << "\" is invalid.\n" << "Changing to \"peak\".\n"; duchampWarning("Reading parameters",errmsg.str()); this->spectralMethod = "peak"; } // Make sure pixelCentre is an acceptable type -- default is "peak" if((this->pixelCentre!="centroid")&& (this->pixelCentre!="average") && (this->pixelCentre!="peak") ){ std::stringstream errmsg; errmsg << "The requested value of the parameter pixelCentre, \"" << this->pixelCentre << "\" is invalid.\n" << "Changing to \"centroid\".\n"; duchampWarning("Reading parameters",errmsg.str()); this->pixelCentre = "centroid"; } return SUCCESS; } std::ostream& operator<< ( std::ostream& theStream, Param& par) { /** * Print out the parameter set in a formatted, easy to read style. * Lists the parameters, a description of them, and their value. */ // Only show the [beamSize] bit if we are using the parameter // otherwise we have read it from the FITS header. std::string beamParam = ""; if(par.getFlagUsingBeam()) beamParam = "[beamSize]"; // BUG -- can get error: `boolalpha' is not a member of type `ios' -- old compilers: gcc 2.95.3? // theStream.setf(std::ios::boolalpha); theStream.setf(std::ios::left); theStream <<"\n---- Parameters ----"<blankKeyword = head.getBlankKeyword(); this->bscaleKeyword = head.getBscaleKeyword(); this->bzeroKeyword = head.getBzeroKeyword(); this->blankPixValue = this->blankKeyword * this->bscaleKeyword + this->bzeroKeyword; this->numPixBeam = head.getBeamSize(); } std::string Param::outputMaskFile() { /** * This function produces the required filename in which to save * the mask image, indicating which pixels have been detected as * part of an object. If the input image is image.fits, then the * output will be image.MASK.fits. */ std::string inputName = this->imageFile; std::stringstream ss; ss << inputName.substr(0,inputName.size()-5); // remove the ".fits" on the end. ss << ".MASK.fits"; return ss.str(); } std::string Param::outputSmoothFile() { /** * This function produces the required filename in which to save * the smoothed array. If the input image is image.fits, then * the output will be: *
  • Spectral smoothing: image.SMOOTH-1D-3.fits, where the * width of the Hanning filter was 3 pixels. *
  • Spatial smoothing : image.SMOOTH-2D-3-2-20.fits, where * kernMaj=3, kernMin=2 and kernPA=20 degrees. *
*/ std::string inputName = this->imageFile; std::stringstream ss; ss << inputName.substr(0,inputName.size()-5); // remove the ".fits" on the end. if(this->flagSubsection) ss<<".sub"; if(this->smoothType=="spectral") ss << ".SMOOTH-1D-" << this->hanningWidth << ".fits"; else if(this->smoothType=="spatial") ss << ".SMOOTH-2D-" << this->kernMaj << "-" << this->kernMin << "-" << this->kernPA << ".fits"; return ss.str(); } std::string Param::outputReconFile() { /** * This function produces the required filename in which to save * the reconstructed array. If the input image is image.fits, then * the output will be eg. image.RECON-3-2-4-1.fits, where the numbers are * 3=reconDim, 2=filterCode, 4=snrRecon, 1=minScale */ std::string inputName = this->imageFile; std::stringstream ss; // First we remove the ".fits" from the end of the filename. ss << inputName.substr(0,inputName.size()-5); if(this->flagSubsection) ss<<".sub"; ss << ".RECON-" << this->reconDim << "-" << this->filterCode << "-" << this->snrRecon << "-" << this->scaleMin << ".fits"; return ss.str(); } std::string Param::outputResidFile() { /** * This function produces the required filename in which to save * the reconstructed array. If the input image is image.fits, then * the output will be eg. image.RESID-3-2-4-1.fits, where the numbers are * 3=reconDim, 2=filterCode, 4=snrRecon, 1=scaleMin */ std::string inputName = this->imageFile; std::stringstream ss; // First we remove the ".fits" from the end of the filename. ss << inputName.substr(0,inputName.size()-5); if(this->flagSubsection) ss<<".sub"; ss << ".RESID-" << this->reconDim << "-" << this->filterCode << "-" << this->snrRecon << "-" << this->scaleMin << ".fits"; return ss.str(); } }