//# ImageMoments.h: generate moments from images //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: ImageMoments.h 20299 2008-04-03 05:56:44Z gervandiepen $ #ifndef IMAGES_IMAGEMOMENTS_H #define IMAGES_IMAGEMOMENTS_H #include <imageanalysis/ImageAnalysis/MomentsBase.h> #include <imageanalysis/ImageTypedefs.h> namespace casacore{ template <class T> class MaskedLattice; } namespace casa { class ImageMomentsProgressMonitor; // <summary> // This class generates moments from an image. // </summary> // <use visibility=export> // <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos=""> // </reviewed> // <prerequisite> // <li> <linkto class="casacore::ImageInterface">casacore::ImageInterface</linkto> // <li> <linkto class="MomentsBase">MomentsBase</linkto> // <li> <linkto class="casacore::LatticeApply">casacore::LatticeApply</linkto> // <li> <linkto class="MomentCalcBase">MomentCalcBase</linkto> // </prerequisite> // <etymology> // This class computes moments from images // </etymology> // <synopsis> // The primary goal of this class is to help spectral-line astronomers analyze // their multi-dimensional images by generating moments of a specified axis. // The word "moment" is used loosely here. It refers to collapsing an axis // to one pixel and putting the value of that pixel (for all of the other // non-collapsed axes) to something computed from the data values along // the moment axis. For example, take an RA-DEC-Velocity cube, collapse // the velocity axis by computing the mean intensity at each RA-DEC // pixel. This class offers many different moments and a variety of // interactive and automatic ways to compute them. // // This class only accepts images of type <src>casacore::Float</src> and <src>casacore::Double</src>. // This restriction is because of the plotting capabilities which are a // bit awkward for other types. // // This class makes a distinction between a "moment" and a "method". This // boundary is a little blurred, but it claims to refer to the distinction // of what you are computing, as to how the pixels that were included in the // computation were selected. For example, a "moment" would be the average value // of some pixels. A method for selecting those pixels would be a simple range // specifying a range for which pixels should be included. // // The default state of this class is to do nothing. If you specify an image via // the <src>setImage</src> function then invoking the <src>createMoments</src> // function will cause it to compute the integrated itensity along the // spectral axis of the image (if it can find one). You can change the // computational state of the class from this basic form via the remaining // <src>set</src> functions. You can call any number of these functions to // suit your needs. // // Because there are a wide variety of methods available, if you specify an // invalid combination, a table showing the available methods is listed. It // is reproduced below for convenience. // // The basic method is to just compute moments directly from the pixel intensities. // This can be modified by applying pixel intensity inclusion or exclusion ranges. // You can then also smooth the image and compute a mask based on the inclusion or // exclusion ranges applied to the smoothed image. This mask is then applied to // the unsmoothed data for moment computation. // // The window method does no pixel intensity range selection. Instead a spectral // window is found (hopefully surrounding the spectral line feature) and only the // pixels in that window are used for computation. This window can be found from the // smoothed or unsmoothed data. The moments are always computed from the unsmoothed // data. For each spectrum, the window can be found interactively or automatically. // There are two interactive methods. Either you just mark the window with the // cursor, or you interactively fit a Gaussian to the profile and the +/- 3-sigma // window is returned. There are two automatic methods. Either Bosma's converging // mean algorithm is used, or an automatically fit Gaussian +/- 3-sigma window // is returned. // // The fitting method fits Gaussians to spectral features either automatically // or interactively. The moments are then computed from the Gaussian fits // (not the data themselves). // // When an output image is created, it will have N-1 axes, where the input image // has N axes. In the output image, the physical axis corresponding to the moment // axis will have been removed, but the coordinate information will be retained // for future coordinate transformations. For example, if you have a RA-DEC-VELOCITY // image and you collapsed axis 2 (the DEC axis) the output images would be // RA-VELOCITY with the coordinate information retained for the DEC axis so that // the coupled nature of RA/DEC coordinates is preserved. // // Output images are created with an all true (good) mask. If, for a given // pixel, the moment calculation fails, then the mask is set to F. // // When making plots, the order in which the spectra are displayed is determined // by the tiling sequence of the image (for optimum speed of access). // // // <srcblock> // Allowed Methods // --------------- // // casacore::Smooth Window Fit in/exclude Interactive // ----------------------------------------------------- // N N N N N // Y/N N N Y N // // Y/N Y N N Y // Y/N Y N N N // Y/N Y Y N Y/N // // N N Y N Y/N // ---------------------------------------------------- // </srcblock> // // <note role=caution> // Note that the <src>MEDIAN_COORDINATE</src> moment is not very robust. // It is very useful for generating quickly a velocity field in a way // that is not sensitive to noise. However, it will only give sensible // results under certain conditions. It treats the spectrum as a // probability distribution, generates the cumulative distribution for // the selected pixels (via an <src>include</src> or <src>exclude</src> // pixel range, and finds the (interpolated) coordinate coresponding to // the 50% value. The generation of the cumulative distribution and the // finding of the 50% level really only makes sense if the cumulative // distribution is monotonically increasing. This essentially means only // selecting pixels which are positive or negative. For this reason, this // moment type is *only* supported with the basic method (i.e. no smoothing, // no windowing, no fitting) with a pixel selection range that is either // all positive, or all negative. // </note> // // <note role=tip> // If you ignore return error statuses from the functions that set the // state of the class, the internal status of the class is set to bad. // This means it will just keep on returning error conditions until you // explicitly recover the situation. A message describing the last // error condition can be recovered with function errorMessage. // </note> // </synopsis> // <example> // <srcBlock> //// Set state function argument values // // casacore::Vector<casacore::Int> moments(2); // moments(0) = ImageMoments<casacore::Float>::AVERAGE; // moments(1) = ImageMoments<casacore::Float>::WEIGHTED_MEAN_COORDINATE; // casacore::Vector<int> methods(2); // methods(0) = ImageMoments<casacore::Float>::WINDOW; // methods(1) = ImageMoments<casacore::Float>::INTERACTIVE; // casacore::Vector<casacore::Int> nxy(2); // nxy(0) = 3; // nxy(1) = 3; // //// Open paged image // // casacore::PagedImage<casacore::Float> inImage(inName); // //// Construct moment helper object // // casacore::LogOrigin or("myClass", "myFunction(...)", WHERE); // casacore::LogIO os(or); // ImageMoments<casacore::Float> moment(casacore::SubImage<casacore::Float>(inName), os); // //// Specify state via control functions // // if (!moment.setMoments(moments)) return 1; // if (!moment.setWinFitMethod(methods)) return 1; // if (!moment.setMomentAxis(3)) return 1; // if (!moment.setPlotting("/xs", nxy)) return 1; // //// Create the moments // // if (!moment.createMoments()) return 1; // </srcBlock> // In this example, we generate two moments (average intensity and intensity // weighted mean coordinate -- usually the velocity field) of axis 3 by the // interactive window method. The interactive plotting is done on the // device called <src>/xs</src> (no longer supported). We put 9 subplots on each page. The output // file names are constructed by the class from the input file name plus some // internally generated suffixes. // </example> // <motivation> // This is a fundamental and traditional piece of spectral-line image analysis. // </motivation> // <todo asof="1996/11/26"> // <li> more control over histogram of image noise at start (pixel // range and number of bins) // <li> better algorithm for seeing if spectrum is pure noise // <li> Make this class extensible so users could add their own method. // </todo> template <class T> class ImageMoments : public MomentsBase<T> { public: // Note that if I don't put MomentCalcBase as a forward declaration // and use instead "friend class MomentCalcBase<T>" The gnu compiler // fails with a typedef problem. But I can't solve it with say // <src>typedef MomentCalcBase<T> gpp_type;</src> because you can only do a // typedef with an actual type, not a <tt>T</tt> ! friend class MomentCalcBase<T>; ImageMoments() = delete; // Constructor takes an image and a <src>casacore::LogIO</src> object for logging purposes. // You specify whether output images are automatically overwritten if pre-existing, // or whether an intercative choice dialog widget appears (overWriteOutput=F) // You may also determine whether a progress meter is displayed or not. ImageMoments ( const casacore::ImageInterface<T>& image, casacore::LogIO &os, casacore::Bool overWriteOutput=false, casacore::Bool showProgress=true ); ImageMoments(const ImageMoments<T> &other) = delete; // Destructor ~ImageMoments(); ImageMoments<T> &operator=(const ImageMoments<T> &other) = delete; // Set the moment axis (0 relative). A return value of <src>false</src> // indicates that the axis was not contained in the image. If you don't // call this function, the default state of the class is to set the // moment axis to the spectral axis if it can find one. Otherwise // an error will result. casacore::Bool setMomentAxis (casacore::Int momentAxis); // This function invokes smoothing of the input image. Give <src>casacore::Int</src> // arrays for the axes (0 relative) to be smoothed and the smoothing kernel // types (use the <src>enum KernelTypes</src>) for each axis. Give a // <src>casacore::Double</src> array for the widths (full width for BOXCAR and full // width at half maximum for GAUSSIAN) in pixels of the smoothing kernels for // each axis. For HANNING smoothing, you always get the quarter-half-quarter // kernel (no matter what you might ask for). A return value of <src>false</src> // indicates that you have given an inconsistent or invalid set of smoothing // parameters. If you don't call this function the default state of the // class is to do no smoothing. The kernel types are specified with // the casacore::VectorKernel::KernelTypes enum casacore::Bool setSmoothMethod( const casacore::Vector<casacore::Int>& smoothAxes, const casacore::Vector<casacore::Int>& kernelTypes, const casacore::Vector<casacore::Quantum<casacore::Double> >& kernelWidths ); casacore::Bool setSmoothMethod( const casacore::Vector<casacore::Int>& smoothAxes, const casacore::Vector<casacore::Int>& kernelTypes, const casacore::Vector<casacore::Double>& kernelWidths ); // This is the function that does all the computational work. It should be called // after the <src>set</src> functions. // If the axis being collapsed comes from a coordinate with one axis only, // the axis and its coordinate are physically removed from the output image. Otherwise, // if <src>removeAxes=true</src> then the output axis is logically removed from the // the output CoordinateSystem. If <src>removeAxes=false</src> then the axis // is retained with shape=1 and with its original coordinate information (which // is probably meaningless). // // The output vector will hold PagedImages or TempImages (doTemp=true). // If doTemp is true, the outFileName is not used. // // If you create PagedImages, outFileName is the root name for // the output files. Suffixes will be made up internally to append // to this root. If you only ask for one moment, // this will be the actual name of the output file. If you don't set this // variable, the default state of the class is to set the output name root to // the name of the input file. std::vector<std::shared_ptr<casacore::MaskedLattice<T> > > createMoments( casacore::Bool doTemp, const casacore::String& outFileName, casacore::Bool removeAxes=true ); // Set a new image. A return value of <src>false</src> indicates the // image had an invalid type (this class only accepts casacore::Float or casacore::Double images). casacore::Bool setNewImage (const casacore::ImageInterface<T>& image); // Get CoordinateSystem const casacore::CoordinateSystem& coordinates() {return _image->coordinates();}; // Get shape casacore::IPosition getShape() const { return _image->shape(); } //Set an ImageMomentsProgressMonitor interested in getting updates on the //progress of the collapse process. void setProgressMonitor(ImageMomentsProgressMonitor* progressMonitor); private: SPCIIT _image = SPCIIT(nullptr); ImageMomentsProgressMonitor* _progressMonitor = nullptr; // casacore::Smooth an image SPIIT _smoothImage(); // Determine the noise by fitting a Gaussian to a histogram // of the entire image above the 25% levels. If a plotting // device is set, the user can interact with this process. void _whatIsTheNoise (T& noise, const casacore::ImageInterface<T>& image); protected: using MomentsBase<T>::os_p; using MomentsBase<T>::showProgress_p; using MomentsBase<T>::momentAxisDefault_p; using MomentsBase<T>::peakSNR_p; using MomentsBase<T>::stdDeviation_p; using MomentsBase<T>::yMin_p; using MomentsBase<T>::yMax_p; using MomentsBase<T>::out_p; using MomentsBase<T>::smoothOut_p; using MomentsBase<T>::goodParameterStatus_p; using MomentsBase<T>::doWindow_p; using MomentsBase<T>::doFit_p; using MomentsBase<T>::doSmooth_p; using MomentsBase<T>::noInclude_p; using MomentsBase<T>::noExclude_p; using MomentsBase<T>::fixedYLimits_p; using MomentsBase<T>::momentAxis_p; using MomentsBase<T>::worldMomentAxis_p; using MomentsBase<T>::kernelTypes_p; using MomentsBase<T>::kernelWidths_p; using MomentsBase<T>::moments_p; using MomentsBase<T>::selectRange_p; using MomentsBase<T>::smoothAxes_p; using MomentsBase<T>::overWriteOutput_p; using MomentsBase<T>::error_p; using MomentsBase<T>::convertToVelocity_p; using MomentsBase<T>::velocityType_p; using MomentsBase<T>::_checkMethod; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include <imageanalysis/ImageAnalysis/ImageMoments.tcc> #endif #endif