LCOV - code coverage report
Current view: top level - imageanalysis/ImageAnalysis - Image2DConvolver.tcc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 0 539 0.0 %
Date: 2024-10-10 15:00:01 Functions: 0 17 0.0 %

          Line data    Source code
       1             : //# Image2DConvolver.cc:  convolution of an image by given Array
       2             : //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
       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             : //   
      27             : #include <imageanalysis/ImageAnalysis/Image2DConvolver.h>
      28             : 
      29             : #include <casacore/casa/aips.h>
      30             : #include <casacore/casa/Arrays/IPosition.h>
      31             : #include <casacore/casa/Arrays/Array.h>
      32             : #include <casacore/casa/Arrays/ArrayMath.h>
      33             : #include <casacore/casa/Arrays/Vector.h>
      34             : #include <casacore/casa/Arrays/Matrix.h>
      35             : #include <casacore/casa/Exceptions/Error.h>
      36             : #include <components/ComponentModels/GaussianDeconvolver.h>
      37             : #include <components/ComponentModels/GaussianShape.h>
      38             : #include <components/ComponentModels/SkyComponentFactory.h>
      39             : #include <casacore/coordinates/Coordinates/CoordinateUtil.h>
      40             : #include <casacore/coordinates/Coordinates/CoordinateSystem.h>
      41             : #include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
      42             : #include <casacore/lattices/LatticeMath/Fit2D.h>
      43             : #include <casacore/scimath/Functionals/Gaussian2D.h>
      44             : #include <imageanalysis/ImageAnalysis/ImageConvolver.h>
      45             : #include <imageanalysis/ImageAnalysis/ImageMetaData.h>
      46             : #include <casacore/images/Images/PagedImage.h>
      47             : #include <casacore/images/Images/TempImage.h>
      48             : #include <casacore/images/Images/ImageInterface.h>
      49             : #include <casacore/images/Images/ImageInfo.h>
      50             : #include <casacore/images/Images/ImageUtilities.h>
      51             : #include <casacore/images/Images/SubImage.h>
      52             : #include <casacore/casa/Logging/LogIO.h>
      53             : #include <casacore/scimath/Mathematics/Convolver.h>
      54             : #include <casacore/casa/Quanta/Quantum.h>
      55             : #include <casacore/casa/Quanta/MVAngle.h>
      56             : #include <casacore/casa/Quanta/Unit.h>
      57             : #include <casacore/casa/Quanta/QLogical.h>
      58             : #include <iostream>
      59             : 
      60             : #include <memory>
      61             : 
      62             : namespace casa {
      63             : 
      64             : template <class T> const casacore::String Image2DConvolver<T>::CLASS_NAME
      65             :     = "Image2DConvolver";
      66             : 
      67           0 : template <class T> Image2DConvolver<T>::Image2DConvolver(
      68             :     const SPCIIT image, const casacore::Record *const &region,
      69             :     const casacore::String& mask, const casacore::String& outname,
      70             :     const bool overwrite
      71             : ) : ImageTask<T>(image, "", region, "", "", "", mask, outname, overwrite),
      72           0 :     _type(casacore::VectorKernel::GAUSSIAN),  _scale(0), _major(), _minor(),
      73           0 :     _pa(), _axes(image->coordinates().directionAxesNumbers()) {
      74           0 :     this->_construct(true);
      75           0 : }
      76             : 
      77             : // TODO use GaussianBeams rather than casacore::Vector<casacore::Quantity>s, this method
      78             : // can probably be eliminated.
      79             : template <class T>
      80           0 : std::vector<casacore::Quantity> Image2DConvolver<T>::_getConvolvingBeamForTargetResolution(
      81             :     const std::vector<casacore::Quantity>& targetBeamParms,
      82             :     const casacore::GaussianBeam& inputBeam
      83             : ) const {
      84           0 :     casacore::GaussianBeam convolvingBeam;
      85           0 :     casacore::GaussianBeam targetBeam(
      86             :         targetBeamParms[0], targetBeamParms[1],
      87             :         targetBeamParms[2]
      88             :     );
      89             :     try {
      90           0 :         if(
      91           0 :             GaussianDeconvolver::deconvolve(
      92             :                 convolvingBeam, targetBeam, inputBeam
      93             :             )
      94             :         ) {
      95             :             // point source, or convolvingBeam nonsensical
      96           0 :             throw casacore::AipsError();
      97             :         }
      98             :     }
      99           0 :     catch (const casacore::AipsError& x) {
     100           0 :         ostringstream os;
     101           0 :         os << "Unable to reach target resolution of " << targetBeam << " Input "
     102           0 :             << "image beam " << inputBeam << " is (nearly) identical to or "
     103           0 :             << "larger than the output beam size";
     104           0 :         ThrowCc(os.str());
     105           0 :     }
     106           0 :     std::vector<casacore::Quantity> kernelParms {
     107           0 :         convolvingBeam.getMajor(),
     108           0 :         convolvingBeam.getMinor(),
     109             :         convolvingBeam.getPA(true)
     110             :     };
     111           0 :     return kernelParms;
     112           0 : }
     113             : 
     114           0 : template <class T> void Image2DConvolver<T>::setAxes(
     115             :     const std::pair<uint, uint>& axes
     116             : ) {
     117           0 :     auto ndim = this->_getImage()->ndim();
     118           0 :     ThrowIf(axes.first == axes.second, "Axes must be different");
     119           0 :     ThrowIf(
     120             :         axes.first >= ndim || axes.second >= ndim,
     121             :         "Axis value must be less than number of axes in image"
     122             :     );
     123           0 :     if (_axes.size() != 2) {
     124           0 :         _axes.resize(2, false);
     125             :     }
     126           0 :     _axes[0] = axes.first;
     127           0 :     _axes[1] = axes.second;
     128           0 : }
     129             : 
     130           0 : template <class T> void Image2DConvolver<T>::setKernel(
     131             :     const casacore::String& type, const casacore::Quantity& major,
     132             :     const casacore::Quantity& minor, const casacore::Quantity& pa
     133             : ) {
     134           0 :     ThrowIf (major < minor, "Major axis is less than minor axis");
     135           0 :     _type = casacore::VectorKernel::toKernelType(type);
     136           0 :     _major = major;
     137           0 :     _minor = minor;
     138           0 :     _pa = pa;
     139           0 : }
     140             : 
     141           0 : template <class T> SPIIT Image2DConvolver<T>::convolve() {
     142           0 :     ThrowIf(
     143             :         _axes.nelements() != 2, "You must give two pixel axes to convolve"
     144             :     );
     145           0 :     auto inc = this->_getImage()->coordinates().increment();
     146           0 :     auto units = this->_getImage()->coordinates().worldAxisUnits();
     147           0 :     ThrowIf(
     148             :         ! near (
     149             :             casacore::Quantity(fabs(inc[_axes[0]]), units[_axes[0]]),
     150             :             casacore::Quantity(fabs(inc[_axes[1]]), units[_axes[1]])
     151             :         ),
     152             :         "Pixels must be square, please regrid your image so that they are"
     153             :     );
     154           0 :     auto subImage = SubImageFactory<T>::createImage(
     155           0 :         *this->_getImage(), "", *this->_getRegion(), this->_getMask(),
     156           0 :         this->_getDropDegen(), false, false, this->_getStretch()
     157             :     );
     158           0 :     const auto nDim = subImage->ndim();
     159           0 :     ThrowIf(
     160             :         _axes(0) < 0 || _axes(0) >= nDim
     161             :         || _axes(1) < 0 || _axes(1) >= nDim,
     162             :         "The pixel axes " + casacore::String::toString(_axes) + " are illegal"
     163             :     );
     164           0 :     ThrowIf(
     165             :         nDim < 2, "The image axes must have at least 2 pixel axes"
     166             :     );
     167           0 :     shared_ptr<TempImage<T>> outImage(
     168           0 :         new casacore::TempImage<T>(
     169           0 :             subImage->shape(), subImage->coordinates()
     170             :         )
     171             :      );
     172           0 :     _convolve(
     173           0 :         outImage, *subImage, _type
     174             :     );
     175           0 :     if (subImage->isMasked()) {
     176           0 :         TempLattice<bool> mask(outImage->shape());
     177           0 :         ImageTask<T>::_copyMask(mask, *subImage);
     178           0 :         outImage->attachMask(mask);
     179           0 :     }
     180           0 :     return this->_prepareOutputImage(*outImage);
     181           0 : }
     182             : 
     183           0 : template <class T> void Image2DConvolver<T>::_convolve(
     184             :     SPIIT imageOut, const ImageInterface<T>& imageIn,
     185             :     VectorKernel::KernelTypes kernelType
     186             : ) const {
     187           0 :     const auto& inShape = imageIn.shape();
     188           0 :     const auto& outShape = imageOut->shape();
     189           0 :     ThrowIf(
     190             :         ! inShape.isEqual(outShape),
     191             :         "Input and output images must have the same shape"
     192             :     );
     193             :     // Generate Kernel Array (height unity)
     194           0 :     ThrowIf(
     195             :         _targetres && kernelType != casacore::VectorKernel::GAUSSIAN,
     196             :         "targetres can only be true for a Gaussian convolving kernel"
     197             :     );
     198             :     // maybe can remove this comment if I'm smart enough
     199             :     // kernel needs to be type T because ultimately we use ImageConvolver which
     200             :     // requires the kernel and input image to be of the same type. This is
     201             :     // kind of stupid because our kernels are always real-valued, and we use
     202             :     // Fit2D which requires a real-valued kernel, so it seems we could support
     203             :     // complex valued images and real valued kernels if ImageConvolver was
     204             :     // smarter
     205           0 :     Array<double> kernel;
     206             :     // initialize to avoid compiler warning, kernelVolume will always be set to
     207             :     // something reasonable below before it is used.
     208           0 :     double kernelVolume = -1;
     209           0 :     std::vector<casacore::Quantity> originalParms{_major, _minor, _pa};
     210           0 :     if (! _targetres) {
     211           0 :         kernelVolume = _makeKernel(
     212             :             kernel, kernelType, originalParms, imageIn
     213             :         );
     214             :     }
     215           0 :     const auto& cSys = imageIn.coordinates();
     216           0 :     if (_major.getUnit().startsWith("pix")) {
     217           0 :         auto inc = cSys.increment()[_axes[0]];
     218           0 :         auto unit = cSys.worldAxisUnits()[_axes[0]];
     219           0 :         originalParms[0] = _major.getValue()*casacore::Quantity(abs(inc), unit);
     220           0 :     }
     221           0 :     if (_minor.getUnit().startsWith("pix")) {
     222           0 :         auto inc = cSys.increment()[_axes[1]];
     223           0 :         auto unit = cSys.worldAxisUnits()[_axes[1]];
     224           0 :         originalParms[1] = _minor.getValue()*casacore::Quantity(abs(inc), unit);
     225           0 :     }
     226           0 :     auto kernelParms = originalParms;
     227             :     // Figure out output image restoring beam (if any), output units and scale
     228             :     // factor for convolution kernel array
     229           0 :     GaussianBeam beamOut;
     230           0 :     const auto& imageInfo = imageIn.imageInfo();
     231           0 :     const auto& brightnessUnit = imageIn.units();
     232           0 :     String brightnessUnitOut;
     233           0 :     auto iiOut = imageOut->imageInfo();
     234           0 :     auto logFactors = false;
     235           0 :     double factor1 = -1;
     236           0 :     double pixelArea = 0;
     237           0 :     auto autoScale = _scale <= 0;
     238           0 :     if (autoScale) {
     239           0 :         auto bunitUp = brightnessUnit.getName();
     240           0 :         bunitUp.upcase();
     241           0 :         logFactors = bunitUp.contains("/BEAM");
     242           0 :         if (logFactors) {
     243           0 :             pixelArea = cSys.directionCoordinate().getPixelArea().getValue(
     244             :                 "arcsec*arcsec"
     245             :             );
     246           0 :             if (! _targetres) {
     247           0 :                 Vector<Quantity> const kernelParmsV(kernelParms);
     248           0 :                 GaussianBeam kernelBeam(kernelParmsV);
     249           0 :                 factor1 = pixelArea/kernelBeam.getArea("arcsec*arcsec");
     250           0 :             }
     251             :         }
     252           0 :     }
     253           0 :     if (imageInfo.hasMultipleBeams()) {
     254           0 :         _doMultipleBeams(
     255             :             iiOut, kernelVolume, imageOut, brightnessUnitOut,
     256             :             beamOut, factor1, imageIn, originalParms, kernelParms,
     257             :             kernel, kernelType, logFactors, pixelArea
     258             :         );
     259             :     }
     260             :     else {
     261           0 :         _doSingleBeam(
     262             :             iiOut, kernelVolume, kernelParms, kernel,
     263             :             brightnessUnitOut, beamOut, imageOut, imageIn,
     264             :             originalParms, kernelType, logFactors, factor1, 
     265             :             pixelArea
     266             :         );
     267             :     }
     268           0 :     imageOut->setUnits(brightnessUnitOut);
     269           0 :     imageOut->setImageInfo(iiOut);
     270           0 :     _logBeamInfo(imageInfo, "Original " + this->_getImage()->name());
     271           0 :     _logBeamInfo(iiOut, "Output " + this->_getOutname());
     272           0 : }
     273             : 
     274           0 : template <class T> void Image2DConvolver<T>::_logBeamInfo(
     275             :     const ImageInfo& imageInfo, const String& desc
     276             : ) const {
     277           0 :     ostringstream oss;
     278           0 :     const auto& beamSet = imageInfo.getBeamSet();
     279           0 :     if (! imageInfo.hasBeam()) {
     280           0 :         oss << desc << " has no beam";
     281             :     }
     282           0 :     else if (imageInfo.hasSingleBeam()) {
     283           0 :         oss << desc << " resolution " << beamSet.getBeam();
     284             :     }
     285             :     else {
     286           0 :         oss << desc << " has multiple beams. Min area beam: "
     287           0 :             << beamSet.getMinAreaBeam() << ". Max area beam: "
     288           0 :             << beamSet.getMaxAreaBeam() << ". Median area beam "
     289           0 :             << beamSet.getMedianAreaBeam();
     290             :     }
     291           0 :     auto msg = oss.str();
     292           0 :     LogOrigin lor(getClass(), __func__);
     293           0 :     this->addHistory(lor, msg);
     294           0 :     _log(msg, LogIO::NORMAL);
     295           0 : }
     296             : 
     297           0 : template <class T> void Image2DConvolver<T>::_log(
     298             :     const String& msg, LogIO::Command priority
     299             : ) const {
     300           0 :     if (! _suppressWarnings) {
     301           0 :         *this->_getLog() << priority << msg << LogIO::POST;
     302             :     }
     303           0 : }
     304             : 
     305           0 : template <class T> void Image2DConvolver<T>::_doSingleBeam(
     306             :     ImageInfo& iiOut, double& kernelVolume, vector<Quantity>& kernelParms,
     307             :     Array<double>& kernel, String& brightnessUnitOut, GaussianBeam& beamOut,
     308             :     SPIIT imageOut, const ImageInterface<T>& imageIn,
     309             :     const vector<Quantity>& originalParms, VectorKernel::KernelTypes kernelType,
     310             :     bool logFactors, double factor1, double pixelArea
     311             : ) const {
     312           0 :     GaussianBeam inputBeam = imageIn.imageInfo().restoringBeam();
     313           0 :     Vector<Quantity> const kernelParmsV(kernelParms);
     314           0 :     Vector<Quantity> const originalParmsV(originalParms);
     315           0 :     if (_targetres) {
     316           0 :         kernelParms = _getConvolvingBeamForTargetResolution(
     317             :             originalParms, inputBeam
     318             :         );
     319           0 :         if (! _suppressWarnings) {
     320           0 :             *this->_getLog() << LogOrigin("Image2DConvolver<T>", __func__);
     321           0 :             ostringstream oss;
     322           0 :             oss << "Convolving image that has a beam of "
     323           0 :                 << inputBeam << " with a Gaussian of "
     324           0 :                 << GaussianBeam(Vector<Quantity>(kernelParms))
     325           0 :                 << " to reach a target resolution of "
     326           0 :                 << GaussianBeam(originalParmsV);
     327           0 :             _log(oss.str(), LogIO::NORMAL);
     328           0 :         }
     329           0 :         kernelVolume = _makeKernel(
     330             :             kernel, kernelType, kernelParms, imageIn
     331             :         );
     332             :     }
     333           0 :     const CoordinateSystem& cSys = imageIn.coordinates();
     334           0 :     auto scaleFactor = _dealWithRestoringBeam(
     335             :         brightnessUnitOut, beamOut, kernel, kernelVolume,
     336             :         kernelType, kernelParmsV, cSys, inputBeam,
     337           0 :         imageIn.units(), true
     338             :     );
     339           0 :     ostringstream oss;
     340           0 :     if (! _suppressWarnings) {
     341           0 :         oss << "Scaling pixel values by ";
     342             :     }
     343           0 :     if (logFactors) {
     344           0 :         if (_targetres) {
     345           0 :             GaussianBeam kernelBeam(kernelParmsV);
     346           0 :             factor1 = pixelArea/kernelBeam.getArea("arcsec*arcsec");
     347           0 :         }
     348           0 :         double factor2 = beamOut.getArea("arcsec*arcsec")/inputBeam.getArea("arcsec*arcsec");
     349           0 :         if (! _suppressWarnings) {
     350           0 :             oss << "inverse of area of convolution kernel in pixels (" << factor1
     351           0 :                 << ") times the ratio of the beam areas (" << factor2 << ") = ";
     352             :         }
     353             :     }
     354           0 :     if (! _suppressWarnings) {
     355           0 :         oss << scaleFactor;
     356           0 :         _log(oss.str(), LogIO::NORMAL);
     357             :     }
     358           0 :     if (_targetres && near(beamOut.getMajor(), beamOut.getMinor(), 1e-7)) {
     359             :         // circular beam should have same PA as given by user if
     360             :         // targetres
     361           0 :         beamOut.setPA(originalParms[2]);
     362             :     }
     363             :     // Convolve.  We have already scaled the convolution kernel (with some
     364             :     // trickery cleverer than what ImageConvolver can do) so no more scaling
     365           0 :     ImageConvolver<T> aic;
     366           0 :     Array<T> modKernel(kernel.shape());
     367           0 :     casacore::convertArray(modKernel, scaleFactor*kernel);
     368           0 :     aic.convolve(
     369           0 :         *this->_getLog(), *imageOut, imageIn, modKernel,
     370             :         ImageConvolver<T>::NONE, 1.0, true
     371             :     );
     372             :     // Overwrite some bits and pieces in the output image to do with the
     373             :     // restoring beam  and image units
     374             :     bool holdsOneSkyAxis;
     375           0 :     const auto hasSky = casacore::CoordinateUtil::holdsSky(
     376             :         holdsOneSkyAxis, cSys, _axes.asVector()
     377             :     );
     378           0 :     if (hasSky && ! beamOut.isNull()) {
     379           0 :         if (_targetres) {
     380           0 :             Vector<Quantity> const originalParmsV(originalParms);
     381           0 :             casacore::GaussianBeam target(originalParmsV);
     382           0 :             iiOut.setRestoringBeam(target);
     383           0 :             if (
     384           0 :                 ! _suppressWarnings && ! near(
     385           0 :                     beamOut, target, 1e-3, casacore::Quantity(0.01, "deg")
     386             :                 ) 
     387             :             ) {
     388           0 :                 *this->_getLog() << LogIO::WARN << "Fitted restoring beam is "
     389             :                     << beamOut << ", but putting requested target "
     390             :                     << "resolution beam " << target << " in the image "
     391             :                     << "metadata. Both beams may be considered consistent with "
     392           0 :                     << "the convolution result." << LogIO::POST;
     393             :             }
     394           0 :         }
     395             :         else {
     396           0 :             iiOut.setRestoringBeam(beamOut);
     397             :         }
     398             :     }
     399           0 :     else if (holdsOneSkyAxis) {
     400             :         // If one of the axes is in the sky plane, we must
     401             :         // delete the restoring beam as it is no longer meaningful
     402           0 :         iiOut.removeRestoringBeam();
     403           0 :         if (! _suppressWarnings) {
     404           0 :             oss.str("");
     405           0 :             oss << "Because you convolved just one of the sky axes" << endl;
     406           0 :             oss << "The output image does not have a valid spatial restoring beam";
     407           0 :             _log(oss.str(), LogIO::WARN);
     408             :         }
     409             :     }
     410           0 : }
     411             : 
     412           0 : template <class T> void Image2DConvolver<T>::_doMultipleBeams(
     413             :     ImageInfo& iiOut, double& kernelVolume, SPIIT imageOut,
     414             :     String& brightnessUnitOut, GaussianBeam& beamOut, Double factor1,
     415             :     const ImageInterface<T>& imageIn, const vector<Quantity>& originalParms,
     416             :     vector<Quantity>& kernelParms, Array<double>& kernel,
     417             :     VectorKernel::KernelTypes kernelType, bool logFactors, double pixelArea
     418             : ) const {
     419           0 :     ImageMetaData<T> md(imageOut);
     420           0 :     auto nChan = md.nChannels();
     421           0 :     auto nPol = md.nStokes();
     422             :     // initialize all beams to be null
     423           0 :     iiOut.setAllBeams(nChan, nPol, casacore::GaussianBeam());
     424           0 :     const auto& cSys = imageIn.coordinates();
     425           0 :     auto specAxis = cSys.spectralAxisNumber();
     426           0 :     auto polAxis = cSys.polarizationAxisNumber();
     427           0 :     casacore::IPosition start(imageIn.ndim(), 0);
     428           0 :     auto end = imageIn.shape();
     429           0 :     if (nChan > 0) {
     430           0 :         end[specAxis] = 1;
     431             :     }
     432           0 :     if (nPol > 0) {
     433           0 :         end[polAxis] = 1;
     434             :     }
     435           0 :     int channel = -1;
     436           0 :     int polarization = -1;
     437           0 :     if (_targetres) {
     438           0 :         iiOut.removeRestoringBeam();
     439           0 :         Vector<Quantity> const kernelParmsV(kernelParms);
     440           0 :         iiOut.setRestoringBeam(casacore::GaussianBeam(kernelParmsV));
     441           0 :     }
     442           0 :     uint count = (nChan > 0 && nPol > 0)
     443           0 :         ? nChan * nPol
     444             :         : nChan > 0
     445           0 :           ? nChan
     446             :           : nPol;
     447           0 :     for (uint i=0; i<count; ++i) {
     448           0 :         if (nChan > 0) {
     449           0 :             channel = i % nChan;
     450           0 :             start[specAxis] = channel;
     451             :         }
     452           0 :         if (nPol > 0) {
     453           0 :             polarization = nChan > 1
     454           0 :                 ? i/nChan // integer arithmetic
     455             :                 : i;
     456           0 :             start[polAxis] = polarization;
     457             :         }
     458           0 :         casacore::Slicer slice(start, end);
     459           0 :         casacore::SubImage<T> subImage(imageIn, slice);
     460           0 :         auto subCsys = subImage.coordinates();
     461           0 :         if (subCsys.hasSpectralAxis()) {
     462           0 :             auto subRefPix = subCsys.referencePixel();
     463           0 :             subRefPix[specAxis] = 0;
     464           0 :             subCsys.setReferencePixel(subRefPix);
     465           0 :         }
     466           0 :         auto inputBeam
     467           0 :             = imageIn.imageInfo().restoringBeam(channel, polarization);
     468           0 :         auto doConvolve = true;
     469           0 :         if (_targetres) {
     470           0 :             *this->_getLog() << LogIO::NORMAL;
     471           0 :             if (channel >= 0) {
     472           0 :                 *this->_getLog() << "Channel " << channel << " of " << nChan;
     473           0 :                 if (polarization >= 0) {
     474           0 :                     *this->_getLog() << ", ";
     475             :                 }
     476             :             }
     477           0 :             if (polarization >= 0) {
     478           0 :                 *this->_getLog() << "Polarization " << polarization
     479           0 :                     << " of " << nPol;
     480             :             }
     481           0 :             *this->_getLog() << " ";
     482           0 :             Vector<Quantity> const originalParmsV(originalParms);
     483           0 :             if (
     484           0 :                 near(
     485           0 :                     inputBeam, GaussianBeam(originalParmsV), 1e-5,
     486           0 :                     casacore::Quantity(1e-2, "arcsec")
     487             :                 )
     488             :             ) {
     489           0 :                 doConvolve = false;
     490           0 :                 *this->_getLog() << LogIO::NORMAL
     491             :                     << " Input beam is already near target resolution so this "
     492           0 :                     << "plane will not be convolved" << LogIO::POST;
     493             :             }
     494             :             else {
     495           0 :                 kernelParms = _getConvolvingBeamForTargetResolution(
     496             :                     originalParms, inputBeam
     497             :                 );
     498           0 :                 kernelVolume = _makeKernel(
     499             :                     kernel, kernelType, kernelParms, imageIn
     500             :                 );
     501           0 :                 Vector<Quantity> const kernelParmsV(kernelParms);
     502           0 :                 *this->_getLog() << ": Convolving image which has a beam of "
     503             :                     << inputBeam << " with a Gaussian of "
     504           0 :                     << GaussianBeam(kernelParmsV) << " to reach a target "
     505           0 :                     << "resolution of " << GaussianBeam(originalParmsV)
     506           0 :                     << LogIO::POST;
     507           0 :             }
     508           0 :         }
     509           0 :         casacore::TempImage<T> subImageOut(
     510             :             subImage.shape(), subImage.coordinates()
     511             :         );
     512           0 :         if (doConvolve) {
     513           0 :             Vector<Quantity> const kernelParmsV(kernelParms);
     514           0 :             auto scaleFactor = _dealWithRestoringBeam(
     515             :                 brightnessUnitOut, beamOut, kernel, kernelVolume, kernelType,
     516           0 :                 kernelParmsV, subCsys, inputBeam, imageIn.units(), i == 0
     517             :             );
     518             :             {
     519           0 :                 *this->_getLog() << LogIO::NORMAL << "Scaling pixel values by ";
     520           0 :                 if (logFactors) {
     521           0 :                     if (_targetres) {
     522           0 :                         casacore::GaussianBeam kernelBeam(kernelParmsV);
     523           0 :                         factor1 = pixelArea/kernelBeam.getArea("arcsec*arcsec");
     524           0 :                     }
     525           0 :                     auto factor2 = beamOut.getArea("arcsec*arcsec")
     526           0 :                         /inputBeam.getArea("arcsec*arcsec");
     527           0 :                     *this->_getLog() << "inverse of area of convolution kernel "
     528             :                         << "in pixels (" << factor1 << ") times the ratio of "
     529           0 :                         << "the beam areas (" << factor2 << ") = ";
     530             :                 }
     531           0 :                 *this->_getLog() << scaleFactor << " for ";
     532           0 :                 if (channel >= 0) {
     533           0 :                     *this->_getLog() << "channel number " << channel;
     534           0 :                     if (polarization >= 0) {
     535           0 :                         *this->_getLog() << " and ";
     536             :                     }
     537             :                 }
     538           0 :                 if (polarization >= 0) {
     539           0 :                     *this->_getLog() << "polarization number " << polarization;
     540             :                 }
     541           0 :                 *this->_getLog() << casacore::LogIO::POST;
     542             :             }
     543           0 :             if (
     544           0 :                 _targetres && near(beamOut.getMajor(), beamOut.getMinor(), 1e-7)
     545             :             ) {
     546             :                 // circular beam should have same PA as given by user if
     547             :                 // targetres
     548           0 :                 beamOut.setPA(originalParms[2]);
     549             :             }
     550           0 :             Array<T> modKernel(kernel.shape());
     551           0 :             casacore::convertArray(modKernel, scaleFactor*kernel);
     552           0 :             ImageConvolver<T> aic;
     553           0 :             aic.convolve(
     554           0 :                 *this->_getLog(), subImageOut, subImage, modKernel,
     555             :                 ImageConvolver<T>::NONE, 1.0, true
     556             :             );
     557             : 
     558             :             // _doImageConvolver(subImageOut, subImage, scaleFactor*kernel);
     559           0 :         }
     560             :         else {
     561           0 :             brightnessUnitOut = imageIn.units().getName();
     562           0 :             beamOut = inputBeam;
     563           0 :             subImageOut.put(subImage.get());
     564             :         }
     565             :         {
     566           0 :             auto doMask = imageOut->isMasked() && imageOut->hasPixelMask();
     567           0 :             Lattice<bool>* pMaskOut = nullptr;
     568           0 :             if (doMask) {
     569           0 :                 pMaskOut = &imageOut->pixelMask();
     570           0 :                 if (! pMaskOut->isWritable()) {
     571           0 :                     doMask = false;
     572             :                 }
     573             :             }
     574           0 :             auto cursorShape = subImageOut.niceCursorShape();
     575           0 :             auto outPos = start;
     576           0 :             LatticeStepper stepper(
     577             :                 subImageOut.shape(), cursorShape,
     578             :                 casacore::LatticeStepper::RESIZE
     579             :             );
     580           0 :             RO_MaskedLatticeIterator<T> iter(subImageOut, stepper);
     581           0 :             for (iter.reset(); !iter.atEnd(); iter++) {
     582           0 :                 const auto cursorShape = iter.cursorShape();
     583           0 :                 imageOut->putSlice(iter.cursor(), outPos);
     584           0 :                 if (doMask) {
     585           0 :                     pMaskOut->putSlice(iter.getMask(), outPos);
     586             :                 }
     587           0 :                 outPos = outPos + cursorShape;
     588             :             }
     589           0 :         }
     590           0 :         if (_targetres) {
     591           0 :             Vector<Quantity> const originalParmsV(originalParms);
     592           0 :             GaussianBeam target(originalParmsV);
     593           0 :             if (
     594           0 :                 ! _suppressWarnings && ! casacore::near(
     595           0 :                     beamOut, target, 1e-3, casacore::Quantity(0.01, "deg")
     596             :                 )
     597             :             ) {
     598           0 :                 *this->_getLog() << LogIO::WARN << "Fitted restoring beam for "
     599             :                     << " channel " << channel << " and polarization plane "
     600             :                     << polarization << " is " << beamOut << " but putting "
     601             :                     << "requested target resolution beam " << target << " in "
     602             :                     << "the image metadata. Both beams can be considered "
     603           0 :                     << "consistent with the convolution result." << LogIO::POST;
     604             :             }
     605           0 :         }
     606             :         else {
     607           0 :             iiOut.setBeam(channel, polarization, beamOut);
     608             :         }
     609             :     }
     610           0 : }
     611             : 
     612           0 : template <class T> double Image2DConvolver<T>::_makeKernel(
     613             :     casacore::Array<double>& kernelArray,
     614             :     casacore::VectorKernel::KernelTypes kernelType,
     615             :     const std::vector<casacore::Quantity>& parameters,
     616             :     const casacore::ImageInterface<T>& imageIn
     617             : ) const {
     618             : 
     619             : // Check number of parameters
     620             : 
     621           0 :    Vector<Quantity> const parametersV(parameters);
     622           0 :    _checkKernelParameters(kernelType, parametersV);
     623             : 
     624             : // Convert kernel widths to pixels from world.  Demands major and minor
     625             : // both in pixels or both in world, else exception
     626             : 
     627           0 :    casacore::Vector<double> dParameters;
     628           0 :    const casacore::CoordinateSystem cSys = imageIn.coordinates();
     629             : 
     630             : // Use the reference value for the shape conversion direction
     631             : 
     632           0 :    casacore::Vector<casacore::Quantity> wParameters(5);
     633           0 :    for (uint i=0; i<3; i++) {
     634           0 :       wParameters(i+2) = parameters[i];
     635             :    }
     636             : //
     637           0 :    const casacore::Vector<double> refVal = cSys.referenceValue();
     638           0 :    const casacore::Vector<casacore::String> units = cSys.worldAxisUnits();
     639           0 :    uint wAxis = cSys.pixelAxisToWorldAxis(_axes(0));
     640           0 :    wParameters(0) = casacore::Quantity(refVal(wAxis), units(wAxis));
     641           0 :    wAxis = cSys.pixelAxisToWorldAxis(_axes(1));
     642           0 :    wParameters(1) = casacore::Quantity(refVal(wAxis), units(wAxis));
     643           0 :    SkyComponentFactory::worldWidthsToPixel(
     644           0 :        dParameters, wParameters, cSys, _axes, false
     645             :    );
     646             : 
     647             : // Create n-Dim kernel array shape
     648             : 
     649           0 :    auto kernelShape = _shapeOfKernel(kernelType, dParameters, imageIn.ndim());
     650             : 
     651             : // Create kernel array. We will fill the n-Dim array (shape non-unity
     652             : // only for pixelAxes) through its 2D casacore::Matrix incarnation. Aren't we clever.
     653           0 :    kernelArray = 0;
     654           0 :    kernelArray.resize(kernelShape);
     655           0 :    auto kernelArray2 = kernelArray.nonDegenerate(_axes);
     656           0 :    auto kernelMatrix = static_cast<casacore::Matrix<double>>(kernelArray2);
     657             : 
     658             : // Fill kernel casacore::Matrix with functional (height unity)
     659             : 
     660           0 :    return _fillKernel (kernelMatrix, kernelType, kernelShape, dParameters);
     661           0 : }
     662             : 
     663           0 : template <class T> double Image2DConvolver<T>::_dealWithRestoringBeam(
     664             :     String& brightnessUnitOut,
     665             :     GaussianBeam& beamOut, const casacore::Array<double>& kernelArray,
     666             :     double kernelVolume, const casacore::VectorKernel::KernelTypes,
     667             :     const casacore::Vector<casacore::Quantity>& parameters,
     668             :     const casacore::CoordinateSystem& cSys,
     669             :     const casacore::GaussianBeam& beamIn,
     670             :     const casacore::Unit& brightnessUnitIn, bool emitMessage
     671             : ) const {
     672           0 :     *this->_getLog() << LogOrigin(CLASS_NAME, __func__);
     673             :     // Find out if convolution axes hold the sky.  Scaling from
     674             :     // Jy/beam and Jy/pixel only really makes sense if this is true
     675             :     bool holdsOneSkyAxis;
     676           0 :     auto hasSky = casacore::CoordinateUtil::holdsSky(
     677             :         holdsOneSkyAxis, cSys, _axes.asVector()
     678             :     );
     679           0 :     if (hasSky) {
     680           0 :         const casacore::DirectionCoordinate dc = cSys.directionCoordinate();
     681           0 :         auto inc = dc.increment();
     682           0 :         auto unit = dc.worldAxisUnits();
     683           0 :         casacore::Quantity x(inc[0], unit[0]);
     684           0 :         casacore::Quantity y(inc[1], unit[1]);
     685           0 :         auto diag = sqrt(x*x + y*y);
     686           0 :         auto minAx = parameters[1];
     687           0 :         if (minAx.getUnit().startsWith("pix")) {
     688           0 :             minAx.setValue(minAx.getValue()*x.getValue());
     689           0 :             minAx.setUnit(x.getUnit());
     690             :         }
     691           0 :         if (minAx < diag) {
     692           0 :             diag.convert(minAx.getFullUnit());
     693           0 :             if (! _suppressWarnings) {
     694           0 :                 ostringstream oss;
     695           0 :                 oss << "Convolving kernel has minor axis " << minAx << " which "
     696           0 :                     << "is less than the pixel diagonal length of " << diag
     697             :                     << ". Thus, the kernel is poorly sampled, and so the "
     698             :                     << "output of this application may not be what you expect. "
     699             :                     << "You should consider increasing the kernel size or "
     700           0 :                     << "regridding the image to a smaller pixel size";
     701           0 :                 _log(oss.str(), LogIO::WARN);
     702           0 :             }
     703             :         }
     704           0 :         else if (
     705           0 :             beamIn.getMinor() < diag
     706           0 :             && beamIn != casacore::GaussianBeam::NULL_BEAM
     707             :         ) {
     708           0 :             diag.convert(beamIn.getMinor().getFullUnit());
     709           0 :             if (! _suppressWarnings) {
     710           0 :                 ostringstream oss;
     711           0 :                 oss << "Input beam has minor axis "
     712           0 :                     << beamIn.getMinor() << " which is less than the pixel "
     713           0 :                     << "diagonal length of " << diag << ". Thus, the beam is "
     714             :                     << "poorly sampled, and so the output of this application "
     715             :                     << "may not be what you expect. You should consider "
     716           0 :                     << "regridding the image to a smaller pixel size.";
     717           0 :                 _log(oss.str(), LogIO::WARN);
     718           0 :             }
     719             :         }
     720           0 :     }
     721           0 :     if (emitMessage && ! _suppressWarnings) {
     722           0 :         ostringstream oss;
     723           0 :         oss << "You are " << (hasSky ? "" : " not ") << "convolving the sky";
     724           0 :         _log(oss.str(), LogIO::NORMAL);
     725           0 :     }
     726           0 :     beamOut = casacore::GaussianBeam();
     727           0 :     auto bUnitIn = upcase(brightnessUnitIn.getName());
     728           0 :     const auto& refPix = cSys.referencePixel();
     729           0 :     double scaleFactor = 1;
     730           0 :     brightnessUnitOut = brightnessUnitIn.getName();
     731           0 :     auto autoScale = _scale <= 0;
     732           0 :     if (hasSky && bUnitIn.contains("/PIXEL")) {
     733             :         // Easy case.  Peak of convolution kernel must be unity
     734             :         // and output units are Jy/beam.  All other cases require
     735             :         // numerical convolution of beams
     736           0 :         brightnessUnitOut = "Jy/beam";
     737             :         // Exception already generated if only
     738             :         // one of major and minor in pixel units
     739           0 :         auto majAx = parameters(0);
     740           0 :         auto minAx = parameters(1);
     741           0 :         if (majAx.getFullUnit().getName() == "pix") {
     742           0 :             casacore::Vector<double> pixelParameters(5);
     743           0 :             pixelParameters(0) = refPix(_axes(0));
     744           0 :             pixelParameters(1) = refPix(_axes(1));
     745           0 :             pixelParameters(2) = parameters(0).getValue();
     746           0 :             pixelParameters(3) = parameters(1).getValue();
     747           0 :             pixelParameters(4) = parameters(2).getValue(casacore::Unit("rad"));
     748           0 :             casacore::GaussianBeam worldParameters;
     749           0 :             SkyComponentFactory::pixelWidthsToWorld(
     750             :                 worldParameters, pixelParameters,
     751           0 :                 cSys, _axes, false
     752             :             );
     753           0 :             majAx = worldParameters.getMajor();
     754           0 :             minAx = worldParameters.getMinor();
     755           0 :         }
     756           0 :         beamOut = casacore::GaussianBeam(majAx, minAx, parameters(2));
     757             :         // casacore::Input p.a. is positive N->E
     758           0 :         if (! autoScale) {
     759           0 :             scaleFactor = _scale;
     760           0 :             _log(
     761             :                 "Autoscaling is recommended for Jy/pixel convolution",
     762             :                 LogIO::WARN
     763             :             );
     764             :         }
     765           0 :     }
     766             :     else {
     767             :         // Is there an input restoring beam and are we convolving the sky to
     768             :         // which it pertains?  If not, all we can do is use user scaling or
     769             :         // normalize the convolution kernel to unit volume.  There is no point
     770             :         // to convolving the input beam either as it pertains only to the sky
     771           0 :         if (hasSky && ! beamIn.isNull()) {
     772             :             // Convert restoring beam parameters to pixels.
     773             :             // Output pa is pos +x -> +y in pixel frame.
     774           0 :             casacore::Vector<casacore::Quantity> wParameters(5);
     775           0 :             const auto refVal = cSys.referenceValue();
     776           0 :             const auto units = cSys.worldAxisUnits();
     777           0 :             auto wAxis = cSys.pixelAxisToWorldAxis(_axes(0));
     778           0 :             wParameters(0) = casacore::Quantity(refVal(wAxis), units(wAxis));
     779           0 :             wAxis = cSys.pixelAxisToWorldAxis(_axes(1));
     780           0 :             wParameters(1) = casacore::Quantity(refVal(wAxis), units(wAxis));
     781           0 :             wParameters(2) = beamIn.getMajor();
     782           0 :             wParameters(3) = beamIn.getMinor();
     783           0 :             wParameters(4) = beamIn.getPA(true);
     784           0 :             casacore::Vector<double> dParameters;
     785           0 :             SkyComponentFactory::worldWidthsToPixel(
     786           0 :                 dParameters, wParameters, cSys, _axes, false
     787             :             );
     788             :             // Create 2-D beam array shape
     789             :             // casacore::IPosition dummyAxes(2, 0, 1);
     790           0 :             auto beamShape = _shapeOfKernel(
     791             :                 casacore::VectorKernel::GAUSSIAN, dParameters, 2
     792             :             );
     793             : 
     794             :             // Create beam casacore::Matrix and fill with height unity
     795             :    
     796           0 :             casacore::Matrix<double> beamMatrixIn(beamShape(0), beamShape(1));
     797           0 :             _fillKernel(
     798             :                 beamMatrixIn, casacore::VectorKernel::GAUSSIAN, beamShape,
     799             :                 dParameters
     800             :             );
     801           0 :             auto shape = beamMatrixIn.shape();
     802             :             // Get 2-D version of convolution kenrel
     803           0 :             auto kernelArray2 = kernelArray.nonDegenerate(_axes);
     804           0 :             auto kernelMatrix
     805             :                 = static_cast<casacore::Matrix<double>>(kernelArray2);
     806             :             // Convolve input restoring beam array by convolution kernel array
     807           0 :             casacore::Matrix<double> beamMatrixOut;
     808           0 :             casacore::Convolver<double> conv(beamMatrixIn, kernelMatrix.shape());
     809           0 :             conv.linearConv(beamMatrixOut, kernelMatrix);
     810             :             // Scale kernel
     811           0 :             auto maxValOut = max(beamMatrixOut);
     812           0 :             scaleFactor = autoScale ? 1/maxValOut : _scale;
     813           0 :             Fit2D fitter(*this->_getLog());
     814           0 :             const uint n = beamMatrixOut.shape()(0);
     815           0 :             auto bParameters
     816             :                 = fitter.estimate(casacore::Fit2D::GAUSSIAN, beamMatrixOut);
     817           0 :             casacore::Vector<bool> bParameterMask(
     818             :                 bParameters.nelements(), true
     819             :             );
     820           0 :             bParameters(1) = (n-1)/2;          // x centre
     821           0 :             bParameters(2) = bParameters(1);    // y centre
     822             :             // Set range so we don't include too many pixels
     823             :             // in fit which will make it very slow
     824           0 :             fitter.addModel(
     825             :                 casacore::Fit2D::GAUSSIAN, bParameters, bParameterMask
     826             :             );
     827           0 :             casacore::Array<double> sigma;
     828           0 :             fitter.setIncludeRange(maxValOut/10.0, maxValOut+0.1);
     829           0 :             auto error = fitter.fit(beamMatrixOut, sigma);
     830           0 :             ThrowIf(
     831             :                 error == casacore::Fit2D::NOCONVERGE
     832             :                 || error == casacore::Fit2D::FAILED
     833             :                 || error == casacore::Fit2D::NOGOOD,
     834             :                 "Failed to fit the output beam"
     835             :             );
     836           0 :             auto bSolution = fitter.availableSolution();
     837             :             // Convert to world units.
     838           0 :             casacore::Vector<double> pixelParameters(5);
     839           0 :             pixelParameters(0) = refPix(_axes(0));
     840           0 :             pixelParameters(1) = refPix(_axes(1));
     841           0 :             pixelParameters(2) = bSolution(3);
     842           0 :             pixelParameters(3) = bSolution(4);
     843           0 :             pixelParameters(4) = bSolution(5);
     844           0 :             SkyComponentFactory::pixelWidthsToWorld(
     845           0 :                 beamOut, pixelParameters, cSys, _axes, false
     846             :             );
     847           0 :             if (
     848           0 :                 ! brightnessUnitIn.getName().contains(
     849           0 :                     casacore::Regex(Regex::makeCaseInsensitive("beam"))
     850             :                 )
     851             :             ) {
     852           0 :                 scaleFactor *= beamIn.getArea("arcsec2")
     853           0 :                     /beamOut.getArea("arcsec2");
     854             :             }
     855           0 :         }
     856             :         else {
     857           0 :             scaleFactor = autoScale ? 1/kernelVolume : _scale;
     858             :         }
     859             :     }
     860             :     // Put beam position angle into range
     861             :     // +/- 180 in case it has eluded us so far
     862           0 :     if (! beamOut.isNull()) {
     863           0 :         casacore::MVAngle pa(
     864           0 :             beamOut.getPA(true).getValue(casacore::Unit("rad"))
     865             :         );
     866           0 :         pa();
     867           0 :         beamOut = casacore::GaussianBeam(
     868             :             beamOut.getMajor(), beamOut.getMinor(),
     869           0 :             casacore::Quantity(pa.degree(), casacore::Unit("deg"))
     870             :         );
     871           0 :     }
     872           0 :     return scaleFactor;
     873           0 : }
     874             : 
     875           0 : template <class T> void Image2DConvolver<T>::_checkKernelParameters(
     876             :     casacore::VectorKernel::KernelTypes kernelType,
     877             :     const casacore::Vector<casacore::Quantity >& parameters
     878             : ) const {
     879           0 :     if (kernelType==casacore::VectorKernel::BOXCAR) {
     880           0 :         ThrowCc("Boxcar kernel not yet implemented");
     881             :         ThrowIf(
     882             :             parameters.nelements() != 3,
     883             :             "Boxcar kernels require 3 parameters"
     884             :         );
     885             :     }
     886           0 :     else if (kernelType==casacore::VectorKernel::GAUSSIAN) {
     887           0 :         ThrowIf(
     888             :             parameters.nelements() != 3,
     889             :             "Gaussian kernels require exactly 3 parameters"
     890             :         );
     891             :     }
     892             :     else {
     893           0 :         ThrowCc(
     894             :             "The kernel type " + casacore::VectorKernel::fromKernelType(kernelType) + " is not supported"
     895             :         );
     896             :     }
     897           0 : }
     898             : 
     899           0 : template <class T> casacore::IPosition Image2DConvolver<T>::_shapeOfKernel(
     900             :     const casacore::VectorKernel::KernelTypes kernelType,
     901             :     const casacore::Vector<double>& parameters,
     902             :     const uint ndim
     903             : ) const {
     904             : //
     905             : // Work out how big the array holding the kernel should be.
     906             : // Simplest algorithm possible. Shape is presently square.
     907             : //
     908             : 
     909             : // Find 2D shape
     910             : 
     911             :    uint n;
     912           0 :    if (kernelType==casacore::VectorKernel::GAUSSIAN) {
     913           0 :       uint n1 = _sizeOfGaussian (parameters(0), 5.0);
     914           0 :       uint n2 = _sizeOfGaussian (parameters(1), 5.0);
     915           0 :       n = max(n1,n2);
     916           0 :       if (n%2==0) n++;                                     // Make shape odd so centres well
     917           0 :    } else if (kernelType==casacore::VectorKernel::BOXCAR) {
     918           0 :       n = 2 * int(max(parameters(0), parameters(1))+0.5);
     919           0 :       if (n%2==0) n++;                                     // Make shape odd so centres well
     920             :    } else {
     921           0 :      throw(casacore::AipsError("Unrecognized kernel type"));        // Earlier checking should prevent this
     922             :    }
     923             : 
     924             : // Now find the shape for the image and slot the 2D shape in
     925             : // in the correct axis locations
     926             : 
     927           0 :    casacore::IPosition shape(ndim,1);
     928           0 :    shape(_axes(0)) = n;
     929           0 :    shape(_axes(1)) = n;
     930           0 :    return shape;
     931           0 : }
     932             :    
     933             : template <class T>
     934           0 : uint Image2DConvolver<T>::_sizeOfGaussian(
     935             :     const double width, const double nSigma
     936             : ) const {
     937             : // +/- 5sigma is a volume error of less than 6e-5%
     938             : 
     939           0 :    double sigma = width / sqrt(double(8.0) * C::ln2);
     940           0 :    return  (int(nSigma*sigma + 0.5) + 1) * 2;
     941             : }
     942             : 
     943           0 : template <class T> double Image2DConvolver<T>::_fillKernel(
     944             :     casacore::Matrix<double>& kernelMatrix,
     945             :     casacore::VectorKernel::KernelTypes kernelType,
     946             :     const casacore::IPosition& kernelShape,
     947             :     const casacore::Vector<double>& parameters
     948             : ) const {
     949             : 
     950             : // Centre functional in array (shape is odd)
     951             : 
     952           0 :    auto xCentre = double((kernelShape[_axes[0]] - 1)/2.0);
     953           0 :    auto yCentre = double((kernelShape[_axes[1]] - 1)/2.0);
     954           0 :    double height = 1;
     955             : 
     956             : // Create functional.  We only have gaussian2d functionals
     957             : // at this point.  Later the filling code can be moved out
     958             : // of the if statement
     959             : 
     960             :    double maxValKernel;
     961           0 :    double volumeKernel = 0;
     962           0 :    auto pa = parameters[2];
     963           0 :    auto ratio = parameters[1]/parameters[0];
     964           0 :    auto major = parameters[0];
     965           0 :    if (kernelType==casacore::VectorKernel::GAUSSIAN) {
     966           0 :        _fillGaussian(
     967             :            maxValKernel, volumeKernel, kernelMatrix, height,
     968             :            xCentre, yCentre, major, ratio, pa
     969             :        );
     970             :    }
     971           0 :    else if (kernelType==casacore::VectorKernel::BOXCAR) {
     972           0 :        ThrowCc("Boxcar convolution not supported");
     973             :    }
     974             :    else {
     975             :        // Earlier checking should prevent this
     976           0 :        ThrowCc("Unrecognized kernel type");
     977             :    }
     978           0 :    return volumeKernel;
     979             : }         
     980             : 
     981           0 : template <class T> void Image2DConvolver<T>::_fillGaussian(
     982             :     double& maxVal, double& volume, casacore::Matrix<double>& pixels,
     983             :     double height, double xCentre, double yCentre, double majorAxis,
     984             :     double ratio, double positionAngle
     985             : ) const {
     986             : // 
     987             : // pa positive in +x ->+y pixel coordinate frame
     988             : //
     989           0 :    uint n1 = pixels.shape()(0);
     990           0 :    uint n2 = pixels.shape()(1);
     991           0 :    AlwaysAssert(n1==n2,casacore::AipsError);
     992           0 :    positionAngle += C::pi_2;        // +y -> -x
     993           0 :    casacore::Gaussian2D<double> g2d(height, xCentre, yCentre, majorAxis,
     994             :                ratio, positionAngle);
     995           0 :    maxVal = -1.0e30;
     996           0 :    volume = 0.0;
     997           0 :    casacore::Vector<double> pos(2);
     998           0 :    for (uint j=0; j<n1; ++j) {
     999           0 :       pos[1] = j;
    1000           0 :       for (uint i=0; i<n1; ++i) {
    1001           0 :          pos[0] = i;
    1002           0 :          double val = g2d(pos);
    1003           0 :          pixels(i,j) = val;
    1004           0 :          maxVal = max(val, maxVal);
    1005           0 :          volume += val;
    1006             :       }
    1007             :    } 
    1008           0 : }
    1009             : 
    1010             : }

Generated by: LCOV version 1.16