LCOV - code coverage report
Current view: top level - synthesis/MeasurementEquations - MatrixNACleaner.cc (source / functions) Hit Total Coverage
Test: casa_coverage.info Lines: 0 208 0.0 %
Date: 2023-10-25 08:47:59 Functions: 0 20 0.0 %

          Line data    Source code
       1             : //# Matrix Non Amnesiac Cleaner Copyright (C) 2015
       2             : //# Associated Universities, Inc. Washington DC, USA.
       3             : //#
       4             : //# This library is free software; you can redistribute it and/or modify it
       5             : //# under the terms of the GNU  General Public License as published by
       6             : //# the Free Software Foundation; either version 3 of the License, or (at your
       7             : //# option) any later version.
       8             : //#
       9             : //# This library is distributed in the hope that it will be useful, but WITHOUT
      10             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      11             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
      12             : //# License for more details.
      13             : //#
      14             : //# You should have received a copy of the GNU Library General Public License
      15             : //# along with this library; if not, write to the Free Software Foundation,
      16             : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      17             : //#
      18             : //# Correspondence concerning AIPS++ should be addressed as follows:
      19             : //#        Internet email: aips2-request@nrao.edu.
      20             : //#        Postal address: AIPS++ Project Office
      21             : //#                        National Radio Astronomy Observatory
      22             : //#                        520 Edgemont Road
      23             : //#                        Charlottesville, VA 22903-2475 USA
      24             : //#
      25             : //# $Id:  $
      26             : 
      27             : #include <casacore/casa/Arrays/Matrix.h>
      28             : #include <casacore/casa/Arrays/Cube.h>
      29             : #include <casacore/casa/Arrays/MaskedArray.h>
      30             : #include <casacore/casa/Arrays/ArrayMath.h>
      31             : #include <casacore/casa/Arrays/MaskArrMath.h>
      32             : #include <casacore/casa/Arrays/MatrixMath.h>
      33             : #include <casacore/casa/IO/ArrayIO.h>
      34             : #include <casacore/casa/BasicMath/Math.h>
      35             : #include <casacore/casa/BasicSL/Complex.h>
      36             : #include <casacore/casa/Logging/LogIO.h>
      37             : #include <casacore/casa/OS/File.h>
      38             : #include <casacore/casa/Containers/Record.h>
      39             : 
      40             : #include <casacore/lattices/LRegions/LCBox.h>
      41             : #include <casacore/casa/Arrays/Slicer.h>
      42             : #include <casacore/scimath/Mathematics/FFTServer.h>
      43             : #include <casacore/casa/OS/HostInfo.h>
      44             : #include <casacore/casa/Arrays/ArrayError.h>
      45             : #include <casacore/casa/Arrays/ArrayIter.h>
      46             : #include <casacore/casa/Arrays/VectorIter.h>
      47             : 
      48             : 
      49             : #include <casacore/casa/Utilities/GenSort.h>
      50             : #include <casacore/casa/BasicSL/String.h>
      51             : #include <casacore/casa/Utilities/Assert.h>
      52             : #include <casacore/casa/Utilities/Fallible.h>
      53             : 
      54             : #include <casacore/casa/BasicSL/Constants.h>
      55             : 
      56             : #include <casacore/casa/Logging/LogSink.h>
      57             : #include <casacore/casa/Logging/LogMessage.h>
      58             : 
      59             : #include <synthesis/MeasurementEquations/MatrixNACleaner.h>
      60             : #include <synthesis/MeasurementEquations/MatrixCleaner.h>
      61             : #include <casacore/coordinates/Coordinates/TabularCoordinate.h>
      62             : #ifdef _OPENMP
      63             : #include <omp.h>
      64             : #endif
      65             : using namespace std;
      66             : using namespace casacore;
      67             : namespace casa { //# NAMESPACE CASA - BEGIN
      68             : 
      69             :  
      70             : 
      71             :   
      72             :  
      73           0 : MatrixNACleaner::MatrixNACleaner():
      74             :   itsMask( ),
      75             :   itsDirty( ),
      76             :   itsBitPix( ),
      77             :   itsMaximumResidual(1e100),
      78             :   itsTotalFlux(0.0),
      79             :   itsSupport(3),
      80             :   psfShape_p(0),
      81             :   itsPositionPeakPsf(0),
      82             :   itsRms(0.0),
      83             :   typeOfMemory_p(2),
      84           0 :   numSigma_p(5.0)
      85             : {
      86             :   
      87             :  
      88           0 : }
      89             : 
      90             :  
      91             : 
      92           0 : MatrixNACleaner::MatrixNACleaner(const Matrix<Float> & psf,
      93           0 :                                  const Matrix<Float> &dirty, const Int memType, const Float  numSigma):
      94           0 :   itsBitPix( ), itsSupport(3), itsRms(0.0),typeOfMemory_p(memType), numSigma_p(numSigma)
      95             : {
      96           0 :   psfShape_p.resize(0, false);
      97           0 :   psfShape_p=psf.shape();
      98             :   // Check that everything is the same dimension and that none of the
      99             :   // dimensions is zero length.
     100           0 :   AlwaysAssert(psf.shape().nelements() == dirty.shape().nelements(),
     101             :                AipsError);
     102           0 :   AlwaysAssert(dirty.shape().product() != 0, AipsError);
     103             :  
     104           0 :   itsDirty=make_shared<Matrix<Float> >();
     105           0 :   itsDirty->reference(dirty);
     106           0 :   setPsf(psf);
     107           0 :   itsResidual=std::make_shared<Matrix<Float> >();
     108           0 :   itsResidual->assign(dirty);
     109           0 :   itsMask= std::make_shared<Matrix<Float> >(itsDirty->shape(), Float(0.0));
     110             :   //itsRms=rms(*itsResidual);
     111           0 :   itsMaximumResidual=1e100;
     112           0 : }
     113             : 
     114             : 
     115           0 :   Bool MatrixNACleaner::validatePsf(const Matrix<Float> & psf)
     116             : {
     117           0 :   LogIO os(LogOrigin("MatrixCleaner", "validatePsf()", WHERE));
     118             :   
     119             :   // Find the peak of the raw Psf
     120           0 :   AlwaysAssert(psf.shape().product() != 0, AipsError);
     121           0 :   Float maxPsf=0;
     122           0 :   itsPositionPeakPsf=IPosition(psf.shape().nelements(), 0);
     123           0 :   Int psfSupport = max(psf.shape().asVector())/2;
     124           0 :   MatrixCleaner::findPSFMaxAbs(psf, maxPsf, itsPositionPeakPsf, psfSupport);
     125           0 :   os << "Peak of PSF = " << maxPsf << " at " << itsPositionPeakPsf
     126           0 :      << LogIO::POST;
     127           0 :   return true;
     128             : }
     129             :   
     130             : 
     131           0 : void MatrixNACleaner::setPsf(const Matrix<Float>& psf){
     132           0 :   AlwaysAssert(validatePsf(psf), AipsError);
     133           0 :   psfShape_p.resize(0, false);
     134           0 :   psfShape_p=psf.shape();
     135           0 :   itsPsf=make_shared<Matrix<Float> >();
     136           0 :   itsPsf->reference(psf);
     137             :   //cout << "shapes " << itsXfr->shape() << " psf " << psf.shape() << endl;
     138           0 : }
     139             : 
     140           0 : MatrixNACleaner::MatrixNACleaner(const MatrixNACleaner & other)
     141             :    
     142             : {
     143           0 :   operator=(other);
     144           0 : }
     145             : 
     146           0 : MatrixNACleaner & MatrixNACleaner::operator=(const MatrixNACleaner & other) {
     147           0 :   if (this != &other) {
     148             :     
     149             :    
     150           0 :     itsMask = other.itsMask;
     151           0 :     itsDirty = other.itsDirty;
     152           0 :     itsResidual=other.itsResidual;
     153           0 :     itsBitPix=other.itsBitPix;
     154           0 :     itsTotalFlux=other.itsTotalFlux;
     155           0 :     psfShape_p.resize(0, false);
     156           0 :     psfShape_p=other.psfShape_p;
     157           0 :     itsSupport=other.itsSupport;
     158           0 :     itsRms=other.itsRms;
     159           0 :     typeOfMemory_p=other.typeOfMemory_p;
     160             :     
     161             :   }
     162           0 :   return *this;
     163             : }
     164             : 
     165           0 : MatrixNACleaner::~MatrixNACleaner()
     166             : {
     167             :   
     168             :   
     169           0 : }
     170             : 
     171             : 
     172             : 
     173             : 
     174             : // add a mask image
     175           0 :   void MatrixNACleaner::setMask(Matrix<Float> & mask) 
     176             : {
     177             :  
     178             : 
     179             :   //cerr << "Mask Shape " << mask.shape() << endl;
     180             :   // This is not needed after the first steps
     181           0 :   itsMask = std::make_shared<Matrix<Float> >(mask.shape());
     182           0 :   itsMask->reference(mask);
     183             :  
     184             : 
     185           0 : }
     186             :  
     187           0 : void MatrixNACleaner::setPixFlag(const Matrix<Bool> & bitpix) 
     188             : {
     189             :  
     190             : 
     191             :   //cerr << "Mask Shape " << mask.shape() << endl;
     192             :   // This is not needed after the first steps
     193           0 :   itsBitPix = std::make_shared<Matrix<Bool> >(bitpix.shape());
     194           0 :   itsBitPix->reference(bitpix);
     195             :  
     196             : 
     197           0 : }
     198             : 
     199             : 
     200             : // Set up the control parameters
     201           0 : void MatrixNACleaner::setcontrol(
     202             :                                    const Int niter,
     203             :                                    const Float gain,
     204             :                                    const Quantity& aThreshold,
     205             :                                    const Int masksupp, const Int memType, const Float numSigma)
     206             : {
     207             :   
     208           0 :   itsMaxNiter=niter;
     209           0 :   itsGain=gain;
     210           0 :   itsThreshold=aThreshold;
     211           0 :   itsSupport=masksupp;
     212           0 :   typeOfMemory_p=memType;
     213           0 :   numSigma_p=numSigma;
     214             :   
     215           0 : }
     216             : 
     217             : 
     218             : 
     219             : 
     220             : // Do the clean as set up
     221           0 : Int MatrixNACleaner::clean(Matrix<Float>& model)
     222             : {
     223           0 :   AlwaysAssert(model.shape()==itsDirty->shape(), AipsError);
     224             : 
     225           0 :   LogIO os(LogOrigin("MatrixCleaner", "clean()", WHERE));
     226             : 
     227             : 
     228           0 :   if(typeOfMemory_p==0)
     229           0 :     f_p=std::bind(&MatrixNACleaner::amnesiac, this, std::placeholders::_1);
     230           0 :   else if(typeOfMemory_p==1)
     231           0 :     f_p=std::bind(&MatrixNACleaner::weak, this, std::placeholders::_1);
     232           0 :   else if(typeOfMemory_p==3)
     233           0 :     f_p=std::bind(&MatrixNACleaner::strong, this, std::placeholders::_1);
     234             :   else
     235           0 :     f_p=std::bind(&MatrixNACleaner::medium, this, std::placeholders::_1);
     236           0 :   Int converged=0;
     237           0 :   itsTotalFlux=0.0;
     238           0 :    if(!itsBitPix){
     239           0 :      itsRms=rms(*itsResidual);
     240             :    }else{
     241           0 :      itsRms=rms(MaskedArray<Float>(*itsResidual, *itsBitPix));
     242             :    }
     243             : 
     244             :   // Define a subregion for the inner quarter
     245           0 :   IPosition blcDirty(model.shape().nelements(), 0);
     246           0 :   IPosition trcDirty(model.shape()-1);
     247             : 
     248           0 :   if(!itsMask){
     249           0 :     itsMask=make_shared<Matrix<Float> >(model.shape(), Float(0.0));
     250             :   } 
     251             : 
     252             :     
     253             :    
     254             :  
     255             :  
     256           0 :   os << "Starting iteration"<< LogIO::POST;
     257           0 :   Float maxima=0.0;
     258           0 :   IPosition posMaximum;
     259           0 :   itsIteration = 0;
     260           0 :   for (Int ii=0; ii < itsMaxNiter; ii++) {
     261           0 :     itsIteration++;
     262             :  
     263             :         
     264             :         
     265             :         
     266           0 :     if(!findMaxAbsMask(*itsResidual, *itsMask,
     267             :                        maxima, posMaximum, itsSupport))
     268           0 :       break;
     269             : 
     270             :         
     271             : 
     272             :    
     273             :    
     274             : 
     275             :     // Now add to the total flux
     276           0 :     itsTotalFlux += (maxima*itsGain);
     277             :     
     278             : 
     279             : 
     280           0 :     if(ii==0 ) {
     281           0 :       itsMaximumResidual=abs(maxima);
     282           0 :       os << "Initial maximum residual is " << itsMaximumResidual;
     283             :      
     284           0 :       os << LogIO::POST;
     285             :     }
     286             : 
     287             :     // Various ways of stopping:
     288             :     //    1. stop if below threshold
     289           0 :     if(abs(maxima)<threshold() ) {
     290           0 :       os << "Reached stopping threshold " << threshold() << " at iteration "<<
     291           0 :             ii << LogIO::POST;
     292           0 :       os << "Optimum flux is " << abs(maxima) << LogIO::POST;
     293           0 :       converged = 1;
     294           0 :       break;
     295             :     }
     296             :     
     297             :     /*
     298             :     if(progress) {
     299             :       progress->info(false, itsIteration, itsMaxNiter, maxima,
     300             :                      posMaximum, itsStrengthOptimum,
     301             :                      optimumScale, positionOptimum,
     302             :                      totalFlux, totalFluxScale,
     303             :                      itsJustStarting );
     304             :       itsJustStarting = false;
     305             :       } else*/ {
     306           0 :       if (itsIteration ==   1) {
     307           0 :           os << "iteration    MaximumResidual   CleanedFlux" << LogIO::POST;
     308             :       }
     309           0 :       if ((itsIteration % (itsMaxNiter/10 > 0 ? itsMaxNiter/10 : 1)) == 0) {
     310             :         //Good time to redo rms if necessary
     311           0 :         if(abs(maxima) >  3*itsRms){
     312           0 :           if(!itsBitPix){
     313           0 :             itsRms=rms(*itsResidual);
     314             :           }else{
     315           0 :             itsRms=rms(MaskedArray<Float>(*itsResidual, *itsBitPix));
     316             :           }
     317             :         }
     318             :         os << itsIteration <<"      "<< maxima<<"      "
     319           0 :            << itsTotalFlux << " rms " << itsRms << LogIO::POST;
     320             :       }
     321             :     }
     322             : 
     323             :    
     324             : 
     325             :     // Continuing: subtract the peak that we found from all dirty images
     326             :     // Define a subregion so that that the peak is centered
     327           0 :     IPosition support(model.shape());
     328             :     
     329             : 
     330           0 :     IPosition inc(model.shape().nelements(), 1);
     331             :     //cout << "support " << support.asVector()  << endl;
     332             :     //support(0)=1024;
     333             :     //support(1)=1024;
     334             :     //support(0)=min(Int(support(0)), Int(trcDirty(0)-blcDirty(0)));
     335             :     //support(1)=min(Int(support(1)), Int(trcDirty(1)-blcDirty(1)));
     336             :     // support(0)=min(Int(support(0)), (trcDirty(0)-blcDirty(0)+
     337             :     //                          Int(2*abs(positionOptimum(0)-blcDirty(0)/2.0-trcDirty(0)/2.0))));
     338             :     //support(1)=min(Int(support(1)), (trcDirty(1)-blcDirty(1)+
     339             :     //                          Int(2*abs(positionOptimum(1)-blcDirty(1)/2.0-trcDirty(1)/2.0))));
     340             : 
     341           0 :     IPosition blc(posMaximum-support/2);
     342           0 :     IPosition trc(posMaximum+support/2-1);
     343           0 :     LCBox::verify(blc, trc, inc, model.shape());
     344             :     
     345             :     //cout << "blc " << blc.asVector() << " trc " << trc.asVector() << endl;
     346             : 
     347           0 :     IPosition blcPsf(blc+itsPositionPeakPsf-posMaximum);
     348           0 :     IPosition trcPsf(trc+itsPositionPeakPsf-posMaximum);
     349           0 :     LCBox::verify(blcPsf, trcPsf, inc, model.shape());
     350           0 :     makeBoxesSameSize(blc,trc,blcPsf,trcPsf);
     351             :     // cout << "blcPsf " << blcPsf.asVector() << " trcPsf " << trcPsf.asVector() << endl;
     352             :     //cout << "blc " << blc.asVector() << " trc " << trc.asVector() << endl;
     353             :     //    LCBox subRegion(blc, trc, model.shape());
     354             :     //  LCBox subRegionPsf(blcPsf, trcPsf, model.shape());
     355             :     
     356             :    
     357           0 :     Matrix<Float> resSub=(*itsResidual)(blc,trc);
     358             :     
     359             :  
     360             :     // Now do the addition to the model image....
     361           0 :     model(posMaximum) += itsGain*maxima;
     362             : 
     363             :                         
     364             :   
     365             :       
     366             :         
     367           0 :     Matrix<Float> psfSub=(*itsPsf)(blcPsf, trcPsf);
     368           0 :     resSub -= itsGain*maxima*psfSub;
     369             :             
     370             :       
     371             :     
     372             :   }
     373             :   // End of iteration
     374             : 
     375             :  
     376             :     os << LogIO::NORMAL
     377           0 :        << "  " << "  Total Flux  " << itsTotalFlux
     378           0 :        << LogIO::POST;
     379             :   
     380             :   // Finish off the plot, etc.
     381             :   /*
     382             :   if(progress) {
     383             :     progress->info(true, itsIteration, itsMaxNiter, maxima, posMaximum,
     384             :                    itsStrengthOptimum,
     385             :                    optimumScale, positionOptimum,
     386             :                    totalFlux, totalFluxScale);
     387             :   }
     388             :   */
     389             : 
     390           0 :   if(!converged) {
     391           0 :     os << "Failed to reach stopping threshold" << LogIO::POST;
     392             :   }
     393             : 
     394           0 :   return converged;
     395             : }
     396             : 
     397             : 
     398             : 
     399             : 
     400             : 
     401             : 
     402           0 : Bool MatrixNACleaner::findMaxAbsMask(const Matrix<Float>& lattice,
     403             :                                    Matrix<Float>& mask,
     404             :                                               Float& peakval,
     405             :                                    IPosition& posPeak, const Int support)
     406             : {
     407             : 
     408             :   /*
     409             :     Here is the secret of non masking algorithm
     410             :     find peak of mask*resid -- m1*p1   p1 (value of resid at that peak)
     411             :     find peak of resid -- p2
     412             :     if p2 < m1*p1
     413             :        return p1 
     414             :        modify box (using user given support) at pos of p1 in mask to be f(p1) or m@p1 which ever is higher
     415             :    else 
     416             :         return p2
     417             :          modify box at pos of p2 in mask to be f(p2) or m@p2  which ever is higher
     418             :     
     419             :    stop modifying mask when peak reaches 5 (or user settable) sigma
     420             :    
     421             :    f(p) is a memory function...f(p)=1 implies no memory or normal clean
     422             :    default right now we are using f(p)=p
     423             :     a weak memory can be f(p)=1+ k*p  ( k << 1)  or p ^0.1
     424             :     a strong memory can be f(p)=p^2
     425             :    */
     426             : 
     427             :   //cerr << "SHAPES " << lattice.shape() << "  " << mask.shape() << endl;
     428           0 :   Matrix<Float> wgtresid=lattice*mask;
     429             :   
     430           0 :   IPosition posMaxWgt = IPosition(lattice.shape().nelements(), 0);
     431           0 :   Float maxValWgt=0.0;
     432             :   Float minValWgt;
     433           0 :   IPosition posMinWgt(lattice.shape().nelements(), 0);
     434           0 :   if(!itsBitPix)
     435           0 :     minMax(minValWgt, maxValWgt, posMinWgt, posMaxWgt, wgtresid);
     436             :   else
     437           0 :      minMax(minValWgt, maxValWgt, posMinWgt, posMaxWgt, MaskedArray<Float>(wgtresid, *itsBitPix));
     438           0 :   if(abs(minValWgt) > abs(maxValWgt)){
     439           0 :     posMaxWgt=posMinWgt;
     440           0 :     maxValWgt=minValWgt;
     441             :   }
     442           0 :   IPosition posMaxRes = IPosition(lattice.shape().nelements(), 0);
     443           0 :   Float maxValRes=0.0;
     444             :   Float minValRes;
     445           0 :   IPosition posMinRes(lattice.shape().nelements(), 0);
     446           0 :   if(!itsBitPix)
     447           0 :     minMax(minValRes, maxValRes, posMinRes, posMaxRes, lattice);
     448             :   else
     449           0 :     minMax(minValRes, maxValRes, posMinRes, posMaxRes,  MaskedArray<Float>(lattice, *itsBitPix));
     450           0 :   if(abs(minValRes) > abs(maxValRes)){
     451           0 :     posMaxRes=posMinRes;
     452           0 :     maxValRes=minValRes;
     453             :   }
     454           0 :   if(abs(maxValWgt) > abs(maxValRes)){
     455           0 :     posMaxRes=posMaxWgt;
     456           0 :     maxValRes=lattice(posMaxRes);
     457             :   }
     458           0 :   posPeak=posMaxRes;
     459           0 :   peakval=maxValRes;
     460           0 :   Int nx=lattice.shape()(0);
     461           0 :   Int ny=lattice.shape()(1);
     462           0 :   Float apeakval=abs(peakval);
     463           0 :   if(apeakval > numSigma_p*itsRms){
     464           0 :     for (Int y=-support; y < support+1; ++y){
     465           0 :       if(((posPeak[1]+y) >=0) && ((posPeak[1]+y) < ny)){
     466           0 :         for(Int x=-support; x < support+1; ++x){
     467           0 :           if(((posPeak[0]+x) >=0) && ((posPeak[0]+x) < nx))
     468           0 :             if(mask(x+posPeak[0], y+posPeak[1]) < f_p(apeakval))
     469           0 :               mask(x+posPeak[0], y+posPeak[1])=f_p(apeakval);
     470             :         }
     471             :       }
     472             :     }
     473             :   }
     474           0 :   itsMaximumResidual=peakval;
     475             :   //if(peakval > 2*itsRms)
     476             :   //  return true;
     477             : 
     478           0 :   return true;
     479             : }
     480           0 :   Float  MatrixNACleaner::amnesiac(const Float& ){
     481           0 :     return 1.0;
     482             :   }
     483             : 
     484           0 :   Float MatrixNACleaner::weak(const Float& v){
     485           0 :     return 1.0+0.1*v;
     486             :   }
     487             : 
     488           0 :   Float MatrixNACleaner::medium(const Float& v){
     489           0 :     return v;
     490             : 
     491             :   }
     492           0 :   Float MatrixNACleaner::strong(const Float& v){
     493           0 :     return v*v;
     494             :   }
     495             : 
     496             : 
     497           0 : void MatrixNACleaner::setDirty(const Matrix<Float>& dirty){
     498             : 
     499           0 :   itsDirty=make_shared<Matrix<Float> >(dirty.shape());
     500           0 :   itsDirty->reference(dirty);
     501           0 :    itsResidual=make_shared<Matrix<Float> >(dirty.shape());
     502           0 :   itsResidual->assign(dirty);
     503           0 :   itsRms=rms(*itsResidual);
     504             :   
     505             :   
     506             :   
     507             : 
     508           0 : } 
     509             : 
     510             : //Define the scales without doing anything else
     511             : // user will call make makePsfScales and makeDirtyScales like an adult in the know
     512             : 
     513             : 
     514             : 
     515             : 
     516           0 : void MatrixNACleaner::unsetMask()
     517             : {
     518             :  
     519           0 :   if(!itsMask)
     520           0 :     itsMask=nullptr;
     521             :   
     522           0 : }
     523             : 
     524             : 
     525             : 
     526             : 
     527             : 
     528             : 
     529             : 
     530             : 
     531           0 : Float MatrixNACleaner::threshold() const
     532             : {
     533           0 :   return Float(itsThreshold.getValue("Jy"));
     534             : }
     535             : 
     536             : 
     537             : 
     538           0 : void MatrixNACleaner::makeBoxesSameSize(IPosition& blc1, IPosition& trc1, 
     539             :                   IPosition &blc2, IPosition& trc2)
     540             : {
     541           0 :   const IPosition shape1 = trc1 - blc1;
     542           0 :   const IPosition shape2 = trc2 - blc2;
     543             : 
     544           0 :   AlwaysAssert(shape1.nelements() == shape2.nelements(), AipsError);
     545             :   
     546           0 :   if (shape1 == shape2) {
     547           0 :       return;
     548             :   }
     549           0 :   for (uInt i=0;i<shape1.nelements();++i) {
     550           0 :        Int minLength = shape1[i];
     551           0 :        if (shape2[i]<minLength) {
     552           0 :            minLength = shape2[i];
     553             :        }
     554           0 :        AlwaysAssert(minLength>=0, AipsError);
     555             :        //if (minLength % 2 != 0) {
     556             :            // if the number of pixels is odd, ensure that the centre stays 
     557             :            // the same by making this number even
     558             :            //--minLength; // this code is a mistake and should be removed
     559             :        //}
     560           0 :        const Int increment1 = shape1[i] - minLength;
     561           0 :        const Int increment2 = shape2[i] - minLength;
     562           0 :        blc1[i] += increment1/2;
     563           0 :        trc1[i] -= increment1/2 + (increment1 % 2 != 0 ? 1 : 0);
     564           0 :        blc2[i] += increment2/2;
     565           0 :        trc2[i] -= increment2/2 + (increment2 % 2 != 0 ? 1 : 0);
     566             :   }
     567             : }
     568             : 
     569             : 
     570             : 
     571             : } //# NAMESPACE CASA - END
     572             : 

Generated by: LCOV version 1.16