LCOV - code coverage report
Current view: top level - imageanalysis/ImageAnalysis - StatImageCreator.cc (source / functions) Hit Total Coverage
Test: casa_coverage.info Lines: 425 474 89.7 %
Date: 2023-10-25 08:47:59 Functions: 16 16 100.0 %

          Line data    Source code
       1             : #include <imageanalysis/ImageAnalysis/StatImageCreator.h>
       2             : 
       3             : #include <imageanalysis/Annotations/AnnCenterBox.h>
       4             : #include <imageanalysis/Annotations/AnnCircle.h>
       5             : #include <casacore/lattices/LEL/LatticeExpr.h>
       6             : 
       7             : namespace casa {
       8             : 
       9          34 : StatImageCreator::StatImageCreator(
      10             :     const SPCIIF image, const Record *const region,
      11             :         const String& mask, const String& outname, Bool overwrite
      12          34 : ) : ImageStatsBase(image, region, mask, outname, overwrite) {
      13          34 :     this->_construct();
      14          68 :         auto da = _getImage()->coordinates().directionAxesNumbers();
      15          34 :     _dirAxes[0] = da[0];
      16          34 :     _dirAxes[1] = da[1];
      17          34 :         setAnchorPosition(0, 0);
      18          34 : }
      19             : 
      20           1 : void StatImageCreator::setRadius(const Quantity& radius) {
      21           1 :     ThrowIf(
      22             :         ! (radius.getUnit().startsWith("pix") || radius.isConform("rad")),
      23             :         "radius units " + radius.getUnit() + " must be pixel or angular"
      24             :     );
      25           1 :     _xlen = radius;
      26           1 :     _ylen = Quantity(0,"");
      27           1 : }
      28             : 
      29          33 : void StatImageCreator::setRectangle(
      30             :     const Quantity& xLength, const Quantity& yLength
      31             : ) {
      32          33 :     ThrowIf(
      33             :         ! (xLength.getUnit().startsWith("pix") || xLength.isConform("rad")),
      34             :         "xLength units " + xLength.getUnit() + " must be pixel or angular"
      35             :     );
      36          33 :     ThrowIf(
      37             :         ! (yLength.getUnit().startsWith("pix") || yLength.isConform("rad")),
      38             :         "xLength units " + yLength.getUnit() + " must be pixel or angular"
      39             :     );
      40          33 :     _xlen = xLength;
      41          33 :     _ylen = yLength;
      42          33 : }
      43             : 
      44          68 : void StatImageCreator::setAnchorPosition(Int x, Int y) {
      45          68 :     Vector<Double> anchorPixel(_getImage()->ndim(), 0);
      46          68 :     anchorPixel[_dirAxes[0]] = x;
      47          68 :     anchorPixel[_dirAxes[1]] = y;
      48          68 :     _anchor = _getImage()->coordinates().toWorld(anchorPixel);
      49          68 : }
      50             : 
      51          30 : void StatImageCreator::useReferencePixelAsAnchor() {
      52          60 :     const auto refPix = _getImage()->coordinates().referencePixel();
      53          30 :     Int x = round(refPix[_dirAxes[0]]);
      54          30 :     Int y = round(refPix[_dirAxes[1]]);
      55          90 :     *_getLog() << LogIO::NORMAL << LogOrigin("StatImageCreator", __func__)
      56             :         << "Anchor being set at pixel [" << x << "," << y
      57          60 :         << "], at/near image reference pixel." << LogIO::POST;
      58          30 :     setAnchorPosition(x, y);
      59          30 : }
      60             : 
      61          34 : void StatImageCreator::setGridSpacing(uInt x, uInt y) {
      62          34 :     _grid.first = x;
      63          34 :     _grid.second = y;
      64          34 : }
      65             : 
      66          34 : SPIIF StatImageCreator::compute() {
      67          34 :     static const AxesSpecifier dummyAxesSpec;
      68          34 :     *_getLog() << LogOrigin(getClass(), __func__);
      69          34 :     auto mylog = _getLog().get();
      70             :     auto subImageRO = SubImageFactory<Float>::createSubImageRO(
      71          34 :         *_getImage(), *_getRegion(), _getMask(),
      72          34 :         mylog, dummyAxesSpec, _getStretch()
      73         102 :     );
      74             :     auto subImageRW = SubImageFactory<Float>::createImage(
      75         102 :         *_getImage(), "", *_getRegion(), _getMask(),
      76          34 :         dummyAxesSpec, False, False, _getStretch()
      77         136 :     );
      78          34 :     _doMask = ! ImageMask::isAllMaskTrue(*subImageRO);
      79          68 :     const auto imshape = subImageRO->shape();
      80          34 :     const auto xshape = imshape[_dirAxes[0]];
      81          34 :     const auto yshape = imshape[_dirAxes[1]];
      82          34 :     const auto& csys = subImageRO->coordinates();
      83          68 :     auto anchorPixel = csys.toPixel(_anchor);
      84          34 :     Int xanchor = rint(anchorPixel[_dirAxes[0]]);
      85          34 :     Int yanchor = rint(anchorPixel[_dirAxes[1]]);
      86             :     // ensure xanchor and yanchor are positive
      87          34 :     if (xanchor < 0) {
      88             :         // ugh, mod of a negative number in C++ doesn't do what I want it to
      89             :         // integer division
      90           0 :         xanchor += (abs(xanchor)/_grid.first + 1)*_grid.first;
      91             :     }
      92          34 :     if (yanchor < 0) {
      93           0 :         yanchor += (abs(yanchor)/_grid.second + 1)*_grid.second;
      94             :     }
      95             :     // xstart and ystart are the pixel location in the
      96             :     // subimage of the lower left corner of the grid,
      97             :     // ie they are the pixel location of the grid point
      98             :     // with the smallest non-negative x and y values
      99          34 :     Int xstart = xanchor % _grid.first;
     100          34 :     Int ystart = yanchor % _grid.second;
     101          34 :     if (xstart < 0) {
     102           0 :         xstart += _grid.first;
     103             :     }
     104          34 :     if (ystart < 0) {
     105           0 :         ystart += _grid.second;
     106             :     }
     107          68 :     Array<Float> tmp;
     108             :     // integer division
     109          34 :     auto nxpts = (xshape - xstart)/_grid.first;
     110          34 :     if ((xshape - xstart) % _grid.first != 0) {
     111           4 :         ++nxpts;
     112             :     }
     113          34 :     auto nypts = (yshape - ystart)/ _grid.second;
     114          34 :     if ((yshape - ystart) % _grid.second != 0) {
     115           3 :         ++nypts;
     116             :     }
     117          68 :     IPosition storeShape = imshape;
     118          34 :     storeShape[_dirAxes[0]] = nxpts;
     119          34 :     storeShape[_dirAxes[1]] = nypts;
     120          34 :     auto interpolate = _grid.first > 1 || _grid.second > 1;
     121          34 :     TempImage<Float>* writeTo = dynamic_cast<TempImage<Float> *>(subImageRW.get());
     122          34 :     std::unique_ptr<TempImage<Float>> store;
     123          34 :     if (interpolate) {
     124           6 :         store.reset(new TempImage<Float>(storeShape, csys));
     125           6 :         store->set(0.0);
     126           6 :         if (_doMask) {
     127           0 :             store->attachMask(ArrayLattice<Bool>(storeShape));
     128           0 :             store->pixelMask().set(True);
     129             :         }
     130           6 :         writeTo = store.get();
     131             :     }
     132          34 :     _computeStat(*writeTo, subImageRO, nxpts, nypts, xstart, ystart);
     133          34 :     if (_doProbit) {
     134           0 :         writeTo->copyData((LatticeExpr<Float>)(*writeTo * C::probit_3_4));
     135             :     }
     136          34 :     if (interpolate) {
     137          12 :         _doInterpolation(
     138           6 :             subImageRW, *store, subImageRO,
     139             :             nxpts, nypts, xstart,  ystart
     140             :         );
     141             :     }
     142          34 :     auto res = _prepareOutputImage(*subImageRW);
     143          68 :     return res;
     144             : }
     145             : 
     146          34 : void StatImageCreator::_computeStat(
     147             :     TempImage<Float>& writeTo, SPCIIF subImage,
     148             :     uInt nxpts, uInt nypts, Int xstart, Int ystart
     149             : ) {
     150          34 :     std::shared_ptr<Array<Bool>> regionMask;
     151          34 :     uInt xBlcOff = 0;
     152          34 :     uInt yBlcOff = 0;
     153          34 :     uInt xChunkSize = 0;
     154          34 :     uInt yChunkSize = 0;
     155          34 :     _nominalChunkInfo(
     156             :         regionMask,  xBlcOff, yBlcOff,
     157             :         xChunkSize, yChunkSize, subImage
     158             :     );
     159          68 :     Array<Bool> regMaskCopy;
     160          34 :     if (regionMask) {
     161           1 :         regMaskCopy = regionMask->copy();
     162           1 :         if (! writeTo.hasPixelMask()) {
     163           2 :             ArrayLattice<Bool> mymask(writeTo.shape());
     164           1 :             mymask.set(True);
     165           1 :             writeTo.attachMask(mymask);
     166             :         }
     167             :     }
     168          68 :     auto imshape = subImage->shape();
     169          34 :     auto ndim = imshape.size();
     170          68 :     IPosition chunkShape(ndim, 1);
     171          34 :     chunkShape[_dirAxes[0]] = xChunkSize;
     172          34 :     chunkShape[_dirAxes[1]] = yChunkSize;
     173          34 :     auto xsize = imshape[_dirAxes[0]];
     174          34 :     auto ysize = imshape[_dirAxes[1]];
     175          68 :     IPosition planeShape(imshape.size(), 1);
     176          34 :     planeShape[_dirAxes[0]] = xsize;
     177          34 :     planeShape[_dirAxes[1]] = ysize;
     178             :     RO_MaskedLatticeIterator<Float> lattIter (
     179          34 :         *subImage, planeShape, True
     180          68 :     );
     181          68 :     String algName;
     182          68 :     auto alg = _getStatsAlgorithm(algName);
     183          68 :     std::set<StatisticsData::STATS> statToCompute;
     184          34 :     statToCompute.insert(_statType);
     185          34 :     alg->setStatsToCalculate(statToCompute);
     186          34 :     auto ngrid = nxpts*nypts;
     187          34 :     auto nPts = ngrid;
     188          68 :     IPosition loopAxes;
     189         165 :     for (uInt i=0; i<ndim; ++i) {
     190         131 :         if (i != _dirAxes[0] && i != _dirAxes[1]) {
     191          63 :             loopAxes.append(IPosition(1, i));
     192          63 :             nPts *= imshape[i];
     193             :         }
     194             :     }
     195          34 :     auto doCircle = _ylen.getValue() <= 0;
     196          68 :     *_getLog() << LogOrigin(getClass(), __func__) << LogIO::NORMAL
     197          34 :         << "Using ";
     198          34 :     if (doCircle) {
     199           1 :         *_getLog() << "circular region of radius " << _xlen;
     200             :     }
     201             :     else {
     202          66 :         *_getLog() << "rectangular region of specified dimensions " << _xlen
     203          33 :             << " x " << _ylen;
     204             :     }
     205          68 :     *_getLog() << " (because of centering and "
     206             :             << "rounding to use whole pixels, actual dimensions of bounding box are "
     207          34 :             << xChunkSize << " pix x " << yChunkSize << " pix";
     208          34 :     if (doCircle) {
     209           2 :         *_getLog() << " and there are " << ntrue(*regionMask)
     210           1 :             << " good pixels in the circle that are being used";
     211             :     }
     212          68 :     *_getLog() << ") to choose pixels for computing " << _statName
     213             :         << " using the " << algName << " algorithm around each of "
     214          34 :         << ngrid << " grid points in " << (nPts/ngrid) << " planes." << LogIO::POST;
     215          34 :     auto imageChunkShape = chunkShape;
     216          34 :     _doStatsLoop(
     217             :         writeTo, lattIter, nxpts, nypts, xstart, ystart,
     218             :         xBlcOff, yBlcOff, xChunkSize, yChunkSize, imshape,
     219             :         chunkShape, regionMask, alg, regMaskCopy, loopAxes, nPts
     220             :     );
     221          34 : }
     222             : 
     223          34 : void StatImageCreator::_doStatsLoop(
     224             :     TempImage<Float>& writeTo, RO_MaskedLatticeIterator<Float>& lattIter,
     225             :     uInt nxpts, uInt nypts, Int xstart, Int ystart, uInt xBlcOff,
     226             :     uInt yBlcOff, uInt xChunkSize, uInt yChunkSize, const IPosition& imshape,
     227             :     const IPosition& chunkShape, std::shared_ptr<Array<Bool>> regionMask,
     228             :     std::shared_ptr<
     229             :         StatisticsAlgorithm<
     230             :             Double, Array<Float>::const_iterator, Array<Bool>::const_iterator,
     231             :             Array<Float>::const_iterator
     232             :         >
     233             :     >& alg, const Array<Bool>& regMaskCopy, const IPosition& loopAxes, uInt nPts
     234             : ) {
     235          34 :     auto ximshape = imshape[_dirAxes[0]];
     236          34 :     auto yimshape = imshape[_dirAxes[1]];
     237          34 :     auto ndim = imshape.size();
     238          68 :     IPosition planeBlc(ndim, 0);
     239          34 :     auto& xPlaneBlc = planeBlc[_dirAxes[0]];
     240          34 :     auto& yPlaneBlc = planeBlc[_dirAxes[1]];
     241          68 :     auto planeTrc = planeBlc + chunkShape - 1;
     242          34 :     auto& xPlaneTrc = planeTrc[_dirAxes[0]];
     243          34 :     auto& yPlaneTrc = planeTrc[_dirAxes[1]];
     244          34 :     uInt nCount = 0;
     245          34 :     auto hasLoopAxes = ! loopAxes.empty();
     246          68 :     ProgressMeter pm(0, nPts, "Processing stats at grid points");
     247             :     // Vector rather than IPosition because blc values can be negative
     248          68 :     Vector<Int> blc(ndim, 0);
     249          34 :     blc[_dirAxes[0]] = xstart - xBlcOff;
     250          34 :     blc[_dirAxes[1]] = ystart - yBlcOff;
     251         209 :     for (lattIter.atStart(); ! lattIter.atEnd(); ++lattIter) {
     252         350 :         auto plane = lattIter.cursor();
     253         350 :         auto lattMask = lattIter.getMask();
     254         175 :         auto checkGrid = ! allTrue(lattMask);
     255         350 :         auto outPos = lattIter.position();
     256         175 :         auto& xOutPos = outPos[_dirAxes[0]];
     257         175 :         auto& yOutPos = outPos[_dirAxes[1]];
     258             :         // inPos is the position of the current grid point
     259             :         // This is the position in the current chunk, not the entire lattice
     260         350 :         auto inPos = plane.shape() - 1;
     261         175 :         inPos[_dirAxes[0]] = xstart;
     262         175 :         inPos[_dirAxes[1]] = ystart;
     263         175 :         auto& xInPos = inPos[_dirAxes[0]];
     264         175 :         auto& yInPos = inPos[_dirAxes[1]];
     265         175 :         Int yblc = ystart - yBlcOff;
     266       42386 :         for (uInt yCount=0; yCount<nypts; ++yCount, yblc+=_grid.second, yInPos+=_grid.second) {
     267       42211 :             yOutPos = yCount;
     268       42211 :             yPlaneBlc = max(0, yblc);
     269      126633 :             yPlaneTrc = min(
     270       42211 :                 yblc + (Int)yChunkSize - 1, (Int)yimshape - 1
     271             :             );
     272       84422 :             IPosition regMaskStart(ndim, 0);
     273       42211 :             auto& xRegMaskStart = regMaskStart[_dirAxes[0]];
     274       42211 :             auto& yRegMaskStart = regMaskStart[_dirAxes[1]];
     275       84422 :             auto regMaskLength = regMaskStart;
     276       42211 :             auto& xRegMaskLength = regMaskLength[_dirAxes[0]];
     277       42211 :             auto& yRegMaskLength = regMaskLength[_dirAxes[1]];
     278       42211 :             Bool yDoMaskSlice = False;
     279       42211 :             if (regionMask) {
     280           5 :                 regMaskLength = regionMask->shape();
     281           5 :                 if (yblc < 0) {
     282           1 :                     yRegMaskStart = -yblc;
     283           1 :                     yRegMaskLength += yblc;
     284           1 :                     yDoMaskSlice = True;
     285             :                 }
     286           4 :                 else if (yblc + yChunkSize > yimshape) {
     287           1 :                     yRegMaskLength = yimshape - yblc;
     288           1 :                     yDoMaskSlice = True;
     289             :                 }
     290             :             }
     291       42211 :             xInPos = xstart;
     292       42211 :             Int xblc = xstart - xBlcOff;
     293    10933570 :             for (uInt xCount=0; xCount<nxpts; ++xCount, xblc+=_grid.first, xInPos+=_grid.first) {
     294    10891359 :                 Float res = 0;
     295    10891359 :                 xOutPos = xCount;
     296    10891359 :                 if (checkGrid && ! lattMask(inPos)) {
     297             :                     // grid point is masked, no computation should be done
     298     3014675 :                     writeTo.putAt(res, outPos);
     299     3014675 :                     writeTo.pixelMask().putAt(False, outPos);
     300             :                 }
     301             :                 else {
     302     7876684 :                     xPlaneBlc = max(0, xblc);
     303    23630052 :                     xPlaneTrc = min(
     304     7876684 :                         xblc + (Int)xChunkSize - 1, (Int)ximshape - 1
     305             :                     );
     306     7876684 :                     std::shared_ptr<Array<Bool>> subRegionMask;
     307     7876684 :                     if (regionMask) {
     308          25 :                         auto doMaskSlice = yDoMaskSlice;
     309          25 :                         xRegMaskStart = 0;
     310          25 :                         if (xblc < 0) {
     311           5 :                             xRegMaskStart = -xblc;
     312           5 :                             xRegMaskLength = regionMask->shape()[_dirAxes[0]] + xblc;
     313           5 :                             doMaskSlice = True;
     314             :                         }
     315          20 :                         else if (xblc + xChunkSize > ximshape) {
     316           5 :                             regMaskLength[_dirAxes[0]] = ximshape - xblc;
     317           5 :                             doMaskSlice = True;
     318             :                         }
     319             :                         else {
     320          15 :                             xRegMaskLength = xChunkSize;
     321             :                         }
     322          25 :                         if (doMaskSlice) {
     323          32 :                             Slicer sl(regMaskStart, regMaskLength);
     324          16 :                             subRegionMask.reset(new Array<Bool>(regMaskCopy(sl)));
     325             :                         }
     326             :                         else {
     327           9 :                             subRegionMask.reset(new Array<Bool>(regMaskCopy));
     328             :                         }
     329             :                     }
     330    15753368 :                     auto maskChunk = lattMask(planeBlc, planeTrc).copy();
     331     7876684 :                     if (subRegionMask) {
     332          25 :                         maskChunk = maskChunk && *subRegionMask;
     333             :                     }
     334     7876684 :                     if (anyTrue(maskChunk)) {
     335     7876684 :                         auto chunk = plane(planeBlc, planeTrc);
     336     7876684 :                         if (allTrue(maskChunk)) {
     337     7876659 :                             alg->setData(chunk.begin(), chunk.size());
     338             :                         }
     339             :                         else {
     340          25 :                             alg->setData(chunk.begin(), maskChunk.begin(), chunk.size());
     341             :                         }
     342     7876684 :                         res = alg->getStatistic(_statType);
     343             :                     }
     344             :                     else {
     345             :                         // no pixels in region on which to do stats, mask grid point
     346           0 :                         writeTo.pixelMask().putAt(False, outPos);
     347             :                     }
     348     7876684 :                     writeTo.putAt(res, outPos);
     349             :                 }
     350             :             }
     351       42211 :             nCount += nxpts;
     352       42211 :             pm.update(nCount);
     353             :         }
     354         175 :         if (hasLoopAxes) {
     355         174 :             Bool done = False;
     356         174 :             uInt idx = 0;
     357         516 :             while (! done) {
     358         342 :                 auto targetAxis = loopAxes[idx];
     359         342 :                 ++blc[targetAxis];
     360         342 :                 if (blc[targetAxis] == imshape[targetAxis]) {
     361         201 :                     blc[targetAxis] = 0;
     362         201 :                     ++idx;
     363         201 :                     done = idx == loopAxes.size();
     364             :                 }
     365             :                 else {
     366         141 :                     done = True;
     367             :                 }
     368             :             }
     369             :         }
     370             :     }
     371          34 : }
     372             : 
     373             : std::shared_ptr<
     374             :     StatisticsAlgorithm<Double, Array<Float>::const_iterator,
     375             :     Array<Bool>::const_iterator>
     376             : >
     377          34 : StatImageCreator::_getStatsAlgorithm(String& algName) const {
     378          34 :     auto ac = _getAlgConf();
     379          34 :     switch(ac.algorithm) {
     380          29 :     case StatisticsData::CLASSICAL:
     381          29 :         algName = "classical";
     382          58 :         return std::shared_ptr<
     383             :             ClassicalStatistics<Double, Array<Float>::const_iterator,
     384             :             Array<Bool>::const_iterator>
     385             :         >(
     386             :             new ClassicalStatistics<
     387             :                 Double, Array<Float>::const_iterator,
     388             :                 Array<Bool>::const_iterator
     389          29 :             >()
     390          29 :         );
     391           5 :     case StatisticsData::CHAUVENETCRITERION:
     392           5 :         algName = "Chauvenet criterion/z-score";
     393          10 :         return std::shared_ptr<ChauvenetCriterionStatistics<
     394             :             Double, Array<Float>::const_iterator,
     395             :             Array<Bool>::const_iterator>
     396             :         >(
     397             :             new ChauvenetCriterionStatistics<
     398             :                 Double, Array<Float>::const_iterator,
     399             :                 Array<Bool>::const_iterator
     400             :             >(
     401             :                 ac.zs, ac.mi
     402           5 :             )
     403           5 :         );
     404           0 :     default:
     405           0 :         ThrowCc("Unsupported statistics algorithm");
     406             :     }
     407             : }
     408             : 
     409          34 : void StatImageCreator::_nominalChunkInfo(
     410             :     std::shared_ptr<Array<Bool>>& templateMask, uInt& xBlcOff, uInt& yBlcOff,
     411             :     uInt& xChunkSize, uInt& yChunkSize, SPCIIF subimage
     412             : ) const {
     413          34 :     Double xPixLength = 0;
     414          34 :     const auto& csys = subimage->coordinates();
     415          34 :     if (_xlen.getUnit().startsWith("pix")) {
     416          34 :         xPixLength = _xlen.getValue();
     417             :     }
     418             :     else {
     419           0 :         const auto& dc = csys.directionCoordinate();
     420           0 :         auto units = dc.worldAxisUnits();
     421           0 :         auto inc = dc.increment();
     422           0 :         Quantity worldPixSize(abs(inc[0]), units[0]);
     423           0 :         xPixLength = _xlen.getValue("rad")/worldPixSize.getValue("rad");
     424             :     }
     425          68 :     const auto shape = subimage->shape();
     426          34 :     ThrowIf(
     427             :         xPixLength >= shape[_dirAxes[0]] - 4,
     428             :         "x region length is nearly as large as or larger than the subimage extent of "
     429             :         + String::toString(shape[_dirAxes[0]]) + " pixels. Such a configuration is not supported"
     430             :     );
     431          34 :     ThrowIf(
     432             :         xPixLength <= 0.5,
     433             :         "x region length is only " + String::toString(xPixLength)
     434             :         + " pixels. Such a configuration is not supported"
     435             :     );
     436          34 :     Double yPixLength = xPixLength;
     437          34 :     if (_ylen.getValue() > 0) {
     438          33 :         if (_ylen.getUnit().startsWith("pix")) {
     439          33 :             yPixLength = _ylen.getValue();
     440             :         }
     441             :         else {
     442           0 :             const auto& dc = csys.directionCoordinate();
     443           0 :             auto units = dc.worldAxisUnits();
     444           0 :             auto inc = dc.increment();
     445           0 :             Quantity worldPixSize(abs(inc[1]), units[1]);
     446           0 :             yPixLength = _ylen.getValue("rad")/worldPixSize.getValue("rad");
     447             :         }
     448             :     }
     449          34 :     ThrowIf(
     450             :         yPixLength >= shape[_dirAxes[1]] - 4,
     451             :         "y region length is nearly as large as or larger than the subimage extent of "
     452             :         + String::toString(_dirAxes[1]) + " pixels. Such a configuration is not supported"
     453             :     );
     454          34 :     ThrowIf(
     455             :         yPixLength <= 0.5,
     456             :         "y region length is only " + String::toString(yPixLength)
     457             :         + " pixels. Such a configuration is not supported"
     458             :     );
     459          68 :     IPosition templateShape(shape.size(), 1);
     460          34 :     templateShape[_dirAxes[0]] = shape[_dirAxes[0]];
     461          34 :     templateShape[_dirAxes[1]] = shape[_dirAxes[1]];
     462          68 :     TempImage<Float> templateImage(templateShape, csys);
     463             :     // integer division, xq, yq must have integral values
     464          34 :     uInt xcenter = templateShape[_dirAxes[0]]/2;
     465          34 :     uInt ycenter = templateShape[_dirAxes[1]]/2;
     466          68 :     Quantity xq(xcenter, "pix");
     467          68 :     Quantity yq(ycenter, "pix");
     468          68 :     IPosition centerPix(templateShape.size(), 0);
     469          34 :     centerPix[_dirAxes[0]] = xcenter;
     470          34 :     centerPix[_dirAxes[1]] = ycenter;
     471          68 :     auto world = csys.toWorld(centerPix);
     472          34 :     static const Vector<Stokes::StokesTypes> dummyStokes;
     473          68 :     Record reg;
     474          34 :     if (_ylen.getValue() > 0) {
     475             :         AnnCenterBox box(
     476          33 :             xq, yq, _xlen, _ylen, csys, templateShape, dummyStokes
     477          33 :         );
     478          33 :         reg = box.getRegion()->toRecord("");
     479             :     }
     480             :     else {
     481             :         AnnCircle circle(
     482           1 :             xq, yq, _xlen, csys, templateShape, dummyStokes
     483           1 :         );
     484           1 :         reg = circle.getRegion()->toRecord("");
     485             :     }
     486          34 :     static const AxesSpecifier dummyAxesSpec;
     487             :     auto chunkImage = SubImageFactory<Float>::createSubImageRO(
     488             :         templateImage, reg, "", nullptr, dummyAxesSpec, False
     489          68 :     );
     490          68 :     auto blcOff = chunkImage->coordinates().toPixel(world);
     491          34 :     xBlcOff = rint(blcOff[_dirAxes[0]]);
     492          34 :     yBlcOff = rint(blcOff[_dirAxes[1]]);
     493          68 :     auto chunkShape = chunkImage->shape();
     494          34 :     xChunkSize = chunkShape[_dirAxes[0]];
     495          34 :     yChunkSize = chunkShape[_dirAxes[1]];
     496          34 :     templateMask.reset();
     497          34 :     if (chunkImage->isMasked()) {
     498          68 :         auto mask = chunkImage->getMask();
     499          34 :         if (! allTrue(mask)) {
     500           1 :             templateMask.reset(new Array<Bool>(mask));
     501             :         }
     502             :     }
     503          34 : }
     504             : 
     505           6 : void StatImageCreator::_doInterpolation(
     506             :     SPIIF output, TempImage<Float>& store,
     507             :     SPCIIF subImage, uInt nxpts, uInt nypts,
     508             :     Int xstart, Int ystart
     509             : ) const {
     510          12 :     const auto imshape = subImage->shape();
     511           6 :     const auto xshape = imshape[_dirAxes[0]];
     512           6 :     const auto yshape = imshape[_dirAxes[1]];
     513           6 :     const auto ndim = subImage->ndim();
     514          12 :     *_getLog() << LogOrigin(getClass(), __func__)
     515             :         << LogIO::NORMAL << "Interpolate using "
     516           6 :         << _interpName << " algorithm." << LogIO::POST;
     517          12 :     Matrix<Float> result(xshape, yshape);
     518          12 :     Matrix<Bool> resultMask;
     519           6 :     if (_doMask) {
     520           0 :         resultMask.resize(IPosition(2, xshape, yshape));
     521           0 :         resultMask.set(True);
     522             :     }
     523          12 :     Matrix<Float> storage(nxpts, nypts);
     524          12 :     Matrix<Bool> storeMask(nxpts, nypts);
     525           6 :     std::pair<uInt, uInt> start;
     526           6 :     start.first = xstart;
     527           6 :     start.second = ystart;
     528           6 :     if (ndim == 2) {
     529           1 :         store.get(storage);
     530           1 :         store.getMask(storeMask);
     531           1 :         _interpolate(result, resultMask, storage, storeMask, start);
     532           1 :         output->put(result);
     533           1 :         if (_doMask) {
     534           0 :             output->pixelMask().put(resultMask && subImage->pixelMask().get());
     535             :         }
     536             :     }
     537             :     else {
     538             :         // get each direction plane in the storage lattice at each chan/stokes
     539          10 :         IPosition cursorShape(ndim, 1);
     540           5 :         cursorShape[_dirAxes[0]] = nxpts;
     541           5 :         cursorShape[_dirAxes[1]] = nypts;
     542          10 :         auto axisPath = _dirAxes;
     543           5 :         axisPath.append((IPosition::otherAxes(ndim, _dirAxes)));
     544          10 :         LatticeStepper stepper(store.shape(), cursorShape, axisPath);
     545          15 :         Slicer slicer(stepper.position(), stepper.endPosition(), Slicer::endIsLast);
     546          10 :         IPosition myshape(ndim, 1);
     547           5 :         myshape[_dirAxes[0]] = xshape;
     548           5 :         myshape[_dirAxes[1]] = yshape;
     549          12 :         for (stepper.reset(); ! stepper.atEnd(); stepper++) {
     550          14 :             auto pos = stepper.position();
     551           7 :             slicer.setStart(pos);
     552           7 :             slicer.setEnd(stepper.endPosition());
     553           7 :             storage = store.getSlice(slicer, True);
     554           7 :             storeMask = store.getMaskSlice(slicer, True);
     555           7 :             _interpolate(result, resultMask, storage, storeMask, start);
     556           7 :             output->putSlice(result, pos);
     557           7 :             if (_doMask) {
     558           0 :                 output->pixelMask().putSlice(
     559           0 :                     resultMask && subImage->pixelMask().getSlice(pos, myshape, True),
     560             :                     pos
     561             :                 );
     562             :             }
     563             :         }
     564             :     }
     565           6 : }
     566             : 
     567          34 : void StatImageCreator::setInterpAlgorithm(Interpolate2D::Method alg) {
     568          34 :     switch (alg) {
     569          33 :     case Interpolate2D::CUBIC:
     570          33 :         _interpName = "CUBIC";
     571          33 :         break;
     572           0 :     case Interpolate2D::LANCZOS:
     573           0 :         _interpName = "LANCZOS";
     574           0 :         break;
     575           1 :     case Interpolate2D::LINEAR:
     576           1 :         _interpName = "LINEAR";
     577           1 :         break;
     578           0 :     case Interpolate2D::NEAREST:
     579           0 :         _interpName = "NEAREST";
     580           0 :         break;
     581           0 :     default:
     582           0 :         ThrowCc("Unhandled interpolation method " + String::toString(alg));
     583             :     }
     584          34 :     _interpolater = Interpolate2D(alg);
     585          34 : }
     586             : 
     587          34 : void StatImageCreator::setStatType(StatisticsData::STATS s) {
     588          34 :     _statType = s;
     589          34 :     _statName = StatisticsData::toString(s);
     590          34 :     _statName.upcase();
     591          34 : }
     592             : 
     593          34 : void StatImageCreator::setStatType(const String& s) {
     594          68 :     String m = s;
     595             :     StatisticsData::STATS stat;
     596          34 :     m.downcase();
     597          34 :     if (m.startsWith("i")) {
     598           0 :         stat = StatisticsData::INNER_QUARTILE_RANGE;
     599             :     }
     600          34 :     else if (m.startsWith("max")) {
     601           0 :         stat = StatisticsData::MAX;
     602             :     }
     603          34 :     else if (m.startsWith("mea")) {
     604           0 :         stat = StatisticsData::MEAN;
     605             :     }
     606          34 :     else if (m.startsWith("meda") || m.startsWith("mad") || m.startsWith("x")) {
     607           0 :         stat = StatisticsData::MEDABSDEVMED;
     608             :     }
     609          34 :     else if (m.startsWith("medi")) {
     610           1 :         stat = StatisticsData::MEDIAN;
     611             :     }
     612          33 :     else if (m.startsWith("mi")) {
     613           0 :         stat = StatisticsData::MIN;
     614             :     }
     615          33 :     else if (m.startsWith("n")) {
     616           1 :         stat = StatisticsData::NPTS;
     617             :     }
     618          32 :     else if (m.startsWith("q1")) {
     619           0 :         stat = StatisticsData::FIRST_QUARTILE;
     620             :     }
     621          32 :     else if (m.startsWith("q3")) {
     622           0 :         stat = StatisticsData::THIRD_QUARTILE;
     623             :     }
     624          32 :     else if (m.startsWith("r")) {
     625           0 :         stat = StatisticsData::RMS;
     626             :     }
     627          32 :     else if (m.startsWith("si") || m.startsWith("st")) {
     628          31 :         stat = StatisticsData::STDDEV;
     629             :     }
     630           1 :     else if (m.startsWith("sums")) {
     631           0 :         stat = StatisticsData::SUMSQ;
     632             :     }
     633           1 :     else if (m.startsWith("sum")) {
     634           1 :         stat = StatisticsData::SUM;
     635             :     }
     636           0 :     else if (m.startsWith("v")) {
     637           0 :         stat = StatisticsData::VARIANCE;
     638             :     }
     639             :     else {
     640           0 :         ThrowCc("Statistic " + s + " not supported.");
     641             :     }
     642          34 :     _doProbit = m.startsWith("x");
     643          34 :     setStatType(stat);
     644          34 : }
     645             : 
     646           8 : void StatImageCreator::_interpolate(
     647             :     Matrix<Float>& result, Matrix<Bool>& resultMask,
     648             :     const Matrix<Float>& storage,
     649             :     const Matrix<Bool>& storeMask,
     650             :     const std::pair<uInt, uInt>& start
     651             : ) const {
     652          16 :     auto shape = result.shape();
     653           8 :     auto imax = shape[0];
     654           8 :     auto jmax = shape[1];
     655             :     // x values change fastest in the iterator
     656          16 :     auto iter = result.begin();
     657          16 :     auto miter = resultMask.begin();
     658             :     // xcell and ycell are the current positions within the current
     659             :     // grid cell represented by an integer modulo pointsPerCell
     660           8 :     auto xCell = start.first == 0 ? 0 : _grid.first - start.first;
     661           8 :     auto yCell = start.second == 0 ? 0 : _grid.second - start.second;
     662           8 :     Int xStoreInt = start.first == 0 ? 0 : -1;
     663           8 :     Int yStoreInt = start.second == 0 ? 0 : -1;
     664           8 :     Double xStoreFrac = (Double)xCell/(Double)_grid.first;
     665           8 :     Double yStoreFrac = (Double)yCell/(Double)_grid.second;
     666          16 :     Vector<Double> storeLoc(2);
     667           8 :     storeLoc[0] = xStoreInt + xStoreFrac;
     668           8 :     storeLoc[1] = yStoreInt + yStoreFrac;
     669        1408 :     for (uInt j=0; j<jmax; ++j, ++yCell) {
     670        1400 :         Bool onYGrid = yCell == 0;
     671        1400 :         if (yCell == _grid.second) {
     672         404 :             yCell = 0;
     673         404 :             ++yStoreInt;
     674         404 :             yStoreFrac = 0;
     675         404 :             onYGrid = True;
     676             :         }
     677             :         else {
     678         996 :             yStoreFrac = (Double)yCell/(Double)_grid.second;
     679             :         }
     680        1400 :         storeLoc[1] = yStoreInt + yStoreFrac;
     681        1400 :         xCell = start.first == 0 ? 0 : _grid.first - start.first;
     682        1400 :         xStoreInt = start.first == 0 ? 0 : -1;
     683        1400 :         xStoreFrac = (Double)xCell/(Double)_grid.first;
     684      321400 :         for (uInt i=0; i<imax; ++i, ++xCell) {
     685      320000 :             Bool onXGrid = xCell == 0;
     686      320000 :             if (xCell == _grid.first) {
     687      103100 :                 xCell = 0;
     688      103100 :                 ++xStoreInt;
     689      103100 :                 onXGrid = True;
     690             :             }
     691      320000 :             if (onXGrid && onYGrid) {
     692             :                 // exactly on a grid point, no interpolation needed
     693             :                 // just copy value directly from storage matrix
     694       36149 :                 *iter = storage(xStoreInt, yStoreInt);
     695       36149 :                 if (_doMask) {
     696           0 :                     *miter = storeMask(xStoreInt, yStoreInt);
     697             :                 }
     698             :             }
     699             :             else {
     700      283851 :                 xStoreFrac = (Double)xCell/(Double)_grid.first;
     701      283851 :                 storeLoc[0] = xStoreInt + xStoreFrac;
     702      283851 :                 if (! _interpolater.interp(*iter, storeLoc, storage, storeMask)) {
     703           0 :                     ThrowIf(
     704             :                         ! _doMask,
     705             :                         "Logic error: bad interpolation but there is no mask to set"
     706             :                     );
     707           0 :                     *miter = False;
     708             :                 }
     709             :             }
     710      320000 :             ++iter;
     711      320000 :             if (_doMask) {
     712           0 :                 ++miter;
     713             :             }
     714             :         }
     715             :     }
     716           8 : }
     717             : 
     718             : }

Generated by: LCOV version 1.16