//# ImageDecomposer.h: decompose images into components //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002 //# 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: ImageDecomposer.h 20739 2009-09-29 01:15:15Z Malte.Marquarding $ #ifndef IMAGES_IMAGEDECOMPOSER_H #define IMAGES_IMAGEDECOMPOSER_H #include <iostream> #include <cmath> #include <casacore/casa/aips.h> #include <casacore/casa/Arrays/Vector.h> #include <casacore/casa/Arrays/Matrix.h> #include <casacore/casa/Containers/Block.h> #include <casacore/scimath/Functionals/Function1D.h> #include <casacore/lattices/Lattices/TempLattice.h> #include <casacore/lattices/Lattices/SubLattice.h> #include <casacore/images/Images/ImageInterface.h> namespace casa { //# NAMESPACE CASA - BEGIN // <summary> // A tool to separate a complex image into individual components. // </summary> // // <use visibility=export> // // <reviewed reviewer="" date="" tests="tImageDecomposer.cc"> // </reviewed> // // <prerequisite> // <li> <linkto class=casacore::CoordinateSystem>CoordinateSystem</linkto> // <li> <linkto class=casacore::ImageInterface>ImageInterface</linkto> // </prerequisite> // <etymology> // It takes an image, and separates it into components. // </etymology> // <synopsis> // ImageDecomposer is an image decomposition tool that performs several tasks, // with the end result being that a strongly blended image is separated into // components - both in the sense that it determines the parameters for each // component (assuming a Gaussian model) and that it physically assigns each // pixel in the image to an individual object. The products of these two // operations are called the component list and the component map, // respectively. The fitting process (which determines the component list) and // the pixel-decomposition process (which determines the component map) are // designed to work cooperatively to increase the efficiency and accuracy of // both, though each can operate without the other if necessary. // // The algorithm between the decomposition is based on the function clfind // described in Williams et al 1994, which uses a contouring procedure whereby // a closed contour designates a separate component. The program first // separates the image into clearly distint 'regions' of blended emission, then // contours each region to determine the areas constituting each component and // passes this information on to the fitter, which determines the component // list. // // The software is compatible with 2 and 3 dimensional images, but is not // yet structured for higher dimensions. // </synopsis> // <example> // <srcblock> // casacore::TempImage<casacore::Double> image; // //(populate the image with data: see dImageDecomposer.cc) // ImageDecomposer<casacore::Double> id(image); // id.setDeblendOptions(0.3, 8); // id.setFitOptions(0.4); // id.decomposeImage(); // id.display(); // id.printComponents(); // </srcblock> // </example> // // <motivation> // </motivation> // // <note> // </note> // // <todo asof="2002/07/23"> // <li> Generalize dimensionality // <li> Use casacore::Lattice iterators in place of casacore::IPosition loops wherever possible // <li> Speed up fitting by not sending every region pixel to the fitter // <li> Send the completed componentmap to the user as an AIPS++ (casacore::Int?) Image // <li> Return a ComponentList instead of a Matrix // <li> Enable custom contouring at user level // <li> Add progress meter // <li> Numerous other improvements to make are documented in the code // </todo> template <class T> class ImageDecomposer { public: // 'Special' flag values for pixels in the component map. An indeterminate // pixel lies directly between two components and cannot be immediately // assigned. A masked pixel is not inside the targeted region of the // sub-componentmap and is not used in decomposition or fitting. enum componentValues { INDETERMINATE = -1, MASKED = -2 }; ImageDecomposer() = delete; ImageDecomposer(const casacore::ImageInterface<T>& image); ImageDecomposer(const ImageDecomposer<T>& other); ImageDecomposer<T> &operator=(const ImageDecomposer<T> &other) = delete; ~ImageDecomposer(); // Tell the decomposer what image to decompose ("target image"). // Also resets the internal component map. // void setImage (casacore::ImageInterface<T>& image); // Tells the program whether or not to use the contour-based deblender. If not, // the program will instead perform a single thresholding followed by a // local maximum scan before fitting. void setDeblend(casacore::Bool deblendIt=true); // Specifies deblending options: // <ul> // <li> thresholdVal: noise cutoff level, used to distinguish source pixels // from background pixels. Also, regions which are not blended above this // value will be fit separately. // <li> nContour: number of total contours to use in deblending regions. // <li> minRange: the minimum number of contours necessary to distinguish // an object as a separate component. // <li> nAxis: paramater used to define whether or not two adjacent blocks // of pixels are contiguous - see identifyRegions for details. // </ul> // See decomposeImage for more information on the deblending process. void setDeblendOptions(T thresholdVal=0.1, casacore::uInt nContour=11, casacore::Int minRange=2, casacore::Int nAxis=2); // Tells the program whether or not to perform fitting. If not, the component // list will be dstermined by estimation from the values of the first and // second order moments of each component. void setFit(casacore::Bool fitIt=true); // Specifies fitting options: // <ul> // <li> maximumRMS: The maximum RMS residual value (after fitting) allowed to // identify a fit as successful. // <li> maxRetries: the maximum number of times the fit will be restarted in // order to try to reach a successful result (convergent with // RMS < maximumRMS). The default value of -1 tells the program // to calculate a reasonable value automatically based on the complexity // of the image. // <li> maxIter: maximum number of iterations to spend on each fit. // <li> convCriteria: criterion to establish convergence: see NonLinearFitLM. // </ul> // Additional information on these parameters can be found in FitGaussian. void setFitOptions(T maximumRMS=0.1, casacore::Int maxRetries=-1, casacore::uInt maxIter=256, T convCriteria=0.0001); // The primary method of this class - executes the instructions stated in the // options above by deblending and/or fitting to the image to generate // the component map and/or component list. void decomposeImage(); // Returns the number of regions found in the image. A 'region' as defined // in this code is a subset of the image of contiguous pixels whose values // are greater than the threshold value specified in decomposeImage. A region // may contain one or more components. casacore::uInt numRegions() const; // Returns the number of components found in the image. A 'component' as // defined in this code is a source that can be described as a single Gaussian. // This can only be determined after deblending. casacore::uInt numComponents() const; // Returns the shape of the component map. casacore::IPosition shape() const; // Returns the length of a specific axis. casacore::Int shape(casacore::uInt axis) const; // Returns true if the image has been thresholded (split up into regions.) casacore::Bool isDerived() const; // Returns true if the image has been decomposed (split up into components.) casacore::Bool isDecomposed() const; // Returns the component parameters as a Matrix. (Ideally, this should be // a ComponentList.) casacore::Matrix<T> componentList() const; // Currently does nothing; in the future should return the component map // in a way that it can be seen by the user in AIPS++, preferably as a // colorized image. void componentMap() const; // Command-line text output functions. // <group> void display() const; void displayContourMap(const casacore::Vector<T>& clevels) const; void printComponents() const; // </group> // Boxes each region in the componentmap: // blc is set to the lowest coordinate value in each region; // trc is set to one above the highest coordinate value in each region. void boundRegions(casacore::Block<casacore::IPosition>& blc, casacore::Block<casacore::IPosition>& trc); private: casacore::ImageInterface<T> *itsImagePtr;// Points to the target image. casacore::Lattice<casacore::Int> *itsMapPtr; // The actual component map. casacore::IPosition itsShape; // Component map shape casacore::uInt itsDim; // Component map number of dimensions casacore::uInt itsNRegions; // Number of distinct regions in component map casacore::uInt itsNComponents; // Number of components that have been fitted casacore::Matrix<T> itsList; // The component list (Gaussian parameters for // each component.) casacore::Bool itsDeblendIt; T itsThresholdVal; casacore::uInt itsNContour; // Decomposition options casacore::Int itsMinRange; // IMPR: maybe use a struct? casacore::Int itsNAxis; casacore::Bool itsFitIt; T itsMaximumRMS; casacore::Int itsMaxRetries; // Fitting options casacore::uInt itsMaxIter; T itsConvCriteria; void copyOptions(const ImageDecomposer<T>& other); // Makes sure a pair of IPositions is in the correct format for blc/trc, and // corrects them if they are not. void correctBlcTrc(casacore::IPosition& blc, casacore::IPosition& trc) const; // Used as an N-dimensional interator. This should probably be replaced by // LatticeIterators...? // <group> casacore::Bool increment(casacore::IPosition& pos, const casacore::IPosition& shape) const; void decrement(casacore::IPosition& pos) const; // </group> // Returns the component to which the specified cell belongs // <group> casacore::Int getCell(casacore::Int x, casacore::Int y) const; casacore::Int getCell(casacore::Int x, casacore::Int y, casacore::Int z) const; casacore::Int getCell(const casacore::IPosition& coord) const; // </group> // Assigns the specified cell to the specified component // <group> void setCell(casacore::Int x, casacore::Int y, casacore::Int sval); void setCell(casacore::Int x, casacore::Int y, casacore::Int z, casacore::Int sval); void setCell(const casacore::IPosition& coord, casacore::Int sval); // </group> // Semi-automatic way to set contour levels: at the given increment counting // between mincon and maxcon. casacore::Vector<T> autoContour(T minCon, T maxCon, T inc) const; // Linearly spaces contours between minvalue and just below the // maximum value in the target region of the target image, and returns // the contour values as a Vector. casacore::Vector<T> autoContour(casacore::Int nContours=11, T minValue=0) const; // Nonlinear spacing option for contouring; spaces contours according to the // function given. The domain of the function is 0 <-> ncontours-1; the // range is automatically calibrated to be minvalue <-> maxvalue. The function // should be nondecreasing in the domain such that each contour is greater // than the last. casacore::Vector<T> autoContour(const casacore::Function1D<T>& fn, casacore::Int nContours = 11, T minValue = 0) const; //Eliminates any regions whose corresponding values in killRegion are true // by setting all pixel values in the componentmap set to that region to // zero. Zero-oriented; there is an offset of one between the index in // killRegion and the actual region in the componentmap. void destroyRegions(const casacore::Vector<casacore::Bool>& killRegion); // Eliminates regions with no cells by replacing them with higher-numbered // regions. void renumberRegions(); // Overlays a smaller map onto an empty region of a larger map, // and adds submap component list to main component list. // The user should exercise caution with this function and synthesize submaps // only into regions of the main map that are truly empty (0), as no blending // is assumed between different maps. void synthesize(const ImageDecomposer<T>& subdecomposer, casacore::IPosition blc); // Set all elements in the component map to zero and clear the component list. void zero(); // Set all nonmasked elements in the component map to zero and clear the // component list. void clear(); // Finds the greatest value inside the specified rectangular area of the // target image. // <group> T findAreaGlobalMax(casacore::IPosition blc, casacore::IPosition trc) const; void findAreaGlobalMax(T& maxval, casacore::IPosition& maxvalpos, casacore::IPosition blc, casacore::IPosition trc) const; casacore::Vector<T> findAreaGlobalMax(casacore::IPosition blc, casacore::IPosition trc, casacore::Int naxis) const; void findAreaGlobalMax(casacore::Vector<T>& maxvals, casacore::Block<casacore::IPosition>& maxvalpos, casacore::IPosition blc, casacore::IPosition trc, casacore::Int naxis) const; // </group> // Finds all local maxima inside the specified rectangular area of the // target image. // <group> casacore::Vector<T> findAreaLocalMax(casacore::IPosition blc, casacore::IPosition trc, casacore::Int naxis) const; void findAreaLocalMax(casacore::Vector<T>& maxvals,casacore::Block<casacore::IPosition>& maxvalpos, casacore::IPosition blc, casacore::IPosition trc, casacore::Int naxis) const; // </group> // Finds the maximum value of the target image in each region of the // componentmap. // <group> casacore::Vector<T> findAllRegionGlobalMax() const; void findAllRegionGlobalMax(casacore::Vector<T>& maxvals, casacore::Block<casacore::IPosition>& maxvalpos) const; // </group> // Finds all local maxima of the target image inside the specifed region // of the componentmap. // <group> casacore::Vector<T> findRegionLocalMax(casacore::Int nregion, casacore::Int naxis) const; void findRegionLocalMax(casacore::Vector<T>& maxvals, casacore::Block<casacore::IPosition>& maxvalpos, casacore::Int nregion, casacore::Int naxis) const; // </group> //Compares specified pixel to adjacent pixels to determine if it is //greatest in local pixel block. //2D: //naxis = 1: compare to 4 adjacent pixels (axes only) //naxis = 2: compare to 8 adjacent pixels (axes and diagonals) //3D: //naxis = 1: compare to 6 adjacent pixels (axes only) //naxis = 2: compare to 18 adjacent pixels (axes and 2-axis diagonals) //naxis = 3: compare to 26 adjacent pixels (axes and 2/3-axis diagonals) // <group> casacore::Bool isLocalMax(const casacore::IPosition& pos, casacore::Int naxis) const; casacore::Bool isLocalMax(casacore::Int x, casacore::Int y, casacore::Int naxis) const; casacore::Bool isLocalMax(casacore::Int x, casacore::Int y, casacore::Int z, casacore::Int naxis) const; // </group> // Finds a rough estimate of the width of each component by scanning to find // the full width at quarter maximum. // Requires the location of each component. // This function is mostly obsolete, and is only used when the contour // deblender is off (since the component map is necessary to determine the // moments). void estimateComponentWidths(casacore::Matrix<T>& width, const casacore::Block<casacore::IPosition>& maxvalpos) const; // Calculates the 0th-2nd order moments of a region. casacore::Array<T> calculateMoments(casacore::Int region) const; // Performs a single threshold scan on the image. In other words, // identifies all contigous blocks of pixels in the target image above the // threshold value thrval, assigning each unique block to an integer, // starting at one. All pixels with target image values below thrval are set // to zero. casacore::uInt identifyRegions(T thrval, casacore::Int naxis=2); // Performs the contour decomposition on a blended image to generate a // component map that can detect components blended above any threshold(s), // by performing threshold scans at each contour level and recognizing // as individual any components that are distinct above any such level. void deblendRegions(const casacore::Vector<T>& contours, casacore::Int minRange=1, casacore::Int naxis=2); // Retrieves the target image's value at the given location. // <group> T getImageVal(casacore::IPosition coord) const; T getImageVal(casacore::Int x, casacore::Int y) const; T getImageVal(casacore::Int x, casacore::Int y, casacore::Int z) const; // </group> // Retrieves the number of the highest contour with a value less then the // target image's value at the given location. // <group> casacore::Int getContourVal(casacore::IPosition coord, const casacore::Vector<T>& clevels) const; casacore::Int getContourVal(casacore::Int x, casacore::Int y, casacore::Int z, const casacore::Vector<T>& clevels) const; casacore::Int getContourVal(casacore::Int x, casacore::Int y, const casacore::Vector<T>& clevels) const; casacore::Int getContourVal(T val, const casacore::Vector<T>& clevels) const; // </group> // Fits multiple gaussians to a single region. First performs a local // maximum scan to estimate the number of components in the region. casacore::Matrix<T> fitRegion(casacore::Int region); // Fits gaussians to an image; multiple gaussians per region in the component // map. The regions are fit sequentially and independently, so this function // can be used on the main image. If the map is not yet thresholded, will fit // to the entire image as if it were a single composite object, which will be // very slow. void fitRegions(); // Fits gaussians to an image; one gaussian per region in the pmap. // This function is intended to be used only by ImageDecomposer on its // intermediary subimages; using it at higher level will execute a full // gaussian fit on the main image and will be extremely slow. Every // nonflagged object pixel in the image is used in fitting. // If the deblended flag is true, the function will treat each region as // an individual component and will fit that many gaussians to the image void fitComponents(); // Estimate the component parameters based on moments calculated using // the component map. casacore::Matrix<T> estimateComponents(); // Fits the specified number of 3D gaussians to the data, and returns // solution in image (world) coordinates. Essentially just an interface // for FitGaussian. // <group> casacore::Matrix<T> fitGauss(const casacore::Matrix<T>& positions, const casacore::Vector<T>& dataValues, const casacore::Matrix<T>& initestimate) const; // </group> }; } //# NAMESPACE CASA - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include <imageanalysis/ImageAnalysis/ImageDecomposer.tcc> #endif //# CASACORE_NO_AUTO_TEMPLATES #endif