LCOV - code coverage report
Current view: top level - imageanalysis/ImageAnalysis - ImageFit1D.tcc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 112 169 66.3 %
Date: 2024-12-11 20:54:31 Functions: 17 19 89.5 %

          Line data    Source code
       1             : //# ImageFit1D.cc: Class to fit Spectral components to vectors in an image
       2             : //# Copyright (C) 2004
       3             : //# Associated Universities, Inc. Washington DC, USA.
       4             : //#
       5             : //# This library is free software; you can redistribute it and/or modify it
       6             : //# under the terms of the GNU Library General Public License as published by
       7             : //# the Free Software Foundation; either version 2 of the License, or (at your
       8             : //# option) any later version.
       9             : //#
      10             : //# This library is distributed in the hope that it will be useful, but WITHOUT
      11             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
      13             : //# License for more details.
      14             : //#
      15             : //# You should have received a copy of the GNU Library General Public License
      16             : //# along with this library; if not, write to the Free Software Foundation,
      17             : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      18             : //#
      19             : //# Correspondence concerning AIPS++ should be addressed as follows:
      20             : //#        Internet email: casa-feedback@nrao.edu.
      21             : //#        Postal address: AIPS++ Project Office
      22             : //#                        National Radio Astronomy Observatory
      23             : //#                        520 Edgemont Road
      24             : //#                        Charlottesville, VA 22903-2475 USA
      25             : //#
      26             : //#   $Id: ImageFit1D.tcc 20652 2009-07-06 05:04:32Z Malte.Marquarding $
      27             : 
      28             : #include <imageanalysis/ImageAnalysis/ImageFit1D.h>
      29             : 
      30             : #include <casacore/casa/Arrays/ArrayMath.h>
      31             : #include <casacore/casa/Exceptions/Error.h>
      32             : #include <casacore/coordinates/Coordinates/CoordinateUtil.h>
      33             : #include <casacore/coordinates/Coordinates/CoordinateSystem.h>
      34             : #include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
      35             : #include <casacore/images/Images/ImageInterface.h>
      36             : #include <casacore/images/Images/SubImage.h>
      37             : #include <casacore/images/Regions/ImageRegion.h>
      38             : #include <casacore/lattices/Lattices/LatticeUtilities.h>
      39             : #include <casacore/lattices/LatticeMath/LatticeMathUtil.h>
      40             : #include <components/SpectralComponents/SpectralEstimate.h>
      41             : #include <components/SpectralComponents/SpectralElement.h>
      42             : #include <casacore/casa/Utilities/Assert.h>
      43             : 
      44             : #include <memory>
      45             : 
      46             : namespace casa {
      47             : 
      48             : /*
      49             : template <class T> 
      50             : ImageFit1D<T>::ImageFit1D()
      51             :  : _image(0),
      52             :    _weights(0),
      53             :    _axis(0),
      54             :    _converged(false), _success(false), _isValid(true), _x(0)
      55             : {
      56             :    checkType();
      57             : }
      58             : */
      59             : 
      60             : template <class T> 
      61          38 : ImageFit1D<T>::ImageFit1D(
      62             :         std::shared_ptr<const casacore::ImageInterface<T> > image, casacore::uInt pixelAxis
      63          38 : ) : _image(image), _weights(), _axis(pixelAxis),
      64          38 :         _converged(false), _success(false), _isValid(true)
      65             : {
      66             :    //checkType();
      67             :    //setImage(image, pixelAxis);
      68          38 :         _construct();
      69          38 : }
      70             : 
      71             : template <class T> 
      72          48 : ImageFit1D<T>::ImageFit1D(
      73             :         std::shared_ptr<const casacore::ImageInterface<T> > image,
      74             :         std::shared_ptr<const casacore::ImageInterface<T> > weights,
      75             :         casacore::uInt pixelAxis
      76          48 : ) : _image(image), _weights(weights),
      77          48 :    _axis(pixelAxis), _converged(false), _success(false), _isValid(true)
      78             : {
      79             :    //checkType();
      80             :    //setImage(image, pixelAxis);
      81             :    // setWeightsImage (weights);
      82          48 :         _construct();
      83          48 : }
      84             : 
      85             : 
      86             : template <class T> 
      87             : ImageFit1D<T>::ImageFit1D(const ImageFit1D<T>& other)
      88             :  : _image(other._image), _weights(other._weights),
      89             :    _axis(other._axis), _converged(false), _success(false), _isValid(true)
      90             : {
      91             :    //checkType();
      92             :    copy(other);
      93             : }
      94             : 
      95             : template <class T> 
      96             : ImageFit1D<T>& ImageFit1D<T>::operator=(const ImageFit1D<T>& other)
      97             : {
      98             :   if (this != &other) {
      99             :      copy(other);
     100             :   }
     101             :   return *this;
     102             : }
     103             : 
     104          86 : template <class T> ImageFit1D<T>::~ImageFit1D() {}
     105             : 
     106             : template <class T> 
     107          86 : void ImageFit1D<T>::_construct() {
     108          86 :         checkType();
     109          86 :         _resetFitter();
     110          86 :    AlwaysAssert(_axis < _image->ndim(), casacore::AipsError);
     111             : 
     112          86 :    if (_weights) {
     113          48 :            AlwaysAssert (_image->shape().isEqual(_weights->shape()), casacore::AipsError);
     114             :    }
     115             :    else {
     116          38 :            _unityWeights.resize(_image->shape()[_axis], false);
     117          38 :            _unityWeights = 1.0;
     118             :    }
     119          86 :    _weightSlice.resize(_image->shape()[_axis], false);
     120          86 :    _sliceShape = casacore::IPosition(_image->ndim(), 1);
     121          86 :    _sliceShape[_axis] = _image->shape()[_axis];
     122          86 : }
     123             : 
     124             : /*
     125             : template <class T> 
     126             : void ImageFit1D<T>::setImage (const casacore::ImageInterface<T>& image,
     127             :                               const casacore::ImageInterface<T>& weights,
     128             :                               casacore::uInt pixelAxis)
     129             : {
     130             :         _resetFitter();
     131             :    setImage(image, pixelAxis);
     132             :    setWeightsImage(weights);
     133             : }
     134             : */
     135             : 
     136      545684 : template <class T>  void ImageFit1D<T>::setData (
     137             :         const casacore::IPosition& pos,
     138             :         /*const ImageFit1D<T>::AbcissaType abcissaType,
     139             :         const casacore::Bool doAbs, const casacore::Double* const &abscissaDivisor,
     140             :     casacore::Array<casacore::Double> (*xfunc)(const casacore::Array<casacore::Double>&), */
     141             :     casacore::Array<FitterType> (*yfunc)(const casacore::Array<FitterType>&)
     142             : ) {
     143      545684 :         _resetFitter();
     144             :         /*
     145             :         const casacore::uInt nDim = _image->ndim();
     146             :         casacore::IPosition start(nDim);
     147             :         start(_axis) = 0;
     148             :         for (casacore::uInt i=0; i<nDim; i++) {
     149             :                 if (i!=_axis) {
     150             :                         start(i) = pos(i);
     151             :                 }
     152             :         }
     153             :         */
     154      545684 :         casacore::IPosition start = pos;
     155      545684 :         start[_axis] = 0;
     156             :         // Get ordinate data
     157             : 
     158      545684 :         casacore::Vector<T> y;
     159      545684 :         y = _image->getSlice(start, _sliceShape, true);
     160             : 
     161             :         // Mask
     162             : 
     163      545684 :         casacore::Vector<casacore::Bool> mask;
     164      545684 :         mask = _image->getMaskSlice(start, _sliceShape, true);
     165             : 
     166             :         // Weights
     167             : 
     168      545684 :         if (_weights.get()) {
     169        1944 :                 convertArray(_weightSlice, _weights->getSlice(start, _sliceShape, true));
     170             :         }
     171             :         else {
     172      543740 :                 _weightSlice = _unityWeights;
     173             :         }
     174             :         // Set data in fitter; we need to use a casacore::Double fitter at present
     175      545684 :         casacore::Vector<FitterType> y2(y.shape());
     176      545684 :         convertArray(y2, y);
     177      545684 :         if (yfunc) {
     178          43 :                 y2 = (*yfunc)(y2);
     179             :                 // in some cases, the supplied function will return NAN values, eg
     180             :                 // log(y) will return NAN for nonpositive y values. Just mask those.
     181          43 :                 mask = mask && ! isNaN(y2);
     182             :         }
     183      545684 :         ThrowIf(
     184             :                 !_fitter.setData (_x, y2, mask, _weightSlice),
     185             :                 _fitter.errorMessage()
     186             :         );
     187      545684 : }
     188             : 
     189           0 : template <class T>  void ImageFit1D<T>::setData (
     190             :         const casacore::IPosition& pos,
     191             :         const ImageFit1D<T>::AbcissaType abcissaType,
     192             :         const casacore::Bool doAbs, const casacore::Double* const &abscissaDivisor,
     193             :     casacore::Array<casacore::Double> (*xfunc)(const casacore::Array<casacore::Double>&),
     194             :     casacore::Array<FitterType> (*yfunc)(const casacore::Array<FitterType>&)
     195             : ) {
     196           0 :         _resetFitter();
     197             :         /*
     198             :         const casacore::uInt nDim = _image->ndim();
     199             :         casacore::IPosition start(nDim);
     200             :         start(_axis) = 0;
     201             :         for (casacore::uInt i=0; i<nDim; i++) {
     202             :                 if (i!=_axis) {
     203             :                         start(i) = pos(i);
     204             :                 }
     205             :         }
     206             :         */
     207             : 
     208           0 :         casacore::IPosition start = pos;
     209           0 :         start[_axis] = 0;
     210             : 
     211             :         // Get ordinate data
     212             : 
     213           0 :         casacore::Vector<T> y;
     214           0 :         y = _image->getSlice(start, _sliceShape, true);
     215             : 
     216             :         // Mask
     217             : 
     218           0 :         casacore::Vector<casacore::Bool> mask;
     219           0 :         mask = _image->getMaskSlice(start, _sliceShape, true);
     220             : 
     221             :         // Weights
     222             : 
     223           0 :         if (_weights.get()) {
     224           0 :                 convertArray(_weightSlice, _weights->getSlice(start, _sliceShape, true));
     225             :         }
     226             :         else {
     227           0 :                 _weightSlice = _unityWeights;
     228             :         }
     229             : 
     230             :         // Generate Abscissa
     231             : 
     232           0 :         casacore::Vector<casacore::Double> x = _x.copy();
     233           0 :         if (x.size() == 0) {
     234           0 :                 x = makeAbscissa(abcissaType, doAbs, abscissaDivisor);
     235           0 :                 if (xfunc) {
     236           0 :                         x = (*xfunc)(x);
     237             :                 }
     238             :         }
     239             :         // Set data in fitter; we need to use a casacore::Double fitter at present
     240             : 
     241           0 :         casacore::Vector<FitterType> y2(y.shape());
     242           0 :         convertArray(y2, y);
     243           0 :         if (yfunc) {
     244           0 :                 y2 = (*yfunc)(y2);
     245             :                 // in some cases, the supplied function will return NAN values, eg
     246             :                 // log(y) will return NAN for nonpositive y values. Just mask those.
     247           0 :                 mask = mask && ! isNaN(y2);
     248             :         }
     249           0 :         ThrowIf(
     250             :                 !_fitter.setData (_x, y2, mask, _weightSlice),
     251             :                 _fitter.errorMessage()
     252             :         );
     253           0 : }
     254             : 
     255             : /*
     256             : template <class T> casacore::Bool ImageFit1D<T>::setData (
     257             :                 const casacore::ImageRegion& region,
     258             :                 const ImageFit1D<T>::AbcissaType abcissaType,
     259             :                 const casacore::Bool doAbs)
     260             :                 {
     261             :         _resetFitter();
     262             :         // Make SubImage
     263             : 
     264             :         const casacore::SubImage<T> subImage(*_image, region, false);
     265             : 
     266             :         // Average over non-profile axes
     267             : 
     268             :         const casacore::uInt nDim = subImage.ndim();
     269             :         casacore::IPosition axes = casacore::IPosition::otherAxes(nDim, casacore::IPosition(1,_axis));
     270             :         casacore::Bool dropDeg = true;
     271             :         casacore::Vector<T> y;
     272             :         casacore::Vector<casacore::Bool> mask;
     273             :         casacore::LatticeMathUtil::collapse (y, mask, axes, subImage, dropDeg);
     274             : 
     275             :         // Weights
     276             : 
     277             :         casacore::Vector<T> weights(y.nelements());
     278             :         weights = 1.0;
     279             :         if (_weights.get()) {
     280             :                 casacore::LatticeMathUtil::collapse (weights, axes, *_weights, dropDeg);
     281             :         }
     282             : 
     283             :         // Generate Abcissa
     284             : 
     285             :         casacore::Vector<casacore::Double> x = _x;
     286             :         if (x.size() == 0) {
     287             :                 x = makeAbscissa(abcissaType, doAbs, 0);
     288             :         }
     289             : 
     290             :         // Set data in fitter; we need to use a casacore::Double fitter at present
     291             : 
     292             :         casacore::Vector<FitterType> y2(y.shape());
     293             :         convertArray(y2, y);
     294             :         casacore::Vector<casacore::Double> w2(weights.shape());
     295             :         convertArray(w2, weights);
     296             :         if (!_fitter.setData (x, y2, mask, w2)) {
     297             :                 _error = _fitter.errorMessage();
     298             :                 return false;
     299             :         }
     300             :         return true;
     301             : }
     302             : */
     303             : 
     304             : template <class T> 
     305       16211 : void ImageFit1D<T>::setGaussianElements (casacore::uInt nGauss) {
     306       16211 :         if (nGauss > 0) {
     307       16211 :                 check();
     308       16211 :                 ThrowIf(
     309             :                         !_fitter.setGaussianElements (nGauss),
     310             :                         _fitter.errorMessage()
     311             :                 );
     312             :         }
     313       16211 : }
     314             : 
     315             : template <class T> 
     316      545637 : bool ImageFit1D<T>::fit () {
     317             :    // check();
     318      545637 :    _converged = _fitter.fit();
     319      545550 :    _success = true;
     320      545550 :    return _converged;
     321             : }
     322             : 
     323             : template <class T> 
     324      562439 : bool ImageFit1D<T>::succeeded() const {
     325      562439 :         return _success;
     326             : }
     327             : 
     328             : template <class T>
     329      558959 : bool ImageFit1D<T>::converged() const {
     330      558959 :         return _converged;
     331             : }
     332             : 
     333             : template <class T>
     334          86 : bool ImageFit1D<T>::setAbcissaState (
     335             :         casacore::String& errMsg, ImageFit1D<T>::AbcissaType& type,
     336             :     casacore::CoordinateSystem& cSys, const casacore::String& xUnit,
     337             :     const casacore::String& doppler, casacore::uInt pixelAxis
     338             : ) {
     339          86 :         if (xUnit == "native") {
     340          16 :                 type = ImageFit1D<T>::IM_NATIVE;
     341          16 :                 return true;
     342             :         }
     343          70 :    if (xUnit.contains(casacore::String("pix"))) {
     344          70 :       type = ImageFit1D<T>::PIXEL;
     345          70 :       return true;
     346             :    }
     347           0 :    casacore::Unit unitKMS(casacore::String("km/s"));
     348             : 
     349           0 :    auto isSpectral = cSys.spectralAxisNumber(false) == (casacore::Int)pixelAxis;
     350             : 
     351             : // Defer unit making until now as 'pix' not a valid unit
     352             : 
     353           0 :    casacore::Bool ok(false);
     354           0 :    casacore::Unit unit(xUnit);
     355           0 :    if (unit==unitKMS && isSpectral) {
     356           0 :       ok = casacore::CoordinateUtil::setSpectralState (errMsg, cSys, xUnit, doppler);
     357           0 :       type = ImageFit1D<T>::VELOCITY;
     358             :    } else {
     359           0 :       casacore::Vector<casacore::String> units = cSys.worldAxisUnits().copy();
     360           0 :       units(pixelAxis) = xUnit;
     361           0 :       ok = cSys.setWorldAxisUnits(units);
     362           0 :       if (!ok) errMsg = cSys.errorMessage();
     363           0 :       type = ImageFit1D<T>::IM_NATIVE;
     364           0 :    }
     365             : //
     366           0 :    return ok;
     367           0 : }
     368             : 
     369             : template <class T> 
     370          86 : casacore::Vector<casacore::Double> ImageFit1D<T>::makeAbscissa (
     371             :         ImageFit1D<T>::AbcissaType type,
     372             :         casacore::Bool doAbs, const casacore::Double* const &abscissaDivisor
     373             : ) {
     374          86 :    const casacore::uInt n = _image->shape()(_axis);
     375          86 :    casacore::Vector<casacore::Double> x(n);
     376             : 
     377          86 :    const casacore::CoordinateSystem& csys = _image->coordinates();
     378          86 :    casacore::Double refPix = csys.referencePixel()(_axis);
     379          86 :    if (type==PIXEL) {
     380          70 :       indgen(x);
     381          70 :       if (!doAbs) {
     382           0 :           x -= refPix;
     383             :       }
     384          70 :       return x;
     385             :    }
     386             : 
     387             : // Find the pixel axis
     388             : 
     389             :    casacore::Int coord, axisInCoord;
     390          16 :    csys.findPixelAxis (coord, axisInCoord, _axis);
     391          16 :    if (type==VELOCITY) {
     392           0 :       AlwaysAssert(csys.type(coord)==casacore::Coordinate::SPECTRAL, casacore::AipsError);
     393           0 :       const casacore::SpectralCoordinate& sCoord = csys.spectralCoordinate(coord);
     394             :       casacore::Double world;
     395           0 :       for (casacore::uInt i=0; i<n; i++) {
     396           0 :          if (!sCoord.pixelToVelocity (world, casacore::Double(i))) {
     397           0 :             throw casacore::AipsError(sCoord.errorMessage());
     398             :          } else {
     399           0 :             if (doAbs) {
     400           0 :                x[i] = world;
     401             :             } else {
     402             :                casacore::Double worldRefVal;
     403           0 :                sCoord.pixelToVelocity (worldRefVal, refPix);
     404           0 :                x -= worldRefVal;
     405             :             }
     406             :          }
     407             :       }
     408             :    }
     409          16 :    else if (type==IM_NATIVE) {
     410          16 :       const casacore::Coordinate& gCoord = csys.coordinate(coord);
     411          32 :       casacore::Vector<casacore::Double> pixel(gCoord.referencePixel().copy());
     412          16 :       casacore::Vector<casacore::Double> world;
     413             : 
     414        1627 :       for (casacore::uInt i=0; i<n; i++) {
     415        1611 :          pixel(axisInCoord) = i;
     416        1611 :          if (!gCoord.toWorld(world, pixel)) {
     417           0 :             throw casacore::AipsError(gCoord.errorMessage());
     418             :          }
     419             : //
     420        1611 :          if (!doAbs) {
     421           0 :                  gCoord.makeWorldRelative(world);
     422             :          }
     423        1611 :          x[i] = world(axisInCoord);
     424             :       }
     425          16 :       if (abscissaDivisor) {
     426           0 :           x /= *abscissaDivisor;
     427             :       }
     428          16 :    } else {
     429           0 :       throw casacore::AipsError("Unrecognized abscissa type");
     430             :    }
     431          16 :    return x;
     432             : 
     433           0 : }
     434             : 
     435             : 
     436             : template <class T> 
     437       16211 : void ImageFit1D<T>::check() const
     438             : {
     439       16211 :    if (!_image.get()) {
     440           0 :       throw(casacore::AipsError("Image has not been set"));
     441             :    }
     442       16211 : }
     443             : 
     444             : /*
     445             : template <class T> 
     446             : void ImageFit1D<T>::setWeightsImage (const casacore::ImageInterface<T>& image)
     447             : {
     448             :    AlwaysAssert (_image->shape().isEqual(image.shape()), casacore::AipsError);
     449             :    _weights.reset(image.cloneII());
     450             : }
     451             : */
     452             : 
     453             : template <class T> 
     454             : void ImageFit1D<T>::copy(const ImageFit1D<T>& other)
     455             : {
     456             :         _image.reset(
     457             :                 other._image.get()
     458             :                         ? other._image->cloneII()
     459             :                         : 0
     460             :         );
     461             :         _weights.reset(
     462             :                 other._weights.get()
     463             :                         ? other._weights->cloneII()
     464             :                         : 0
     465             :         );
     466             : 
     467             : // These things are copies
     468             : 
     469             :    _axis = other._axis;
     470             : //
     471             :    _fitter = other._fitter;
     472             : 
     473             :    _converged = other._converged;
     474             :    _success = other._success;
     475             :    _isValid = other._isValid;
     476             :    _sliceShape = other._sliceShape;
     477             :    _unityWeights = other._unityWeights.copy();
     478             : }
     479             : 
     480             : template <class T> 
     481          86 : void ImageFit1D<T>::checkType() const
     482             : //
     483             : // At this point, ProfileFitter and SpectralFitter
     484             : // take the *Same* template type for X and Y
     485             : // To avoid precision problems we do it all in Double
     486             : // at the moment.  Later X<T> and Y<T> can be separated
     487             :  //
     488             : {
     489          86 :    casacore::DataType tp = casacore::whatType<FitterType>();
     490          86 :    AlwaysAssert(tp==casacore::TpDouble, casacore::AipsError);
     491          86 : }
     492             : 
     493             : 
     494             : template <class T> 
     495             : casacore::Vector<T> ImageFit1D<T>::getEstimate (casacore::Int which) const
     496             : {
     497             :    casacore::Vector<FitterType> e = _fitter.getEstimate(which);
     498             :    casacore::Vector<T> t(e.shape());
     499             :    convertArray (t, e);
     500             :    return t;
     501             : }
     502             : 
     503             : 
     504             : template <class T> 
     505      529069 : casacore::Vector<T> ImageFit1D<T>::getFit (casacore::Int which) const
     506             : {
     507      529069 :    casacore::Vector<FitterType> f = _fitter.getFit(which);
     508      529069 :    casacore::Vector<T> t(f.shape());
     509      529069 :    convertArray (t, f);
     510     1058138 :    return t;
     511      529069 : }
     512             : 
     513             : template <class T> 
     514      529097 : casacore::Vector<T> ImageFit1D<T>::getResidual(casacore::Int which, casacore::Bool fit) const
     515             : {
     516      529097 :    casacore::Vector<FitterType> r = _fitter.getResidual(which, fit);
     517      529097 :    casacore::Vector<T> t(r.shape());
     518      529097 :    convertArray (t, r);
     519     1058194 :    return t;
     520      529097 : }
     521             : 
     522           0 : template <class T>  void ImageFit1D<T>::invalidate() {
     523           0 :         _isValid = false;
     524           0 : }
     525             : 
     526             : template <class T>
     527      558959 : bool ImageFit1D<T>::isValid() const {
     528      558959 :         return _isValid;
     529             : }
     530             : 
     531             : template <class T>
     532      545770 : void ImageFit1D<T>::_resetFitter() {
     533      545770 :         _fitter = ProfileFit1D<FitterType>();
     534      545770 :         _fitter.setElements(_fitter.getList(false));
     535      545770 :         _isValid = true;
     536      545770 :         _converged = false;
     537      545770 :         _success = false;
     538      545770 : }
     539             : 
     540             : 
     541             : } //#End casa namespace

Generated by: LCOV version 1.16