Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

pixel_array.h

Go to the documentation of this file.
00001 /*
00002   Arroyo - software for the simulation of electromagnetic wave propagation
00003   through turbulence and optics.
00004 
00005   Copyright (c) 2000-2004 California Institute of Technology.  Written by
00006   Dr. Matthew Britton.  For comments or questions about this software,
00007   please contact the author at mbritton@astro.caltech.edu.
00008 
00009   This program is free software; you can redistribute it and/or modify it
00010   under the terms of the GNU General Public License as  published by the
00011   Free Software Foundation; either version 2 of the License, or (at your
00012   option) any later version.
00013 
00014   This program is provided "as is" and distributed in the hope that it
00015   will be useful, but WITHOUT ANY WARRANTY; without even the implied
00016   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  In no
00017   event shall California Institute of Technology be liable to any party
00018   for direct, indirect, special, incidental or consequential damages,
00019   including lost profits, arising out of the use of this software and its
00020   documentation, even if the California Institute of Technology has been
00021   advised of the possibility of such damage.   The California Institute of
00022   Technology has no obligation to provide maintenance, support, updates,
00023   enhancements or modifications.  See the GNU General Public License for
00024   more details.
00025 
00026   You should have received a copy of the GNU General Public License along
00027   with this program; if not, write to the Free Software
00028   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00029 */
00030 
00031 #ifndef PIXEL_ARRAY_H
00032 #define PIXEL_ARRAY_H
00033 
00034 #include <vector>
00035 #include <algorithm>
00036 #include <iomanip>
00037 #include <cmath>
00038 #include <cfloat>
00039 #include <iostream>
00040 #include <fstream>
00041 #include "AO_algo.h"
00042 #include "colormap.h"
00043 #include "iofits.h"
00044 #include "fits_header_data.h"
00045 #include "fft_manager.h"
00046 
00047 using namespace std;
00048 
00049 namespace Arroyo {
00050 
00051   using std::string;
00052   using std::vector;
00053   using std::cerr;
00054   using std::endl;
00055   using std::ostream;
00056     
00057   /* forward declarations */
00058   template <class T> class pixel_array;
00059   template <class T> pixel_array<T> operator + (const pixel_array<T> &p1,
00060                                                 const pixel_array<T> &p2);
00061   template <class T> pixel_array<T> operator - (const pixel_array<T> &p1,
00062                                                 const pixel_array<T> &p2);
00063   template <class T> pixel_array<T> operator * (const pixel_array<T> &p1,
00064                                                 const pixel_array<T> &p2);
00065   template <class T> pixel_array<T> operator / (const pixel_array<T> &p1,
00066                                                 const pixel_array<T> &p2);
00067   template <class T> pixel_array<T> operator + (const pixel_array<T> &p1,
00068                                                 double & fac);
00069   template <class T> pixel_array<T> operator - (const pixel_array<T> &p1,
00070                                                 double & fac);
00071   template <class T> pixel_array<T> operator * (const pixel_array<T> &p1,
00072                                                 double & fac);
00073   template <class T> pixel_array<T> operator / (const pixel_array<T> &p1,
00074                                                 double & fac);
00075   template <class T> bool operator == (const pixel_array<T> &p1,
00076                                        const pixel_array<T> &p2);
00077   template <class T> bool operator != (const pixel_array<T> &p1,
00078                                        const pixel_array<T> &p2);
00079 
00084 
00085   template<class T> 
00086     class pixel_array {
00087 
00088     private:
00089 
00098     void simple_rotate_and_shift_by_fft(double dx, double dy,
00099                                         double angle, bool window);
00100 
00101     mutable int private_nelem;
00102 
00103     protected:
00104 
00106     vector<long> axes;
00107   
00109     T * pixeldata;
00110 
00112     float * pixelwts;
00113 
00120     void set_axes(const vector<long> & in_axes);
00121 
00126     void allocate_weights(double wt);
00127 
00130     void deallocate_weights();
00131 
00134     double normalize_by_wts();
00135 
00140     void decimate(int nadd);
00141 
00144     template<class U>
00145       void flag_zero_wts(const pixel_array<U> & pixarr);
00146     
00151     void mask();
00152 
00155     template<class U>
00156       void mask(const pixel_array<U> * pixarr);
00157 
00160     pixel_array(const iofits & iof);
00161 
00162     public:
00163 
00166     pixel_array();
00167 
00172     pixel_array(const pixel_array<T> & pixarr);
00173 
00183     template<class U>
00184       pixel_array(const pixel_array<U> & pixarr,
00185                   const vector<long> & pixel_limits);
00186 
00192     pixel_array(const vector<long> & in_axes, 
00193                 const T * data = NULL, 
00194                 const float * wts = NULL);
00195 
00198     ~pixel_array();  
00199 
00202     pixel_array<T> & operator = (const pixel_array<T> & pixarr);
00203 
00206     virtual void read(const iofits & iof);
00207 
00210     virtual void write(iofits & iof) const;
00211 
00214     void write_to_ppm(double min, double max,
00215                       bool logscale,
00216                       bool colorbar,
00217                       colormap * cmap, 
00218                       const char * filename,
00219                       long min_dimen = -1) const;
00220 
00223     inline int total_space() const {
00224 
00225       if(this->private_nelem>0){
00226         return(this->private_nelem);
00227       }
00228       //cout << "\tATS " << this->private_nelem << "\t";
00229       int naxes = axes.size();
00230       if(naxes == 0) 
00231         this->private_nelem=0;
00232       else {
00233         this->private_nelem = 1;
00234         for(int i=0; i<naxes; i++)
00235           this->private_nelem *= axes[i];
00236       }
00237       //cout << "\tBTS " << this->private_nelem << endl;
00238       return(this->private_nelem);
00239     };
00240 
00243     bool weights_allocated() const;
00244 
00247     vector<long> get_axes() const {return(axes);};
00248 
00253     template<class U>
00254       void copyfrom(const pixel_array<U> & pixarr);
00255 
00258     void print_axes(ostream & os) const;
00259 
00268     T data(long n) const {
00269       if(n<0 || n>=this->total_space()){
00270         cerr << "pixel_array::data error - element " << n
00271              << " out of range\n";
00272         throw(string("pixel_array::data"));
00273       }
00274       return(pixeldata[n]);
00275     };
00276 
00285     void set_data(long n, T val) const {
00286       if(n<0 || n>=this->total_space()){
00287         cerr << "pixel_array::set_data error - element " << n
00288              << " out of range\n";
00289         throw(string("pixel_array::set_data"));
00290       }
00291       pixeldata[n] = val;
00292     };
00293 
00296     float wt(long elem) const{
00297       if(pixelwts!=NULL) return(*(pixelwts+elem));
00298       else {
00299         cerr << "pixel_array::wt error - weights not allocated\n";
00300         throw(string("pixel_array::wt"));
00301       }
00302     };
00303   
00306     void min_and_max(double & min, double & max) const;
00307 
00310     void min_and_max(double & min, vector<int> & minpixel, 
00311                      double & max, vector<int> & maxpixel) const;
00312 
00315     void min_and_max(double & min, vector<int> & minpixel, 
00316                      double & max, vector<int> & maxpixel,
00317                      vector<int> axis_0_limits, 
00318                      vector<int> axis_1_limits) const;
00319 
00322     void flip_x();
00323   
00326     void flip_y();
00327   
00330     void flip_xy();
00331 
00334     void flip_45();
00335 
00339     void pad_array(int npad, double value = 0);
00340 
00343     void clip_array(int nclip);
00344 
00347     void shift_by_fft(double dx, double dy);
00348 
00355     void rotate_by_fft(double angle, bool window=true);
00356 
00366     void rotate_and_shift_by_fft(double dx, 
00367                                  double dy,
00368                                  double angle, 
00369                                  bool window=true);
00370 
00375     void owen_makedon_rotate_and_shift_by_fft(double dx, 
00376                                               double dy, 
00377                                               double angle);
00378 
00382     pixel_array<T> cross_correlate(const pixel_array<T> & pixarr) const;
00383 
00390     void offset(const pixel_array<T> & pixarr,
00391                 vector<double> & offsets, 
00392                 long range = -1) const;
00393 
00396     template<class U, class V> 
00397       friend pixel_array<U> & operator+=(pixel_array<U> & lhs,
00398                                          const pixel_array<V> & rhs);
00399 
00402     template<class U, class V> 
00403       friend pixel_array<U> & operator-=(pixel_array<U> & lhs,
00404                                          const pixel_array<V> & rhs);
00405 
00408     template<class U, class V>  
00409       friend pixel_array<U> & operator*=(pixel_array<U> & lhs,
00410                                          const pixel_array<V> & rhs);
00411 
00414     template<class U, class V>
00415       friend pixel_array<U> & operator/=(pixel_array<U> & lhs,
00416                                          const pixel_array<V> & rhs);
00417 
00420     virtual pixel_array<T> & operator += (const double & fac);
00421 
00424     virtual pixel_array<T> & operator -= (const double & fac);
00425 
00428     virtual pixel_array<T> & operator *= (const double & fac);
00429 
00432     virtual pixel_array<T> & operator /= (const double & fac);
00433 
00436     friend bool operator ==(const pixel_array<T> &p1, const pixel_array<T> &p2){
00437 
00438       if(p1.axes.size()!=p2.axes.size()) return(0);
00439 
00440       if((p1.pixelwts!=NULL && p2.pixelwts==NULL) ||
00441          (p1.pixelwts==NULL && p2.pixelwts!=NULL)){
00442         return(0);
00443       }
00444 
00445       int nelem = 1;
00446       for(int i=0; i<p1.axes.size(); i++){
00447         if(p1.axes[i]!=p2.axes[i]) 
00448           return(0);
00449         nelem *= p1.axes[i];
00450       }
00451 
00452       for(int i=0; i<nelem; i++)
00453         if(p1.pixeldata[i]!=p2.pixeldata[i]) return(0);
00454 
00455       if(p1.pixelwts!=NULL)
00456         for(int i=0; i<nelem; i++)
00457           if(p1.pixelwts[i]!=p2.pixelwts[i]) return(0);
00458 
00459       return(1);
00460     };
00461 
00463     static int verbose_level;
00464 
00465   };
00466 
00467   template<class T>
00468     int pixel_array<T>::verbose_level = 0;
00469 
00470   template<class T>
00471     void pixel_array<T>::set_axes(const vector<long> & in_axes){
00472     
00473     int nelem = in_axes.size()==0 ? 0 : 1;
00474     for(int i=0; i<in_axes.size(); i++)
00475       nelem *= in_axes[i];
00476 
00477     if(nelem<0){
00478       cerr << "pixel_array::set_axes error - "
00479            << "total number of elements " << nelem
00480            << " less than zero\n";
00481       throw(string("pixel_array::set_axes"));
00482     }
00483 
00484     if(pixel_array<T>::verbose_level) 
00485       cout << "pixel_array::set_axes - have " << total_space() 
00486            << " and need " << nelem << endl;
00487 
00488     // Check to see if we can avoid memory reallocation
00489     if(total_space() != nelem){
00490       
00491       axes = in_axes;
00492 
00493       // reset this flag so that this will be recomputed after
00494       // axes have been set to in_axes
00495       this->private_nelem = -1;
00496 
00497       if(pixel_array<T>::verbose_level) 
00498         cout << "pixel_array::set_axes - reallocating memory\n";
00499       
00500       bool wts_initialized = this->weights_allocated();
00501 
00502       if(pixeldata!=NULL) 
00503         delete [] pixeldata;
00504       if(pixelwts!=NULL){
00505         delete [] pixelwts;
00506         pixelwts = NULL;
00507       }
00508 
00509       if(nelem==0){
00510         pixeldata=NULL;
00511         pixelwts=NULL;
00512         axes = in_axes;
00513 
00514         return;
00515       }
00516 
00517       try{
00518         if(pixel_array<T>::verbose_level) 
00519           cout << "pixel_array::set_axes - allocating data: nelems " 
00520                << nelem << endl;
00521         pixeldata = new T[nelem];
00522       } catch(...){
00523         cerr << "pixel_array::set_axes - error reallocating space for data array\n";
00524         throw(string("pixel_array::set_axes"));
00525       }
00526       
00527       for(int i=0; i<nelem; i++)
00528         pixeldata[i] = 0;
00529 
00530       if(wts_initialized) 
00531         this->allocate_weights(0);
00532     } else {
00533       // This is the case where the number of elements
00534       // remain the same, but their dimensional distribution
00535       // may change.  This includes the important case when
00536       // there are no elements, but the number of dimensions
00537       // is specified through in_axes.size()
00538       axes = in_axes;
00539       for(int i=0; i<nelem; i++)
00540         pixeldata[i] = 0;
00541       if(this->weights_allocated())
00542         for(int i=0; i<nelem; i++)
00543           pixelwts[i] = 0;
00544     }
00545   }
00546 
00547   template<class T>
00548     void pixel_array<T>::allocate_weights(double wt){
00549 
00550     int nelem = total_space();
00551     if(pixeldata==NULL || nelem == 0){
00552       pixelwts=NULL;
00553       return;
00554     }
00555 
00556     if(pixelwts!=NULL){
00557       for(int i=0; i<nelem; i++)
00558         pixelwts[i] = wt;
00559       return;
00560     }
00561 
00562     try{
00563       if(pixel_array<T>::verbose_level) 
00564         cout << "pixel_array::allocate_weights - " << total_space() 
00565              << " weights being initialized to " << wt << endl;
00566       pixelwts = new float[nelem];}
00567     catch(...){
00568       cerr << "pixel_array::allocate_weights - could not allocate memory\n";
00569       throw(string("pixel_array::allocate_weights"));
00570     }
00571     for(int i=0; i<nelem; i++) pixelwts[i] = wt;
00572   }
00573 
00574   template<class T>
00575     void pixel_array<T>::deallocate_weights(){
00576     if(pixelwts==NULL) return;
00577     delete [] pixelwts;
00578     pixelwts = NULL;
00579   }
00580 
00581   template<> double pixel_array<long>::normalize_by_wts();
00582 
00583   template<class T>
00584     double pixel_array<T>::normalize_by_wts(){
00585     double maxwt = 0;
00586     if(!weights_allocated()) 
00587       return(0);
00588     int nelem = total_space();
00589     for(int i=0; i<nelem; i++){
00590       if(pixelwts[i]>maxwt && pixelwts[i]!=0)
00591         maxwt = pixelwts[i];
00592     }
00593     for(int i=0; i<nelem; i++){
00594       if(pixelwts[i]!=maxwt && pixelwts[i]!=0){
00595         pixeldata[i] *= maxwt/(float)pixelwts[i];
00596         pixelwts[i] = maxwt;
00597       } else if(pixelwts[i]==0) 
00598         pixeldata[i] = 0;
00599     }
00600     return(maxwt);
00601   }
00602 
00603   template<class T>
00604     void pixel_array<T>::decimate(int nadd){
00605     if(axes.size()!=2){
00606       cerr << "pixel_array::decimate - cannot decimate "
00607            << "pixel_array with number of dimensions " 
00608            << axes.size() << endl;
00609       throw(string("pixel_array::decimate"));
00610     }
00611 
00612     if(nadd<0 || nadd>axes[0] || nadd>axes[1]){
00613       cerr << "pixel_array::decimate - error decimating by a factor of "
00614            << nadd << endl;
00615       throw(string("pixel_array::decimate"));
00616     }
00617 
00618     if(nadd==0 || nadd==1) return;
00619 
00620     vector<long> newaxes(2);
00621     for(int i=0; i<newaxes.size(); i++) 
00622       newaxes[i] = axes[i]/nadd;
00623 
00624     T * olddata = pixeldata;
00625     float * oldwts;
00626     
00627     pixeldata = new T[newaxes[0]*newaxes[1]];
00628 
00629     if(weights_allocated()){
00630       oldwts = pixelwts;
00631       pixelwts = new float[newaxes[0]*newaxes[1]];
00632     }
00633 
00634     int wtsum;
00635     for(int i=0; i<newaxes[1]; i++){
00636       for(int j=0; j<newaxes[0]; j++){
00637         pixeldata[i*newaxes[0]+j] = 0;
00638         if(weights_allocated()){
00639           pixelwts[i*newaxes[0]+j] = 0;
00640           wtsum = 0;
00641         }
00642         for(int k=0; k<nadd; k++){
00643           for(int l=0; l<nadd; l++){
00644             if(weights_allocated()){
00645               pixeldata[i*newaxes[0]+j] += 
00646                 olddata[(i*nadd+k)*axes[0]+j*nadd+l]*
00647                 oldwts[(i*nadd+k)*axes[0]+j*nadd+l];
00648               pixelwts[i*newaxes[0]+j] += oldwts[(i*nadd+k)*axes[0]+j*nadd+l];
00649             } else {
00650               pixeldata[i*newaxes[0]+j] += olddata[(i*nadd+k)*axes[0]+j*nadd+l];
00651             }
00652           }
00653         }
00654       }
00655     }
00656 
00657     axes = newaxes;
00658     delete [] olddata;
00659   }
00660 
00661 
00662   template<class T>
00663     void pixel_array<T>::pad_array(int npad, double value) {
00664     if(npad<0){
00665       cerr << "pixel_array::pad_array error - cannot pad by "
00666            << npad << " pixels\n";
00667       throw(string("pixel_array::pad_array"));
00668     }
00669     if(npad==0 || axes.size()==0) return;
00670     
00671     if(axes.size()!=2){
00672       cerr << "pixel_array::pad_array error - cannot pad by "
00673            << axes.size() << " dimensions, "
00674            << " as this generalization has not yet been coded\n";
00675       throw(string("pixel_array::pad_array"));
00676     }    
00677 
00678     vector<long> new_dimen = axes;
00679     int nelem = 1;
00680     for(int i=0; i<new_dimen.size(); i++){
00681       new_dimen[i] += 2*npad;
00682       nelem *= new_dimen[i];
00683     }
00684 
00685     // allocate new arrays
00686     T * newdata = new T[nelem];
00687     float * newwts = NULL;
00688     if(weights_allocated())
00689       newwts = new float[nelem];
00690 
00691     // initialize arrays to the value
00692     for(int i=0; i<new_dimen[1]; i++){
00693       for(int j=0; j<new_dimen[0]; j++){
00694         newdata[i*new_dimen[0]+j] = value;
00695         if(weights_allocated())
00696           newwts[i*new_dimen[0]+j] = 0;
00697       }
00698     }
00699 
00700     // copy the old data into the new array
00701     for(int i=0; i<axes[1]; i++){
00702       for(int j=0; j<axes[0]; j++){
00703         newdata[(i+npad)*new_dimen[0]+npad+j] = 
00704           pixeldata[i*axes[0]+j];
00705         if(weights_allocated())
00706           newwts[(i+npad)*new_dimen[0]+npad+j] = 
00707             pixelwts[i*axes[0]+j];
00708       }
00709     }
00710   
00711     delete [] pixeldata;
00712     pixeldata = newdata;
00713 
00714     if(weights_allocated()){
00715       delete pixelwts;
00716       pixelwts = newwts;
00717     }
00718 
00719     axes = new_dimen;
00720   }
00721 
00722   template<class T>
00723     void pixel_array<T>::clip_array(int nclip) {
00724 
00725     if(nclip==0) return;
00726     if(nclip<0){
00727       cerr << "pixel_array::clip_array error - cannot clip by "
00728            << nclip << " pixels\n";
00729       throw(string("pixel_array::clip_array"));
00730     }
00731 
00732     vector<long> new_axes = axes;
00733     int nelem = 1;
00734     for(int i=0; i<new_axes.size(); i++){
00735       new_axes[i] -= 2*nclip;
00736       if(new_axes[i]<=0){
00737         cerr << "pixel_array::clip_array error - clipping original array by "
00738              << nclip << " pixels leaves non-positive array size\n";
00739         throw(string("pixel_array::clip_array"));
00740       }
00741       nelem *= new_axes[i];
00742     }
00743 
00744     // allocate new arrays
00745     T * newdata = new T[nelem];
00746     float * newwts = NULL;
00747     if(weights_allocated())
00748       newwts = new float[nelem];
00749 
00750     // copy the old data into the new array
00751     for(int i=0; i<new_axes[1]; i++)
00752       for(int j=0; j<new_axes[0]; j++){
00753         newdata[i*new_axes[0]+j] = 
00754           pixeldata[(i+nclip)*axes[0]+nclip+j];
00755         if(weights_allocated())
00756           newwts[i*new_axes[0]+j+1] = 
00757             pixelwts[(i+nclip)*axes[0]+nclip+j];
00758       }
00759 
00760     // make the switcheroo
00761     delete [] pixeldata;
00762     pixeldata = newdata;
00763     if(weights_allocated()){
00764       delete [] pixelwts;
00765       pixelwts = newwts;
00766     }
00767     axes = new_axes;
00768   } 
00769 
00770   template<class T>
00771     template<class U>
00772     void pixel_array<T>::flag_zero_wts(const pixel_array<U> & pixarr){
00773 
00774     if(!pixarr.weights_allocated()){
00775       cerr << "pixel_array::flag_zero_wts error - "
00776            << "reference array has no weights\n";
00777       throw(string("pixel_array::flag_zero_wts"));
00778     }
00779     if(!this->weights_allocated()) 
00780       this->allocate_weights(1);
00781     int nelem = total_space();
00782     for(int i=0; i<nelem; i++){
00783       if(pixarr.wt(i)==0){
00784         pixeldata[i] = 0;
00785         pixelwts[i] = 0;
00786       }
00787     }
00788   }
00789     
00790   template<class T>
00791     void pixel_array<T>::mask(){
00792     if(!this->weights_allocated())
00793       this->allocate_weights(1);
00794     int nelem = total_space();
00795     for(int i=0; i<nelem; i++){
00796       if(pixeldata[i]==0){
00797         pixelwts[i] = 0;
00798       } else {
00799         pixeldata[i] = 1;
00800         pixelwts[i] = 1;
00801       }
00802     }
00803   }
00804 
00805   template<class T>
00806     template<class U>
00807     void pixel_array<T>::mask(const pixel_array<U> * pixarr){
00808     if(axes!=pixarr->get_axes()){
00809       cerr << "pixel_array::mask error - mismatched axes\n";
00810       throw(string("pixel_array::mask"));
00811     }
00812     if(this->weights_allocated()==0)
00813       this->allocate_weights(1);
00814     int count = 0;
00815     int nelem = total_space();
00816     for(int i=0; i<nelem; i++){
00817       if(pixarr->data(i)==0){
00818         pixeldata[i] = 0;
00819         pixelwts[i] = 0;
00820         count++;
00821       } 
00822     }
00823   }
00824 
00825   template<class T>
00826     void pixel_array<T>::read(const iofits & iof){
00827   
00828     int ndimen = iof.get_img_dim();
00829     vector<long> tmpaxes = iof.get_img_size();
00830     this->set_axes(tmpaxes);
00831 
00832     int nelems = this->total_space();
00833 
00834     try{
00835       if(pixel_array<T>::verbose_level) 
00836         cout << "pixel_array::pixel_array - reading data\n";
00837       iof.read_image(0, nelems-1, pixeldata);
00838     } catch(...){
00839       cerr << "pixel_array::pixel_array - error reading image\n";
00840       throw(string("pixel_array::pixel_array"));
00841     }
00842 
00843     // INCOMPATIBILITY!!!!
00844     // this is a bad idea, as it relies on a global call,
00845     // so that pixel array cannot be written as part of
00846     // an aggregate class
00847     // Possible solution - write a weights keyword to the
00848     // header, but at the same time add something to the
00849     // AO_observation loader that can compensate for the
00850     // fact that this keyword doesn't exist in the present
00851     // header format.
00852     /*
00853       if(iof.get_num_hdus()==2){
00854       this->allocate_weights(0);
00855       if(pixel_array<T>::verbose_level) 
00856       cout << "pixel_array::pixel_array - reading weights\n";
00857       iof.movabs_hdu(2);
00858       iof.read_image(0,nelems-1,pixelwts);
00859       } else pixelwts = NULL;
00860     */
00861 
00862     // Backwards compatibility hack:
00863 
00864     // Before types other than AO_observations were
00865     // supported, I implicitly assumed that all fits 
00866     // files contained a primary array and zero or one
00867     // image extensions, depending on whether the 
00868     // weights were defined for a particular 
00869     // AO_observation.  One could check the total number 
00870     // of hdus to determine which case this was.
00871 
00872     // With the addition of other classes that required
00873     // fits file storage, this simple assumption became
00874     // too restrictive.  In particular, I wanted to be
00875     // able to write many pixel_arrays to the same fits
00876     // file, so that I could store aggregate objects.
00877     // Thus additional information was required to 
00878     // resolve whether the next image extension contained
00879     // weights from this pixel array or a new pixel array 
00880     // altogether - or some other form of data.
00881 
00882     // In order to fix this while maintaining backwards
00883     // compatibility, I added a weights keyword to the 
00884     // headers of the new classes.  If the weights keyword
00885     // is not present then this pixel array is assumed
00886     // to belong to an AO_observation, and the number
00887     // of hdu's is checked as before.  If the weights
00888     // key is present and is true, then we advance to
00889     // the next image extension and read the weights.
00890     // If the weights key is false, we stop.
00891 
00892     // Here we check whether there is a TYPE key,
00893     // which indicates that this pixel array is
00894     // part of an object other than an AO_observation.
00895 
00896     if(!iof.key_exists("WEIGHTS")){
00897       if(iof.get_num_hdus()==2){
00898         if(pixel_array<T>::verbose_level) 
00899           cout << "pixel_array::pixel_array - reading weights\n";
00900         this->allocate_weights(0);
00901         iof.movabs_hdu(2);
00902         iof.read_image(0,nelems-1,pixelwts);
00903       } else pixelwts = NULL;
00904     } else {
00905       bool weights;
00906       string comment;
00907       iof.read_key("WEIGHTS", weights, comment);
00908       if(weights){
00909         if(pixel_array<T>::verbose_level) 
00910           cout << "pixel_array::pixel_array - reading weights\n";
00911         this->allocate_weights(0);
00912         iof.movrel_hdu(1);
00913         iof.read_image(0, nelems-1, pixelwts);
00914       } 
00915       
00916       if(iof.get_hdu_num()<iof.get_num_hdus())
00917         iof.movrel_hdu(1);
00918     }
00919   }
00920 
00921   template<class T>
00922     void pixel_array<T>::write(iofits & iof) const {
00923 
00924     int space = total_space();
00925     if(space!=0) iof.write_image(0,space-1,pixeldata);
00926 
00927     string comment = "weights present";
00928     if(pixelwts!=NULL){
00929       iof.write_key("WEIGHTS", true, comment);
00930       Arroyo::fits_header_data<float> fhd(this->get_axes());
00931       fhd.write(iof);
00932       if(space!=0) iof.write_image(0,space-1,pixelwts); 
00933     } else       
00934       iof.write_key("WEIGHTS", false, comment);
00935  
00936   }
00937 
00938   template<class T>
00939     void pixel_array<T>::write_to_ppm(double min, double max,
00940                                       bool logscale,
00941                                       bool colorbar,
00942                                       colormap * cmap, 
00943                                       const char * filename, 
00944                                       long min_dimen) const {
00945   
00946     if(axes.size()!=2){
00947       cerr << "pixel_array::write_to_ppm error - array has "
00948            << axes.size() << " axes, rather than two\n";
00949       throw(string("pixel_array::write_to_ppm"));
00950     }
00951 
00952     if(min>=max){
00953       cerr << "pixel_array::write_to_ppm error - min " 
00954            << min << " and max " << max 
00955            << " supplied to this function are inconsistent\n";
00956       throw(string("pixel_array::write_to_ppm"));
00957     }
00958 
00959     long ppm_dimen;
00960     long ppm_pix_per_elem;
00961     if(min_dimen==-1){
00962       ppm_dimen = axes[0]>axes[1]?axes[0] : axes[1];
00963       ppm_pix_per_elem = 1;
00964     } else {
00965       if(min_dimen<axes[0] || min_dimen<axes[1]){
00966         cerr << "pixel_array::write_to_ppm error - minimum dimension " 
00967              << min_dimen << " less than array dimensions "
00968              << axes[0] << "x" << axes[1] << endl;
00969         throw(string("pixel_array::write_to_ppm"));
00970       }      
00971       ppm_dimen = min_dimen;
00972       ppm_pix_per_elem = axes[0]>axes[1] ? min_dimen/axes[0] : min_dimen/axes[1];
00973     }
00974     double ppm_min = min;
00975 
00976     long axis_zero_min =
00977       axes[0]>axes[1] ? 0 : ((axes[1]-axes[0])*ppm_pix_per_elem)/2;
00978     long axis_zero_max =
00979       axes[0]>axes[1] ? ppm_dimen : axes[0]*ppm_pix_per_elem+axis_zero_min;
00980     long axis_one_min =
00981       axes[1]>axes[0] ? 0 : ((axes[0]-axes[1])*ppm_pix_per_elem)/2;
00982     long axis_one_max =
00983       axes[1]>axes[0] ? ppm_dimen : axes[1]*ppm_pix_per_elem+axis_one_min;
00984 
00985     /*    
00986           cout << min_dimen 
00987           << "\t" << ppm_dimen 
00988           << "\t" << ppm_pix_per_elem
00989           << "\t" << axes[0] << "\t" << axes[1]
00990           << "\t" << axis_zero_min << "\t" << axis_zero_max
00991           << "\t" << axis_one_min << "\t" << axis_one_max
00992           << endl;
00993     */
00994 
00995     std::ofstream fs;
00996     fs.open(filename);
00997     fs << "P6\n";
00998 
00999     long colorbar_padding = 0;
01000     if(colorbar)
01001       colorbar_padding = .1*ppm_dimen>60 ? (long)(.1*ppm_dimen) : 60;
01002     fs << ppm_dimen << " " << ppm_dimen+colorbar_padding << endl;
01003     fs << "255\n";
01004     fs.close();
01005 
01006     fs.open(filename,
01007             std::ios::app | std::ios::out | std::ios::binary | std::ios::ate);
01008 
01009     // Here I inverted the write order on axes[1]
01010     // to force display to show the results in the
01011     // same orientation as does ds9
01012     int index;
01013     int nelem = axes[0]*axes[1];
01014     for(int i=ppm_dimen-1; i>=0; i--){
01015       for(int j=0; j<ppm_dimen; j++){
01016         if(i<axis_one_min || i>=axis_one_max || j<axis_zero_min || j>=axis_zero_max)
01017           fs << cmap->get_R(ppm_min, min, max, logscale)
01018              << cmap->get_G(ppm_min, min, max, logscale)
01019              << cmap->get_B(ppm_min, min, max, logscale);
01020         else {
01021           index = (((i-axis_one_min)/ppm_pix_per_elem)*axes[0])
01022             +((j-axis_zero_min)/ppm_pix_per_elem);
01023           fs << cmap->get_R(pixeldata[index], min, max, logscale)
01024              << cmap->get_G(pixeldata[index], min, max, logscale)
01025              << cmap->get_B(pixeldata[index], min, max, logscale);
01026         }
01027       }
01028     }
01029 
01030     if(colorbar){
01031       int first_limit = (long)(.3*colorbar_padding);
01032       int second_limit = (long)(.5*colorbar_padding);
01033 
01034       for(int i=0; i<first_limit; i++)
01035         for(int j=0; j<ppm_dimen; j++)  
01036           fs << cmap->get_R(ppm_min, min, max)
01037              << cmap->get_G(ppm_min, min, max)
01038              << cmap->get_B(ppm_min, min, max);
01039 
01040       for(int i=first_limit; i<second_limit; i++){
01041         int first = (long)(.1*ppm_dimen);
01042         int second = (long)(.9*ppm_dimen);
01043         for(int j=0; j<first-1; j++)
01044           fs << cmap->get_R(ppm_min, min, max)
01045              << cmap->get_G(ppm_min, min, max)
01046              << cmap->get_B(ppm_min, min, max);
01047 
01048         fs << '0' << '0' << '0';
01049 
01050         for(int j=first; j<second; j++){
01051           if(i==first_limit || i==second_limit-1)
01052             fs << '0' << '0' << '0';
01053           else 
01054             fs << cmap->get_R(min+(j-first)*(max-min)/(double)(second-first), min, max)
01055                << cmap->get_G(min+(j-first)*(max-min)/(double)(second-first), min, max)
01056                << cmap->get_B(min+(j-first)*(max-min)/(double)(second-first), min, max);
01057         }
01058 
01059         fs << '0' << '0' << '0';
01060 
01061         for(int j=second+1; j<ppm_dimen; j++)
01062           fs << cmap->get_R(ppm_min, min, max)
01063              << cmap->get_G(ppm_min, min, max)
01064              << cmap->get_B(ppm_min, min, max);
01065       }
01066 
01067       for(int i=second_limit; i<colorbar_padding; i++)
01068         for(int j=0; j<ppm_dimen; j++)  
01069           fs << cmap->get_R(ppm_min, min, max)
01070              << cmap->get_G(ppm_min, min, max)
01071              << cmap->get_B(ppm_min, min, max);
01072     }
01073   }
01074 
01075   template<class T>
01076     pixel_array<T>::pixel_array() {
01077     private_nelem = -1;
01078     pixeldata = NULL; 
01079     pixelwts = NULL;
01080   }
01081 
01082   template<class T>
01083     pixel_array<T>::pixel_array(const pixel_array<T> & pixarr){
01084     private_nelem = -1;
01085     pixeldata = NULL;
01086     pixelwts = NULL;
01087     pixel_array::operator=(pixarr);
01088   }
01089 
01090   template<class T>
01091     pixel_array<T>::pixel_array(const iofits & iof){
01092     private_nelem = -1;
01093     pixeldata = NULL;
01094     pixelwts = NULL;
01095     this->read(iof);
01096   }
01097 
01098   template<class T>
01099     template<class U>
01100     pixel_array<T>::pixel_array(const pixel_array<U> & pixarr,
01101                                 const vector<long> & pixel_limits){
01102 
01103 
01104     private_nelem = -1;
01105     vector<long> paxes = pixarr.get_axes();
01106     if(paxes.size()!=2){
01107       cerr << "pixel_array::pixel_array error - "
01108            << "cannot construct through extraction from " 
01109            << axes.size() << " dimensions, "
01110            << " as this generalization has not yet been coded\n";
01111       throw(string("pixel_array::pixel_array"));
01112     }    
01113 
01114     if(pixel_limits.size()!=2*paxes.size()){
01115       cerr << "pixel_array::pixel_array error - "
01116            << "cannot construct from a pixel array of other than 2 axes\n";
01117       throw(string("pixel_array::pixel_array"));
01118     }      
01119     
01120     if(pixel_array<T>::verbose_level)
01121       cout << "pixel_array::pixel_array - original axes " 
01122            << paxes[0] << "\t" << paxes[1] << endl;
01123 
01124     vector<long> tmpaxes(paxes.size());
01125 
01126     for(int i=0; i<pixel_limits.size(); i+=2){
01127 
01128       if(pixel_limits[i+1] >= paxes[i/2] || pixel_limits[i] < 0){
01129         cerr << "pixel_array::pixel_array - "
01130              << "requested pixels out of ";
01131         if(i==0) cerr << "horizontal";
01132         else cerr << "vertical";
01133         cerr << " range of input pixel_array:\n\t" 
01134              << "requested range\t" << pixel_limits[i]
01135              << " - " << pixel_limits[i+1]
01136              << "\n\tinput range\t" << 0 << " - " << paxes[i/2]-1 << endl;
01137         throw(string("pixel_array::pixel_array"));
01138       } 
01139 
01140       tmpaxes[i/2] = pixel_limits[i+1] - pixel_limits[i] + 1;
01141       if(tmpaxes[i/2]<=0){
01142         cerr << "pixel_array::pixel_array - undefined pixel limits " 
01143              << pixel_limits[i+1] << " - " << pixel_limits[i] << endl;
01144         throw(string("pixel_array::pixel_array"));
01145       }
01146     }
01147 
01148     pixeldata = NULL;
01149     pixelwts = NULL;
01150     this->set_axes(tmpaxes);
01151     if(pixarr.weights_allocated())
01152       this->allocate_weights(0);
01153 
01154     // Note:  fits files are stored in fortran array format rather than C format
01155     // Hence the loops are reversed 
01156     if(pixel_array<T>::verbose_level)
01157       cout << "pixel_array::pixel_array - extracting horizontal " 
01158            << pixel_limits[0] << " - " << pixel_limits[1]
01159            << " vertical " << pixel_limits[2] << " - " << pixel_limits[3] << endl;
01160     int tmpa, tmpb;
01161     int axes_one = axes[1];
01162     int axes_zero = axes[0];
01163     int paxes_zero = paxes[0];
01164     long pixel_limits_zero = pixel_limits[0];
01165     long pixel_limits_two = pixel_limits[2];
01166     for(int i=0; i<axes_one; i++){
01167       for(int j=0; j<axes_zero; j++){
01168         tmpa = i*axes_zero+j;
01169         tmpb = (pixel_limits_two+i)*paxes_zero+pixel_limits_zero+j;
01170         pixeldata[tmpa] = pixarr.data(tmpb);
01171         if(pixelwts!=NULL) pixelwts[tmpa] = pixarr.wt(tmpb);
01172       }
01173     }
01174   }
01175 
01176   template<class T>
01177     pixel_array<T>::pixel_array(const vector<long> & in_axes, 
01178                                 const T * data, 
01179                                 const float * wts){
01180 
01181     if(data == NULL && wts != NULL){
01182       cerr << "pixel_array::pixel_array error - data == NULL, wts != NULL" << endl;
01183       throw(string("pixel_array::pixel_array"));
01184     }
01185 
01186     private_nelem = -1;
01187     pixeldata = NULL;
01188     pixelwts = NULL;
01189     this->set_axes(in_axes);
01190 
01191     int nelems = total_space();
01192     
01193     if(data!=NULL){
01194       for(int i=0; i<nelems; i++) 
01195         pixeldata[i] = data[i];
01196 
01197       if(wts!=NULL){
01198         this->allocate_weights(0);
01199         for(int i=0; i<nelems; i++) 
01200           pixelwts[i] = wts[i];
01201       }
01202     } else 
01203       for(int i=0; i<nelems; i++) 
01204         pixeldata[i] = 0;
01205   }
01206   
01207   template<class T>
01208     pixel_array<T>::~pixel_array(){
01209     if(pixeldata!=NULL)
01210       delete [] pixeldata;
01211     if(pixelwts!=NULL)
01212       delete [] pixelwts;
01213   }
01214 
01215   template<class T>
01216     pixel_array<T> & pixel_array<T>::operator = (const pixel_array<T> & pixarr){
01217 
01218     if(this == &pixarr){
01219       return(*this);
01220     }
01221     this->copyfrom(pixarr);
01222     return(*this);
01223   }
01224 
01225   template<class T>
01226     bool pixel_array<T>::weights_allocated() const {
01227     if(pixelwts == NULL) return(0);
01228     return(1);
01229   }
01230 
01231   template<class T>
01232     template<class U>
01233     void pixel_array<T>::copyfrom(const pixel_array<U> & pixarr){
01234 
01235     if((void*)this == (void*)&pixarr) return;
01236 
01237     this->set_axes(pixarr.get_axes());
01238 
01239     // Copy over the data
01240     int nelem = pixarr.total_space();
01241     for(int i=0; i<nelem; i++)
01242       pixeldata[i] = static_cast<T>(pixarr.data(i));
01243 
01244     // Copy over the weights if they exist
01245     if(pixarr.weights_allocated()){
01246       if(!this->weights_allocated())
01247         this->allocate_weights(0);
01248       for(int i=0; i<nelem; i++)
01249         pixelwts[i] = pixarr.wt(i);      
01250     }
01251   }
01252 
01253   template<class T>
01254     void pixel_array<T>::print_axes(ostream & os) const {
01255     os << "pixel_array::print_axes - axes size " << axes.size() << endl;
01256     for(int i=0; i<axes.size(); i++)
01257       os << "pixel_array::print_axes - axis " << i << " size " << axes[i] << endl;
01258   }
01259 
01260   template<class T>
01261     void pixel_array<T>::min_and_max(double & min, double & max) const {
01262     vector<int> minpix(2,0), maxpix(2,0);
01263     this->min_and_max(min, minpix, max, maxpix);
01264   }
01265   
01266   template<class T>
01267     void pixel_array<T>::min_and_max(double & min, vector<int> & minpixel, 
01268                                      double & max, vector<int> & maxpixel) const {
01269   
01270     vector<int> axis_0_limits(2,0);
01271     vector<int> axis_1_limits(2,0);
01272     axis_0_limits[1] = axes[0]-1;
01273     axis_1_limits[1] = axes[1]-1;
01274     this->min_and_max(min, minpixel,
01275                       max, maxpixel,
01276                       axis_0_limits, axis_1_limits);
01277   }
01278 
01279   template<class T>
01280     void pixel_array<T>::min_and_max(double & min, vector<int> & minpixel, 
01281                                      double & max, vector<int> & maxpixel,
01282                                      vector<int> axis_0_limits, 
01283                                      vector<int> axis_1_limits) const {
01284   
01285     if(axis_1_limits.size()!=2 || axis_1_limits.size()!=2){
01286       cerr << "pixel_array<T>::min_and_max error - axis limits wrong dimension\n";
01287       throw(string("pixel_array<T>::min_and_max"));
01288     }
01289   
01290     if(axis_0_limits[0] < 0 || axis_0_limits[1] >= this->axes[0]){
01291       cerr << "pixel_array<T>::min_and_max error - axis 0 limits out of range\n";
01292       throw(string("pixel_array<T>::min_and_max"));
01293     }
01294     if(axis_1_limits[0] < 0 || axis_1_limits[1] >= this->axes[1]){
01295       cerr << "pixel_array<T>::min_and_max error - axis 1 limits out of range\n";
01296       throw(string("pixel_array<T>::min_and_max"));
01297     }
01298   
01299     if(verbose_level) 
01300       cout << "pixel_array<T>::min_and_max - "
01301            << "searching for min and max within limits " 
01302            << "(" << axis_0_limits[0] << "," << axis_0_limits[1] << ")" 
01303            << "(" << axis_1_limits[0] << "," << axis_1_limits[1] << ")" << endl;
01304   
01305     int index;
01306     int axes_0_limits_zero = axis_0_limits[0];
01307     int axes_0_limits_one = axis_0_limits[1];
01308     int axes_1_limits_zero = axis_1_limits[0];
01309     int axes_1_limits_one = axis_1_limits[1];
01310     int axes_zero = axes[0];
01311   
01312     minpixel.resize(2);
01313     maxpixel.resize(2);
01314     min = pixeldata[0];
01315     max = pixeldata[0];
01316     if(pixelwts!=NULL){
01317       for(int i=axes_1_limits_zero; i<axes_1_limits_one; i++){
01318         for(int j=axes_0_limits_zero; j<axes_0_limits_one; j++){
01319           index = i*axes_zero+j;
01320           if(min>pixeldata[index] && pixelwts[index]!=0){
01321             min = pixeldata[index];
01322             minpixel[0] = j; minpixel[1] = i;
01323           }
01324           if(max<pixeldata[index] && pixelwts[index]!=0){
01325             max = pixeldata[index];
01326             maxpixel[0] = j; maxpixel[1] = i;
01327           }
01328         }
01329       }
01330     } else {
01331       for(int i=axes_1_limits_zero; i<axes_1_limits_one; i++){
01332         for(int j=axes_0_limits_zero; j<axes_0_limits_one; j++){
01333           index = i*axes_zero+j;
01334           if(min>pixeldata[index]){
01335             min = pixeldata[index];
01336             minpixel[0] = j; minpixel[1] = i;
01337           }
01338           if(max<pixeldata[index]){
01339             max = pixeldata[index];
01340             maxpixel[0] = j; maxpixel[1] = i;
01341           }
01342         }
01343       }
01344     }
01345   }
01346 
01347   template<class T>
01348     void pixel_array<T>::flip_x(){
01349     double tmp;
01350     for(int i=0; i<axes[1]; i++){
01351       for(int j=0; j<axes[0]/2; j++){
01352         tmp = pixeldata[i*axes[0]+j];
01353         pixeldata[i*axes[0]+j] = pixeldata[i*axes[0]+axes[0]-j-1];
01354         pixeldata[i*axes[0]+axes[0]-j-1] = tmp;
01355       }
01356     }
01357   }
01358   
01359   template<class T>
01360     void pixel_array<T>::flip_y(){
01361     double tmp;
01362     for(int i=0; i<axes[1]/2; i++){
01363       for(int j=0; j<axes[0]; j++){
01364         tmp = pixeldata[i*axes[0]+j];
01365         pixeldata[i*axes[0]+j] = pixeldata[(axes[1]-i-1)*axes[0]+j];
01366         pixeldata[(axes[1]-i-1)*axes[0]+j] = tmp;
01367       }
01368     }
01369   }
01370   
01371   template<class T>
01372     void pixel_array<T>::flip_xy(){
01373     double tmp;
01374     for(int i=0; i<axes[1]/2; i++){
01375       for(int j=0; j<axes[0]/2; j++){
01376         tmp = pixeldata[i*axes[0]+j];
01377         pixeldata[i*axes[0]+j] = pixeldata[(axes[1]-i-1)*axes[0] + (axes[0]-j-1)];
01378         pixeldata[(axes[1]-i-1)*axes[0] + (axes[0]-j-1)] = tmp;
01379       
01380         tmp = pixeldata[i*axes[0]+(axes[0]-j-1)];
01381         pixeldata[i*axes[0]+(axes[0]-j-1)] = pixeldata[(axes[1]-i-1)*axes[0]+j];
01382         pixeldata[(axes[1]-i-1)*axes[0]+j] = tmp;
01383       }
01384     }
01385   }
01386 
01387   template<class T>
01388     void pixel_array<T>::flip_45(){
01389     double tmp;
01390 
01391     if(axes[1]!=axes[0]){
01392       cerr << "pixel_array::flip_45 error - cannot flip an array that isn't square\n"
01393            << "dimens "
01394            << axes[0] << "x" << axes[1] << endl;
01395       throw(string("pixel_array::flip_45"));
01396     }
01397 
01398     for(int i=0; i<axes[1]; i++){
01399       for(int j=i; j<axes[0]; j++){
01400         tmp = pixeldata[i*axes[0]+j];
01401         pixeldata[i*axes[0]+j] = pixeldata[j*axes[0]+i];
01402         pixeldata[j*axes[0]+i] = tmp;
01403       }
01404     }
01405   }
01406   
01407   template<class T>
01408     void pixel_array<T>::shift_by_fft(double dx, double dy){ 
01409 
01410     if(dx==0 && dy==0) return;
01411  
01412     if(axes.size()!=2){
01413       cerr << "pixel_array::shift_by_fft error - "
01414            << "array does not have 2 dimensions\n"; 
01415       throw(string("pixel_array::shift_by_fft")); 
01416     }  
01417     if(axes[0]<=1 || axes[1]<=1){
01418       cerr << "pixel_array::shift_by_fft error - "
01419            << "array doesn't contain enough elements\n";
01420       throw(string("pixel_array::shift_by_fft")); 
01421     }  
01422       
01423     if(fabs(dx)>=1 || fabs(dy)>=1){
01424       cerr << "pixel_array::shift_by_fft error - "
01425            << "cannot shift by more than 1 pixel\n"; 
01426       throw(string("pixel_array::shift_by_fft")); 
01427     }
01428 
01429     if(pixel_array<T>::verbose_level)
01430       cout << "pixel_array::shift_by_fft - shifting " << dx << " pixels in x, "
01431            << dy << " pixels in y\n";
01432 
01433     // array must be padded by an extra complex element  
01434     // for the real to complex fft 
01435     int nelem = axes[1]*2*(axes[0]/2+1); 
01436   
01437     T * thisarr;
01438     T * shiftarr;
01439     try{ 
01440       thisarr = new T[nelem]; 
01441       shiftarr = new T[2*axes[0]*axes[1]];
01442     } catch(...) { 
01443       cerr << "pixel_array::shift_by_fft error - error allocating memory\n"; 
01444       throw(string("pixel_array::shift_by_fft")); 
01445     }  
01446     Arroyo::rfft_manager<T> rfft_mgr;
01447     Arroyo::fft_manager<T> fft_mgr;
01448 
01449     int axes_zero = axes[0];
01450     int axes_one = axes[1];
01451     for(int i=0; i<axes_one; i++)
01452       for(int j=0; j<axes_zero; j++)
01453         thisarr[i*2*(axes_zero/2+1)+j] = pixeldata[i*axes_zero+j]; 
01454 
01455     if(pixel_array<T>::verbose_level)
01456       cout << "pixel_array::shift_by_fft - performing forward fft\n";
01457 
01458     //  SWITCHING FFT SCHEMES TO FFTW_MANAGER
01459     // fftrcf2d(axes[1], axes[0], thisarr); 
01460     // for the fortran ordering...
01461     bool fftw_estimate = true;
01462     bool fftw_in_place = true;
01463     vector<long> flipped_axes(2);
01464     flipped_axes[0] = axes[1];
01465     flipped_axes[1] = axes[0];
01466     rfft_mgr.real_to_complex_fft(flipped_axes,
01467                                  fftw_estimate, fftw_in_place,
01468                                  thisarr);
01469 
01470     double dxs = 2*M_PI*dx/(float)axes[0];
01471     double dys = 2*M_PI*dy/(float)axes[1];
01472     double phase, tmp, cp, sp;
01473     // positive frequencies
01474     int index, indexa, indexb;
01475     for(int i=0; i<axes_one; i++){
01476       for(int j=0; j<axes_zero/2+1; j++){
01477         indexa = i*axes_zero + j;
01478         indexb = i*(axes_zero/2+1) + j;
01479         shiftarr[2*indexa] = thisarr[2*indexb];
01480         shiftarr[2*indexa+1] = thisarr[2*indexb+1];
01481       }
01482     }
01483 
01484     // negative frequencies
01485     for(int j=axes_zero/2+1; j<axes_zero; j++){
01486       indexa = j;
01487       indexb = axes_zero-j;
01488       shiftarr[2*indexa] = thisarr[2*indexb];
01489       shiftarr[2*indexa+1] = -thisarr[2*indexb+1];
01490     }      
01491 
01492     for(int i=1; i<axes_one; i++){
01493       for(int j=axes_zero/2+1; j<axes_zero; j++){
01494         indexa = i*axes_zero + j;
01495         indexb = (axes_one-i)*(axes_zero/2+1) + axes_zero - j;
01496         shiftarr[2*indexa] = thisarr[2*indexb];
01497         shiftarr[2*indexa+1] = -thisarr[2*indexb+1];
01498       }
01499     }
01500 
01501     for(int i=0; i<axes_one; i++){
01502       for(int j=0; j<axes_zero; j++){
01503         if(j<axes_zero/2) phase = dxs*j;  
01504         else phase = -dxs*(axes_zero-j);
01505         if(i<axes_one/2) phase += dys*i;  
01506         else phase -= dys*(axes_one-i);  
01507         cp = cos(-phase);
01508         sp = sin(-phase);
01509         index = 2*(i*axes_zero+j);
01510         tmp = shiftarr[index]*cp-shiftarr[index+1]*sp;
01511         shiftarr[index+1] = shiftarr[index]*sp + shiftarr[index+1]*cp;
01512         shiftarr[index] = tmp;
01513       }
01514     }
01515 
01516     if(pixel_array<T>::verbose_level)
01517       cout << "pixel_array::shift_by_fft - performing backwards fft\n";
01518     //  SWITCHING FFT SCHEMES TO FFTW_MANAGER
01519     //  fftccb2d(axes[1], axes[0], shiftarr);
01520     //  original line - fftccb2d replaced it: fftcrb2dip(axes[1], axes[0], thisarr);
01521     fft_mgr.backward_fft(flipped_axes, fftw_estimate, fftw_in_place, shiftarr);
01522 
01523     double scale = 1/(double)(axes[0]*axes[1]);
01524     for(int i=0; i<axes_one; i++)
01525       for(int j=0; j<axes_zero; j++){
01526         pixeldata[i*axes_zero+j] =
01527           static_cast<T>(shiftarr[2*(i*axes_zero+j)]*scale);
01528       }
01529 
01530     delete [] thisarr;
01531     delete [] shiftarr;
01532   }
01533 
01534   template<class T>
01535     void pixel_array<T>::rotate_by_fft(double angle, bool window){
01536     if(angle==0) return;
01537     this->rotate_and_shift_by_fft(0, 0, angle, window);
01538   }
01539 
01540   template<class T>
01541     void pixel_array<T>::rotate_and_shift_by_fft(
01542                                                  double dx, double dy, double angle, bool window){
01543     this->simple_rotate_and_shift_by_fft(dx, dy, angle, window);
01544   }
01545 
01546   template<class T>
01547     void pixel_array<T>::simple_rotate_and_shift_by_fft(
01548                                                         double dx, double dy, double angle, bool window){
01549 
01550     // not yet supported
01551     if(dx!=0 || dy!=0){
01552       cerr << "pixel_array::simple_rotate_and_shift_by_fft error - "
01553            << "shifting by fft not yet supported by this function\n";
01554       throw(string("pixel_array::simple_rotate_and_shift_by_fft"));
01555     }
01556   
01557     // probably can never be supported
01558     if(this->weights_allocated()){
01559       cerr << "pixel_array::simple_rotate_and_shift_by_fft error - "
01560            << "this member function does not support rotation of "
01561            << "arrays with weights defined\n";
01562       throw(string("pixel_array::simple_rotate_and_shift_by_fft"));
01563     }
01564 
01565     angle = fmod(angle, 2*M_PI);
01566     if(angle<0) angle += 2*M_PI;
01567 
01568     if(pixel_array<T>::verbose_level)
01569       cout << "pixel_array::simple_rotate_and_shift_by_fft - "
01570            << "rotating through angle " 
01571            << angle << endl;
01572 
01573     // mod the angle into the range 0 < angle < M_PI
01574     if(angle>=3*M_PI_2){
01575       // here the pixel_array has the correct amount of memory
01576       // allocated, but we have to reshuffle to switch the indexing
01577       int nelem = axes[0]*axes[1];
01578       T * tmparray = new T[nelem];
01579       for(int i=0; i<axes[1]; i++)
01580         for(int j=0; j<axes[0]; j++)
01581           tmparray[(axes[0]-j-1)*axes[1]+i] = this->pixeldata[i*axes[0]+j];
01582       long tmp = axes[0];
01583       axes[0] = axes[1];
01584       axes[1] = tmp;
01585       for(int i=0; i<nelem; i++)
01586         this->pixeldata[i] = tmparray[i];
01587       delete [] tmparray;
01588       angle -= 3*M_PI_2;
01589     } else if(angle>=M_PI) {
01590       this->flip_xy();
01591       angle -= M_PI;
01592     } else if(angle>=M_PI_2) {
01593       // as above for angle>=3*M_PI_2
01594       int nelem = axes[0]*axes[1];
01595       T * tmparray = new T[nelem];
01596       for(int i=0; i<axes[1]; i++)
01597         for(int j=0; j<axes[0]; j++)
01598           tmparray[j*axes[1]+(axes[1]-i-1)] = this->pixeldata[i*axes[0]+j];
01599       long tmp = axes[0];
01600       axes[0] = axes[1];
01601       axes[1] = tmp;
01602       for(int i=0; i<nelem; i++)
01603         this->pixeldata[i] = tmparray[i];
01604       delete [] tmparray;
01605       angle -= M_PI_2;
01606     }
01607 
01608     // if we're down to zero angle, we can just return
01609     if(dx==0 && dy==0 && angle==0) return;
01610 
01611     if(pixel_array<T>::verbose_level)
01612       cout << "pixel_array::simple_rotate_and_shift_by_fft - "
01613            << "performing rotation by fft through angle " << angle << endl;
01614 
01615     // define new axes as the minimal size required to contain the rotated image
01616     vector<long> new_axes(2);
01617     new_axes[0] = (int)(ceil(axes[0] + axes[1]*tan(angle/2.0)));
01618     new_axes[1] = (int)(2*(ceil(axes[1] + axes[0]*sin(angle))/2));
01619 
01620     // do this again to account for the two row shears
01621     new_axes[0] = (int)(2*(ceil(new_axes[0] + axes[1]*tan(angle/2.0))/2));
01622 
01623     if(pixel_array<T>::verbose_level)
01624       cout << "pixel_array::simple_rotate_and_shift_by_fft - "
01625            << "old axes " << axes[0] << "\t" << axes[1]
01626            << " new axes " << new_axes[0] << "\t" << new_axes[1] << endl;
01627 
01628     int row_pad = new_axes[0]-axes[0];
01629     int column_pad = new_axes[1]-axes[1];
01630 
01631     int nelem = 2*(new_axes[0]/2+1)*new_axes[1];
01632     int column_stride = 2*(new_axes[0]/2+1);
01633 
01634     double * tmparr;
01635     try{tmparr = new double[nelem];} 
01636     catch(...) {
01637       cerr << "pixel_array::simple_rotate_and_shift_by_fft error - "
01638            << "could not allocate memory\n";
01639       throw(string("pixel_array::simple_rotate_and_shift_by_fft"));
01640     }
01641 
01642     // Copy the pixeldata into the temporary array
01643     for(int i=0; i<nelem; i++)
01644       tmparr[i] = 0;
01645 
01646     for(int i=0; i<axes[1]; i++)
01647       for(int j=0; j<axes[0]; j++)
01648         tmparr[(i+column_pad/2)*column_stride + j + row_pad/2]
01649           = pixeldata[i*axes[0]+j];
01650 
01651     bool fftw_estimate = false;
01652     bool fftw_in_place = true;
01653 
01654     Arroyo::rfft_manager<double> rfft_row_mgr;
01655 
01656     int index;
01657     double dyslope, shear, phase, cp, sp, tmp;
01658     double amp, tphase;
01659     for(int i=0; i<new_axes[1]; i++){
01660       rfft_row_mgr.real_to_complex_fft(vector<long>(1,new_axes[0]),
01661                                        fftw_estimate, fftw_in_place,
01662                                        &(tmparr[i*column_stride]));
01663       //shear = -tan(angle/2.0)*(i-new_axes[1]/2);
01664       shear = tan(angle/2.0)*(i-new_axes[1]/2);
01665       dyslope = 2*M_PI*shear/(float)new_axes[0];
01666       for(int j=0; j<column_stride/2; j++){
01667         phase = dyslope*j;
01668         if(window){
01669           cp = (1-(j/(double)(column_stride/2+1)))*cos(phase)/(double)column_stride;
01670           sp = (1-(j/(double)(column_stride/2+1)))*sin(phase)/(double)column_stride;
01671         } else {
01672           cp = cos(phase)/(double)column_stride;
01673           sp = sin(phase)/(double)column_stride;
01674         }
01675         index = i*column_stride+2*j;
01676         tmp = tmparr[index]*cp-tmparr[index+1]*sp;      
01677         tmparr[index+1] = tmparr[index]*sp + tmparr[index+1]*cp;
01678         tmparr[index] = tmp;
01679       }
01680       rfft_row_mgr.complex_to_real_fft(vector<long>(1,new_axes[0]),
01681                                        fftw_estimate, fftw_in_place,
01682                                        &(tmparr[i*column_stride]));
01683     } 
01684 
01685     Arroyo::rfft_manager<double> rfft_column_mgr;
01686 
01687     double * tmp_1d_array = new double[2*(new_axes[1]/2+1)];  
01688     for(int i=0; i<new_axes[0]; i++){
01689       for(int j=0; j<new_axes[1]; j++)
01690         tmp_1d_array[j] = tmparr[j*column_stride+i];
01691       rfft_column_mgr.real_to_complex_fft(vector<long>(1,new_axes[1]),
01692                                           fftw_estimate, fftw_in_place,
01693                                           tmp_1d_array);
01694 
01695       //shear = sin(angle)*(i-new_axes[0]/2);
01696       shear = -sin(angle)*(i-new_axes[0]/2);
01697       dyslope = 2*M_PI*shear/(float)new_axes[1];
01698       for(int j=0; j<new_axes[1]/2+1; j++){
01699         phase = dyslope*j;
01700         if(window){
01701           cp = (1-(j/(double)(new_axes[1]/2+1)))*cos(phase)/(double)new_axes[1];
01702           sp = (1-(j/(double)(new_axes[1]/2+1)))*sin(phase)/(double)new_axes[1];
01703         } else {
01704           cp = cos(phase)/(double)new_axes[1];
01705           sp = sin(phase)/(double)new_axes[1];
01706         } 
01707         tmp = tmp_1d_array[2*j]*cp-tmp_1d_array[2*j+1]*sp;      
01708         tmp_1d_array[2*j+1] = tmp_1d_array[2*j]*sp + tmp_1d_array[2*j+1]*cp;
01709         tmp_1d_array[2*j] = tmp;
01710       }
01711 
01712       rfft_column_mgr.complex_to_real_fft(vector<long>(1,new_axes[1]),
01713                                           fftw_estimate, fftw_in_place,
01714                                           tmp_1d_array);
01715       for(int j=0; j<new_axes[1]; j++)
01716         tmparr[j*column_stride+i] = tmp_1d_array[j];
01717     }
01718     delete [] tmp_1d_array;
01719 
01720     for(int i=0; i<new_axes[1]; i++){
01721       rfft_row_mgr.real_to_complex_fft(vector<long>(1,new_axes[0]),
01722                                        fftw_estimate, fftw_in_place,
01723                                        &(tmparr[i*column_stride]));
01724       //shear = -tan(angle/2.0)*(i-new_axes[1]/2);
01725       shear = tan(angle/2.0)*(i-new_axes[1]/2);
01726       dyslope = 2*M_PI*shear/(float)new_axes[0];
01727       for(int j=0; j<column_stride/2; j++){
01728         phase = dyslope*j;
01729         if(window){
01730           cp = (1-(j/(double)(column_stride/2+1)))*cos(phase)/(double)column_stride;
01731           sp = (1-(j/(double)(column_stride/2+1)))*sin(phase)/(double)column_stride;
01732         } else {
01733           cp = cos(phase)/(double)column_stride;
01734           sp = sin(phase)/(double)column_stride;
01735         }
01736         index = i*column_stride+2*j;
01737         tmp = tmparr[index]*cp-tmparr[index+1]*sp;      
01738         tmparr[index+1] = tmparr[index]*sp + tmparr[index+1]*cp;
01739         tmparr[index] = tmp;
01740       }
01741       rfft_row_mgr.complex_to_real_fft(vector<long>(1,new_axes[0]),
01742                                        fftw_estimate, fftw_in_place,
01743                                        &(tmparr[i*column_stride]));
01744     } 
01745 
01746     this->set_axes(new_axes);
01747     for(int i=0; i<new_axes[1]; i++)
01748       for(int j=0; j<new_axes[0]; j++){
01749         this->pixeldata[i*new_axes[0]+j] = tmparr[i*column_stride+j];
01750       }
01751     delete [] tmparr;  
01752   }
01753 
01754   template<class T>
01755     void pixel_array<T>::owen_makedon_rotate_and_shift_by_fft(
01756                                                               double dx, double dy, double angle){
01757 
01758     // Change this to account for different angles by flipping in x or y
01759     if(angle < -M_PI_2 || angle > M_PI_2){
01760       cerr << "pixel_array::owen_makedon_rotate_and_shift_by_fft error - "
01761            << "requested rotation angle "
01762            << angle << " outside the range -PI/2 < angle < PI/2\n";
01763       throw(string("pixel_array::rotate"));
01764     }
01765 
01766     // Compute the max shear
01767     double ta = tan(angle/2.0);
01768     long max_shear = (long)(ceil(axes[1]*ta));
01769     if(max_shear%1!=0) max_shear++;
01770 
01771     cout << "pixel_array::owen_makedon_rotate_and_shift_by_fft - max shear "
01772          << max_shear 
01773          << " for angle " << angle << " axes * tan angle / 2 "
01774          << axes[1]*ta << endl;
01775 
01776     // allocate a temporary array
01777     vector<long> new_axes(2);
01778     new_axes[0] = (axes[0]+2*max_shear);
01779     new_axes[1] = 2*axes[1];
01780 
01781     cout << "pixel_array::owen_makedon_rotate_and_shift_by_fft - new axes "
01782          << new_axes[0] << "\t" << new_axes[1] << endl;
01783 
01784     int nelem = 2*(new_axes[0]/2+1)*new_axes[1];
01785     int column_stride = 2*(new_axes[0]/2+1);
01786 
01787     cout << "pixel_array::owen_makedon_rotate_and_shift_by_fft - allocating "
01788          << nelem << " elements\n";
01789 
01790     double * tmparr;
01791     try{tmparr = new double[nelem];} 
01792     catch(...) {
01793       cerr << "pixel_array::owen_makedon_rotate_and_shift_by_fft error - "
01794            << "could not allocate memory\n";
01795       throw(string("pixel_array::owen_makedon_rotate_and_shift_by_fft"));
01796     }
01797 
01798     // Copy the pixeldata into the temporary array
01799     for(int i=0; i<nelem; i++)
01800       tmparr[i] = 0;
01801 
01802     for(int i=0; i<axes[1]; i++)
01803       for(int j=0; j<axes[0]; j++)
01804         tmparr[(i+axes[1]/2)*column_stride+j+max_shear] = pixeldata[i*axes[0]+j];
01805 
01806     Arroyo::rfft_manager<double> rfft_row_mgr;
01807     bool fftw_estimate = false;
01808     bool fftw_in_place = true;
01809 
01810     // Following the paper "High quality alias free image rotation" by
01811     // Owen & Makedon.  Dartmouth Dept. of Computer Science PCS-TR96-301
01812 
01813     // Step 1
01814     // Here we follow a slightly different strategy, by allocating all the space
01815     // we will need up front, and then picking through the array to do the correct
01816     // transforms
01817 
01818     // STEPS 2 and 3
01819     // shift each row by the appropriate sheared amount
01820     int index;
01821     double dyslope, shear, phase, cp, sp, tmp;
01822     double amp, tphase;
01823     for(int i=axes[1]/2; i<3*axes[1]/2; i++){
01824       rfft_row_mgr.real_to_complex_fft(vector<long>(1,new_axes[0]),
01825                                        fftw_estimate, fftw_in_place,
01826                                        &(tmparr[i*column_stride]));
01827       shear = -tan(angle/2.0)*(i-axes[1]);
01828       dyslope = 2*M_PI*shear/(float)new_axes[0];
01829       for(int j=0; j<column_stride/2; j++){
01830         phase = dyslope*j;
01831         cp = cos(phase);
01832         sp = sin(phase);
01833         index = i*column_stride+2*j;
01834         tmp = tmparr[index]*cp-tmparr[index+1]*sp;      
01835         tmparr[index+1] = tmparr[index]*sp + tmparr[index+1]*cp;
01836         tmparr[index] = tmp;
01837       }
01838       /*
01839       //rfft_row_mgr.complex_to_real_fft(vector<long>(1,new_axes[0]),
01840       fftw_estimate, fftw_in_place,
01841       &(tmparr[i*column_stride]));
01842       */
01843     } 
01844 
01845     Arroyo::fft_manager<double> fft_column_mgr;
01846 
01847     // STEP 4 
01848     // Transform columns
01849     double * tmp_1d_array = new double[2*new_axes[1]];  
01850     for(int i=0; i<2*axes[1]; i++) 
01851       tmp_1d_array[i] = 0;
01852 
01853     for(int i=0; i<new_axes[0]/2; i++){
01854       for(int j=0; j<axes[1]; j++){
01855         tmp_1d_array[2*j] = tmparr[(axes[1]/2+j)*column_stride+2*i];
01856         tmp_1d_array[2*j+1] = tmparr[(axes[1]/2+j)*column_stride+2*i+1];
01857       }
01858       fft_column_mgr.forward_fft(vector<long>(1,axes[1]), false, true, tmp_1d_array);
01859       for(int j=0; j<axes[1]; j++){
01860         tmparr[(axes[1]/2+j)*column_stride+2*i] = tmp_1d_array[2*j];
01861         tmparr[(axes[1]/2+j)*column_stride+2*i+1] = tmp_1d_array[2*j+1];
01862       }
01863     }
01864 
01865     // Intermediate step - do the shift by fft by adding phase
01866 
01867     // STEP 5
01868     // duplicate rows out of band
01869     for(int i=0; i<axes[1]/2; i++)
01870       for(int j=0; j<2*(new_axes[0]/2+1); j++){
01871         tmparr[i*column_stride+j] = 
01872           tmparr[(axes[1]+i)*column_stride+j];
01873         tmparr[(3*axes[1]/2+i)*column_stride+j] = 
01874           tmparr[(axes[1]/2+i)*column_stride+j];
01875       }
01876 
01877       
01878     // STEPS 6, 7, and 8
01879     // inverse fft rows, shift to effect column shear, and fft back
01880     for(int i=0; i<new_axes[1]; i++){ 
01881       rfft_row_mgr.complex_to_real_fft(vector<long>(1,new_axes[0]),
01882                                        fftw_estimate, fftw_in_place,
01883                                        &(tmparr[i*column_stride]));
01884       shear = -2*sin(angle/2.0)*(i-new_axes[1]/2);
01885       dyslope = 2*M_PI*shear/(float)new_axes[0];
01886       for(int j=0; j<column_stride/2; j++){
01887         phase = dyslope*j;
01888         cp = cos(phase);
01889         sp = sin(phase);
01890         index = i*column_stride+2*j;
01891         tmp = tmparr[index]*cp-tmparr[index+1]*sp;      
01892         tmparr[index+1] = tmparr[index]*sp + tmparr[index+1]*cp;
01893         tmparr[index] = tmp;
01894       }
01895       rfft_row_mgr.real_to_complex_fft(vector<long>(1,new_axes[0]),
01896                                        fftw_estimate, fftw_in_place,
01897                                        &(tmparr[i*column_stride]));
01898     }
01899 
01900 
01901     // STEP 9 Window - here we want to determine whether the coordinates
01902     // of each spectral point lies within the original rectangular
01903     // region.  To do so, we apply the inverse transformation on the
01904     // coordinates to determine the original coordinates, and window if
01905     // these lie outside the rectangle.
01906     // 
01907     // Recall that we got to this stage by applying a spatial row shear
01908     // of -tan theta/2 - equivalent to a spectral column shear of tan
01909     // theta/2 - followed by a spectral row shear of -2 sin theta.
01910     // So the inverse transformation is
01911     // 
01912     //  ( x' )  =  (1             0) (1   2 sin theta) ( x )
01913     //  ( y' )     (-tan theta/2  1) (0       1      ) ( y )
01914     double x_halfpix=0, y_halfpix=0;
01915     int x_extrapix=1, y_extrapix=1;
01916     if(axes[1]%2==0){
01917       x_halfpix = .5;
01918       x_extrapix = 0;
01919     }
01920     if(axes[0]%2==0){
01921       y_halfpix = .5;
01922       y_extrapix = 0;
01923     }
01924     for(int i=-new_axes[1]/2; i<new_axes[1]/2+x_extrapix; i++){
01925       for(int j=0; j<new_axes[0]/2+y_extrapix; j++){
01926         index = (i+new_axes[1]/2)*column_stride+2*j;
01927         cout << i << "\t" << j << "\t";
01928         if(fabs( (i+x_halfpix) + 2*sin(angle)*(j+y_halfpix) )>axes[1]/2)
01929           tmparr[index] = tmparr[index+1] = 0;
01930         if(fabs( -tan(angle/2.0) * (i+x_halfpix)
01931                  + (1-2*sin(angle)*tan(angle/2.0))*(j+y_halfpix) )>axes[0]/2)
01932           tmparr[index] = tmparr[index+1] = 0;
01933         cout << fabs( (i+x_halfpix) + 2*sin(angle)*(j+y_halfpix))
01934              << "\t" 
01935              << fabs( -tan(angle/2.0) * (i+x_halfpix)
01936                       + (1-2*sin(angle)*tan(angle/2.0))*(j+y_halfpix))
01937              << "\t";
01938         cout << tmparr[index] << "\t" << tmparr[index+1] << endl;
01939       }
01940     }
01941 
01942 
01943     // STEPS 10 and 11
01944     // inverse fft columns, shift to effect row shear
01945     Arroyo::fft_manager<double> new_column_mgr;
01946 
01947     for(int i=0; i<new_axes[0]/2; i++){
01948       for(int j=0; j<new_axes[1]; j++){
01949         tmp_1d_array[2*j] = tmparr[j*column_stride+2*i];
01950         tmp_1d_array[2*j+1] = tmparr[j*column_stride+2*i+1];
01951       }
01952 
01953       new_column_mgr.backward_fft(vector<long>(1,new_axes[1]),
01954                                   fftw_estimate, fftw_in_place,
01955                                   tmp_1d_array);
01956 
01957       shear = .5*tan(angle/2.0)*i;
01958       dyslope = 2*M_PI*shear/(float)new_axes[1];
01959       for(int j=0; j<new_axes[1]; j++){
01960         phase = dyslope*(j-new_axes[1]/2);
01961         cp = cos(phase);
01962         sp = sin(phase);
01963         index = 2*j;
01964         tmp = tmp_1d_array[index]*cp-tmp_1d_array[index+1]*sp;      
01965         tmp_1d_array[index+1] = tmp_1d_array[index]*sp + tmp_1d_array[index+1]*cp;
01966         tmp_1d_array[index] = tmp;
01967       }
01968 
01969       for(int j=0; j<new_axes[1]; j++){
01970         tmparr[j*column_stride+2*i] = tmp_1d_array[2*j];
01971         tmparr[j*column_stride+2*i+1] = tmp_1d_array[2*j+1];
01972       }
01973     }
01974 
01975     // STEP 12
01976     // inverse fft rows
01977     for(int i=0; i<new_axes[1]; i++)
01978       rfft_row_mgr.complex_to_real_fft(vector<long>(1,new_axes[0]),
01979                                        fftw_estimate, fftw_in_place,
01980                                        &(tmparr[i*column_stride]));
01981 
01982     delete [] tmp_1d_array;
01983 
01984     this->set_axes(new_axes);
01985     for(int i=0; i<new_axes[1]; i++)
01986       for(int j=0; j<new_axes[0]; j++){
01987         this->pixeldata[i*new_axes[0]+j] = tmparr[i*2*(new_axes[0]/2+1)+j];
01988       }
01989     delete [] tmparr;  
01990 
01991   }
01992 
01993   template<class T>
01994     pixel_array<T> pixel_array<T>::cross_correlate(
01995                                                    const pixel_array<T> & pixarr) const {
01996     if(pixarr.axes.size()!=2 || axes.size()!=2
01997        || axes[0]!=pixarr.axes[0] || axes[1]!=pixarr.axes[1]){
01998       cerr << "pixel_array::cross_correlate error - array mismatch\n";
01999       throw(string("pixel_array::cross_correlate"));
02000     }
02001     if(axes[0]<=1 || axes[1]<=1 || pixarr.axes[0]<=1 || pixarr.axes[1]<=1){
02002       cerr << "pixel_array::cross_correlate error - "
02003            << "array doesn't contain enough elements\n";
02004       throw(string("pixel_array::cross_correlate")); 
02005     }
02006     
02007     // arrays must be padded by an extra complex element 
02008     // for the real to complex fft
02009     int nelem = axes[1]*2*(axes[0]/2+1);
02010 
02011     T * fft_xcorr;
02012     T * xcorr;
02013     T * thisarr;
02014     T * inarr;
02015     try{
02016       if(pixel_array<T>::verbose_level)
02017         cout << "pixel_array::cross_correlate - allocating data nelem "
02018              << nelem << endl;
02019       thisarr = new T[nelem];
02020       inarr = new T[nelem];
02021       fft_xcorr = new T[nelem];
02022       if(pixel_array<T>::verbose_level)
02023         cout << "pixel_array::cross_correlate - allocating data nelem "
02024              << axes[0]*axes[1] << endl;
02025       xcorr = new T[axes[0]*axes[1]];
02026     } catch(...) {
02027       cerr << "pixel_array::cross_correlate error - error allocating memory\n";
02028       throw(string("pixel_array::cross_correlate"));
02029     }
02030     Arroyo::rfft_manager<T> rfft_mgr;
02031 
02032     int axes_one = axes[1];
02033     int axes_zero = axes[0];
02034     for(int i=0; i<axes_one; i++){
02035       for(int j=0; j<axes_zero; j++){
02036         thisarr[i*2*(axes_zero/2+1)+j] = this->pixeldata[i*axes_zero+j];
02037         inarr[i*2*(axes_zero/2+1)+j] = pixarr.pixeldata[i*axes_zero+j];
02038       }
02039     }
02040 
02041     //  SWITCHING FFT SCHEMES TO FFTW_MANAGER
02042     //fftrcf2d(axes[1], axes[0], thisarr);
02043     //fftrcf2d(axes[1], axes[0], inarr);
02044     bool fftw_estimate = true;
02045     bool fftw_in_place = true;
02046     vector<long> flipped_axes(2);
02047     flipped_axes[0] = axes[1];
02048     flipped_axes[1] = axes[0];
02049     if(pixel_array<T>::verbose_level)
02050       cout << "pixel_array::cross_correlate - transforming this pixel_array\n";
02051     rfft_mgr.real_to_complex_fft(flipped_axes,
02052                                  fftw_estimate, fftw_in_place,
02053                                  thisarr); 
02054     if(pixel_array<T>::verbose_level)
02055       cout << "pixel_array::cross_correlate - transforming arg pixel_array\n";
02056     rfft_mgr.real_to_complex_fft(flipped_axes,
02057                                  fftw_estimate, fftw_in_place,
02058                                  inarr);
02059 
02060     int indexa, indexb;
02061     double scale = 1.0/(double)(axes[0]*axes[1]);
02062     for(int i=0; i<axes_one; i++){
02063       for(int j=0; j<axes_zero/2+1; j++){
02064         indexa = i*2*(axes_zero/2+1) + j;
02065         indexb = i*2*(axes_zero/2+1) + 2*j;
02066         fft_xcorr[indexb] = (thisarr[indexb]*inarr[indexb] + 
02067                              thisarr[indexb+1]*inarr[indexb+1])*scale;
02068         fft_xcorr[indexb+1] = (thisarr[indexb]*inarr[indexb+1] - 
02069                                thisarr[indexb+1]*inarr[indexb])*scale;
02070       }
02071     }
02072     delete [] thisarr;
02073     delete [] inarr;
02074 
02075     // Perform inverse fft to obtain autocorrelation function
02076     //  SWITCHING FFT SCHEMES TO FFTW_MANAGER
02077     //fftcrb2d(axes[1],axes[0],fft_xcorr, xcorr);
02078     fftw_in_place = false;
02079     if(pixel_array<T>::verbose_level)
02080       cout << "pixel_array::cross_correlate - "
02081            << "back-transforming to retrieve cross-correlated array\n";
02082     rfft_mgr.complex_to_real_fft(flipped_axes,
02083                                  fftw_estimate, fftw_in_place,
02084                                  fft_xcorr, xcorr);
02085   
02086     delete [] fft_xcorr;
02087 
02088     pixel_array<T> xcorr_pixarr = pixel_array<T>(axes, xcorr);
02089     delete [] xcorr;
02090     return(xcorr_pixarr);
02091   }
02092   
02093   template<class T>
02094     void pixel_array<T>::offset(const pixel_array<T> & pixarr, 
02095                                 vector<double> & offsets, 
02096                                 long range) const {
02097     
02098     if(pixarr.axes.size()!=2 || axes.size()!=2
02099        || axes[0]!=pixarr.axes[0] || axes[1]!=pixarr.axes[1]){
02100       cerr << "pixel_array::offset error - array mismatch\n";
02101       throw(string("pixel_array::offset"));
02102     }
02103     if(axes[0]<=1 || axes[1]<=1 || pixarr.axes[0]<=1 || pixarr.axes[1]<=1){
02104       cerr << "pixel_array::offset error - "
02105            << "array doesn't contain enough elements\n";
02106       throw(string("pixel_array::offset")); 
02107     } 
02108 
02109     if(pixel_array<T>::verbose_level)
02110       cout << "pixel_array::offset - seeking offset between arrays of size\n"
02111            << "\t(" << this->axes[0] << ", " << this->axes[1]  
02112            << ") and (" << pixarr.axes[0] << "," << pixarr.axes[1] 
02113            << ")" << endl;
02114 
02115     pixel_array<T> xcorr_pixarr = this->cross_correlate(pixarr);
02116     vector<int> xcorr_minpixel(2,0), xcorr_maxpixel(2,0);
02117 
02118     double min, max;
02119     xcorr_pixarr.min_and_max(min, xcorr_minpixel, max, xcorr_maxpixel);
02120 
02121     // make the offset (-axis/2,axis/2] rather than [0,axis-1] 
02122     for(int i=0; i<2; i++){
02123       if(xcorr_maxpixel[i]>axes[i]/2){
02124         if(pixel_array<T>::verbose_level) 
02125           cout << "pixel_array<T>::offset - correcting dec " << i << endl;
02126         xcorr_maxpixel[i] -= axes[i];
02127       }
02128       if(xcorr_maxpixel[i]<-axes[i]/2){
02129         if(pixel_array<T>::verbose_level) 
02130           cout << "pixel_array<T>::offset - correcting axis " << i << endl;
02131         xcorr_maxpixel[i] += axes[i];
02132       }
02133     }
02134 
02135     // Find the maximum of this pixel array within limits
02136     // These limits ensure that the maximum lies at least
02137     // <range points> from the edges of the array
02138 
02139     vector<int> tmp_minpixel, tmp_maxpixel;
02140     if(range!=-1){
02141       vector<int> axis_0_range(2,range), axis_1_range(2,range);
02142       axis_0_range[1] = axes[0]-range-1;
02143       axis_1_range[1] = axes[1]-range-1;
02144       
02145       this->min_and_max(min, tmp_minpixel, max, tmp_maxpixel, axis_0_range, axis_1_range);
02146     } else 
02147       this->min_and_max(min, tmp_minpixel, max, tmp_maxpixel);
02148     
02149     // Ensure that the maximum is not shifted off the edge by xcorr_maxpixel
02150     for(int i=0; i<2; i++){
02151       if(tmp_maxpixel[i]+xcorr_maxpixel[i]>axes[i])
02152         xcorr_maxpixel[i] -= axes[i];
02153       if(tmp_maxpixel[i]-xcorr_maxpixel[i]<0)
02154         xcorr_maxpixel[i] += axes[i];
02155     }
02156 
02157     if(pixel_array<T>::verbose_level) {
02158       cout << "pixel_array<T>::offset - xcorr max " << xcorr_maxpixel[0] << "\t" 
02159            << xcorr_maxpixel[1] << "\t" << max << endl;
02160       cout << "pixel_array<T>::offset - this max " << max << " at " 
02161            << tmp_maxpixel[0] << "," << tmp_maxpixel[1] << endl;
02162     }
02163 
02164     pixel_array<T> tpix, apix;
02165     if(range!=-1){
02166       // Extract a region from this pixel array around the 
02167       // maximum set by the maxrange argument
02168         
02169       long maxrange = range;
02170       vector<long> this_pixlimits(4), arg_pixlimits(4);
02171         
02172       this_pixlimits[0] = tmp_maxpixel[0] - maxrange;
02173       this_pixlimits[1] = tmp_maxpixel[0] + maxrange-1;
02174       this_pixlimits[2] = tmp_maxpixel[1] - maxrange;
02175       this_pixlimits[3] = tmp_maxpixel[1] + maxrange-1;
02176         
02177       if(pixel_array<T>::verbose_level) 
02178         cout << "pixel_array<T>::offset - extracting pixels " 
02179              << this_pixlimits[0] << " - " << this_pixlimits[1] << " in x " 
02180              << this_pixlimits[2] << " - " << this_pixlimits[3] << " in y "
02181              << this_pixlimits[1] - this_pixlimits[0] + 1 << " x " 
02182              << this_pixlimits[3] - this_pixlimits[2] + 1 << endl;
02183       tpix = pixel_array<T>(*this, this_pixlimits);
02184         
02185       // Extract a region from the argument pixel array around the 
02186       // maximum set by the maxrange argument
02187       /*
02188         arg_pixlimits[0] = (this_pixlimits[0]+xcorr_maxpixel[0]+axes[0])%axes[0];
02189         arg_pixlimits[1] = (this_pixlimits[1]+xcorr_maxpixel[0]+axes[0])%axes[0];
02190         arg_pixlimits[2] = (this_pixlimits[2]+xcorr_maxpixel[1]+axes[1])%axes[1];
02191         arg_pixlimits[3] = (this_pixlimits[3]+xcorr_maxpixel[1]+axes[1])%axes[1];
02192       */
02193         
02194       arg_pixlimits[0] = this_pixlimits[0]+xcorr_maxpixel[0];
02195       arg_pixlimits[1] = this_pixlimits[1]+xcorr_maxpixel[0];
02196       arg_pixlimits[2] = this_pixlimits[2]+xcorr_maxpixel[1];
02197       arg_pixlimits[3] = this_pixlimits[3]+xcorr_maxpixel[1];
02198         
02199       if(pixel_array<T>::verbose_level) 
02200         cout << "pixel_array<T>::offset - extracting pixels " 
02201              << arg_pixlimits[0] << " - " << arg_pixlimits[1] << " in x " 
02202              << arg_pixlimits[2] << " - " << arg_pixlimits[3] << " in y " 
02203              << arg_pixlimits[1] - arg_pixlimits[0] + 1 << " x " 
02204              << arg_pixlimits[3] - arg_pixlimits[2] + 1 << endl;
02205       apix = pixel_array<T>(pixarr, arg_pixlimits);
02206     } else {
02207       tpix = *this;
02208       apix = pixarr;
02209     }
02210         
02211     tpix.min_and_max(min, tmp_minpixel, max, tmp_maxpixel);
02212 
02213     if(pixel_array<T>::verbose_level)
02214       cout << "pixel_array<T>::offset - tpix max " << max << " at " 
02215            << tmp_maxpixel[0] << "," << tmp_maxpixel[1] << endl;
02216     apix.min_and_max(min, tmp_minpixel, max, tmp_maxpixel);
02217     if(pixel_array<T>::verbose_level)
02218       cout << "pixel_array<T>::offset - apix max " << max << " at " 
02219            << tmp_maxpixel[0] << "," << tmp_maxpixel[1] << endl;
02220 
02221     // Convert from amp phase to product of amplitudes and phase differences
02222     // At the same time, resort the array to reconstruct the correct ordering
02223     // of elements.  For a real to complex fft, we have the symmetry
02224     // X[a,b] = X*[n_a - a, n_b - b]
02225     // Therefore, we must duplicate the array (with the exception of DC and,
02226     // for n even, nyquist)
02227     // by flipping it about the horizontal (symmetry above) and the vertical
02228     // (to get it in order of increasing frequency) and append it to the front of 
02229     // the original array
02230     // We do this on the arrays with the zero padding defined above, neglecting the
02231     // last complex element (the zero padding for the fft
02232 
02233     // Treat the rows in pairs, first/last, first-1/last-1...
02234     // First form the amplitude product and the phase difference
02235     // of the positive frequencies
02236     // Then form these for the negative frequencies stored in the
02237     // other array of the pair
02238     // This stage must take account of whether the array has an
02239     // even or odd number of elements
02240     // Finally, copy them into the original array
02241     int nelem = tpix.axes[1]*2*(tpix.axes[0]/2+1);
02242 
02243     T * tarr;
02244     T * aarr;
02245     T * product_amps;
02246     T * phase_difference;
02247     try{
02248       if(pixel_array<T>::verbose_level)
02249         cout << "pixel_array::offset - allocating data nelem " << nelem << endl;
02250       tarr = new T[nelem];
02251       aarr = new T[nelem];
02252       product_amps = new T[nelem];
02253       phase_difference = new T[nelem];
02254     } catch(...) {
02255       cerr << "pixel_array::offset error - error allocating memory\n";
02256       throw(string("pixel_array::offset"));
02257     }
02258     Arroyo::rfft_manager<T> rfft_mgr;  
02259 
02260     int scale = tpix.axes[0]*tpix.axes[1];
02261     int tpix_axes_one = tpix.axes[1];
02262     int tpix_axes_zero = tpix.axes[0];
02263     for(int i=0; i<tpix_axes_one; i++){
02264       for(int j=0; j<tpix_axes_zero; j++){
02265         tarr[i*2*(tpix_axes_zero/2+1)+j] = tpix.pixeldata[i*tpix_axes_zero+j];
02266         aarr[i*2*(tpix_axes_zero/2+1)+j] = apix.pixeldata[i*tpix_axes_zero+j];
02267       }
02268     }
02269 
02270     //  SWITCHING FFT SCHEMES TO FFTW_MANAGER
02271     //fftrcf2d(tpix.axes[1], tpix.axes[0], tarr);
02272     //fftrcf2d(apix.axes[1], apix.axes[0], aarr);
02273     bool fftw_estimate = true;
02274     bool fftw_in_place = true;
02275     vector<long> flipped_axes(2);
02276     flipped_axes[0] = tpix.axes[1];
02277     flipped_axes[1] = tpix.axes[0];
02278     rfft_mgr.real_to_complex_fft(flipped_axes,
02279                                  fftw_estimate, fftw_in_place,
02280                                  tarr);
02281     rfft_mgr.real_to_complex_fft(flipped_axes,
02282                                  fftw_estimate, fftw_in_place,
02283                                  aarr);
02284 
02285     double tmpamp, tmpphase;
02286     int indexa, indexb;
02287     // positive frequencies
02288     for(int i=0; i<tpix_axes_one; i++){
02289       for(int j=0; j<tpix_axes_zero/2+1; j++){
02290         indexa = i*2*(tpix_axes_zero/2+1) + j;
02291         indexb = i*2*(tpix_axes_zero/2+1) + 2*j;
02292         product_amps[indexa] = 
02293           sqrt(tarr[indexb]*tarr[indexb] + tarr[indexb+1]*tarr[indexb+1])*
02294           sqrt(aarr[indexb]*aarr[indexb] + aarr[indexb+1]*aarr[indexb+1]);
02295         phase_difference[indexa] = 
02296           atan2(tarr[indexb+1],tarr[indexb]) - 
02297           atan2(aarr[indexb+1],aarr[indexb]);
02298       }
02299     }
02300     delete [] tarr;
02301     delete [] aarr;
02302 
02303     // negative frequencies
02304     for(int j=tpix_axes_zero/2+1; j<tpix_axes_zero; j++){
02305       indexa = j;
02306       indexb = tpix_axes_zero - j;
02307       product_amps[indexa] = product_amps[indexb];
02308       phase_difference[indexa] = -phase_difference[indexb];
02309     }
02310 
02311     for(int i=1; i<tpix_axes_one; i++){
02312       for(int j=tpix_axes_zero/2+1; j<tpix_axes_zero; j++){
02313         indexa = i*2*(tpix_axes_zero/2+1) + j;
02314         indexb = (tpix_axes_one-i)*2*(tpix_axes_zero/2+1) + tpix_axes_zero - j;
02315         product_amps[indexa] = product_amps[indexb];
02316         phase_difference[indexa] = -phase_difference[indexb];
02317       }
02318     }
02319 
02320     // Perform a search for the best fitting fractional offset.
02321     double faca = 2*M_PI/tpix.axes[0];
02322     double facb = 2*M_PI/tpix.axes[1];
02323     double tmp, tmpa, tmpb;
02324     double fa, fb, b, chisq;
02325     double dfa_du, dfa_dv, dfb_du, dfb_dv;
02326     double min_chisq = 0, min_fa, min_fb;
02327     double iindex, jindex;
02328     double last_chisq = 0;
02329     double frac_axis_0=0, frac_axis_1=0;
02330     int index; 
02331 
02332     // Solve for best offsets
02333     while(1){
02334       fa=fb=dfa_du=dfa_dv=dfb_dv=chisq=0;
02335       for(int i=0; i<tpix_axes_one; i++){
02336         for(int j=0; j<tpix_axes_zero; j++){
02337           index = 2*i*(tpix_axes_zero/2+1)+j;
02338           if(i<=tpix_axes_one/2) iindex = i;
02339           else iindex = i-tpix_axes_one;
02340           if(j<=tpix_axes_zero/2) jindex = j;
02341           else jindex = j-tpix_axes_zero;
02342 
02343           tmpa = product_amps[index]*sin(phase_difference[index] - 
02344                                          iindex*frac_axis_1*facb -
02345                                          jindex*frac_axis_0*faca);
02346           fa += iindex*tmpa;
02347           fb += jindex*tmpa;
02348           tmpb = product_amps[index]*cos(phase_difference[index] - 
02349                                          iindex*frac_axis_1*facb -
02350                                          jindex*frac_axis_0*faca); 
02351 
02352           dfa_du += iindex*iindex*tmpb;
02353           dfb_dv += jindex*jindex*tmpb;
02354           dfa_dv += iindex*jindex*tmpb;
02355           chisq -= tmpb;
02356         }
02357       }
02358       
02359       dfb_du = dfa_dv;
02360       tmpa = (fa*dfb_dv - fb*dfa_dv)/(dfa_du*dfb_dv-dfa_dv*dfb_du);
02361       tmpb = (-fa*dfb_du + fb*dfa_du)/(dfa_du*dfb_dv-dfa_dv*dfb_du);
02362       frac_axis_0+=tmpb;
02363       frac_axis_1+=tmpa;
02364 
02365       if(fabs(tmpa)<1e-6 && fabs(tmpb)<1e-6)
02366         break;
02367       last_chisq = chisq;
02368     }
02369     if(pixel_array<T>::verbose_level) 
02370       cout << "pixel_array<T>::offset - " << endl
02371            << " dfa_du " << dfa_du << " dfa_dv " << dfa_dv
02372            << " dfb_du " << dfb_du << " dfb_dv " << dfb_dv << endl
02373            << " fa " << setw(10) << setprecision(3) << fa 
02374            << " fb " << setw(10) << setprecision(3) << fb << endl 
02375            << " tmpa " << tmpa << " tmpb " << tmpb << endl
02376            << " chisq " << setw(10) << setprecision(6) << chisq
02377            << setprecision(3) << endl 
02378            << " dra " << setw(10) << frac_axis_0 << endl
02379            << " ddec " << setw(10) << frac_axis_1 << endl    
02380            << " axis 0 " << setw(10) << setprecision(6)
02381            << xcorr_maxpixel[0] + frac_axis_0 << endl
02382            << " axis 1 " << setw(10) << xcorr_maxpixel[1] + frac_axis_1 << endl;
02383     
02384 
02385 
02386     /*
02387       if(fabs(frac_axis_0)>1 || fabs(frac_axis_1)>1){
02388       cerr << "pixel_array::offset error - value for fractional pixel shift "
02389       << frac_axis_0 << "\t" << frac_axis_1 
02390       << " has magnitude greater than one\n";
02391       throw(string("pixel_array::offset"));
02392       }
02393     */
02394 
02395     offsets.resize(2);
02396     offsets[0] = xcorr_maxpixel[0] + frac_axis_0;
02397     offsets[1] = xcorr_maxpixel[1] + frac_axis_1;
02398 
02399     if(pixel_array<T>::verbose_level) 
02400       cout << "pixel_array<T>::offset - final offsets " 
02401            << offsets[0] << "\t" << offsets[1] << endl;
02402 
02403     // Clean up
02404     delete [] product_amps;
02405     delete [] phase_difference;
02406   }
02407 
02408   template <> pixel_array<long> & operator+=<long, float>(
02409                                                           pixel_array<long> & lhs, const pixel_array<float> & rhs);
02410   template <> pixel_array<long> & operator+=<long, double>(
02411                                                            pixel_array<long> & lhs, const pixel_array<double> & rhs);
02412 
02413   template<class T, class U>
02414     pixel_array<T> & operator += (pixel_array<T> & lhs,
02415                                   const pixel_array<U> & rhs){
02416     if(lhs.get_axes()!=rhs.get_axes()){
02417       cerr << "pixel_array::operator+= error - " 
02418            << "mismatched array sizes\n";
02419       for(int i=0; i<lhs.get_axes().size(); i++){
02420         cerr << setw(8) << lhs.get_axes()[i];
02421       }
02422       cerr << endl;
02423       for(int i=0; i<rhs.get_axes().size(); i++){
02424         cerr << setw(8) << rhs.get_axes()[i];
02425       }
02426       cerr << endl;
02427       throw(string("pixel_array::operator+="));
02428     }
02429 
02430     if((lhs.pixelwts!=NULL && rhs.weights_allocated()==0) ||
02431        (lhs.pixelwts==NULL && rhs.weights_allocated()!=0)){
02432       cerr << "pixel_array<T> & operator += error - "
02433            << "weights not defined for both objects\n";
02434       throw(string("pixel_array<T> & operator +="));
02435     }
02436 
02437     int nelem = lhs.total_space();
02438     if(pixel_array<T>::verbose_level)
02439       cout << "pixel_array::operator+= - adding data\n";
02440     for(int i=0; i<nelem; i++){
02441       if(lhs.pixelwts!=NULL && lhs.pixelwts[i] == 0 && rhs.wt(i) == 0){
02442         lhs.pixeldata[i] = 0;   
02443         lhs.pixelwts[i] = 0;
02444       } else if(lhs.pixelwts!=NULL && lhs.pixelwts[i] == 0 && rhs.wt(i) != 0){ 
02445         lhs.pixeldata[i] = rhs.pixeldata[i];
02446         lhs.pixelwts[i] = rhs.pixelwts[i];
02447       } else if(lhs.pixelwts!=NULL){ 
02448         lhs.pixeldata[i] += rhs.pixeldata[i];
02449         lhs.pixelwts[i] += rhs.pixelwts[i];
02450       } else {
02451         lhs.pixeldata[i] += rhs.pixeldata[i];
02452       }
02453     }
02454     return(lhs);
02455   }
02456 
02457   template <> pixel_array<long> & operator-=<long, float>(
02458                                                           pixel_array<long> & lhs, const pixel_array<float> & rhs);
02459   template <> pixel_array<long> & operator-=<long, double>(
02460                                                            pixel_array<long> & lhs, const pixel_array<double> & rhs);
02461 
02462   template<class T, class U>
02463     pixel_array<T> & operator -= (
02464                                   pixel_array<T> & lhs, 
02465                                   const pixel_array<U> & rhs){
02466     if(lhs.get_axes()!=rhs.get_axes()){
02467       cerr << "pixel_array::operator-= error - " 
02468            << "mismatched array sizes\n";
02469       for(int i=0; i<lhs.get_axes().size(); i++){
02470         cerr << setw(8) << lhs.get_axes()[i];
02471       }
02472       cerr << endl;
02473       for(int i=0; i<rhs.get_axes().size(); i++){
02474         cerr << setw(8) << rhs.get_axes()[i];
02475       }
02476       cerr << endl;
02477       throw(string("pixel_array::operator-="));
02478     }
02479 
02480     if((lhs.pixelwts!=NULL && rhs.weights_allocated()==0) ||
02481        (lhs.pixelwts==NULL && rhs.weights_allocated()!=0)){
02482       cerr << "pixel_array<T> & operator-= error - "
02483            << "weights not defined for both objects\n";
02484       throw(string("pixel_array<T> & operator-="));
02485     }
02486 
02487     int nelem = lhs.total_space();
02488     if(pixel_array<T>::verbose_level)
02489       cout << "pixel_array::operator-= - subtracting data\n";
02490     for(int i=0; i<nelem; i++){
02491       if(lhs.pixelwts!=NULL && lhs.pixelwts[i] == 0 && rhs.pixelwts[i] == 0){
02492         lhs.pixeldata[i] = 0;   
02493         lhs.pixelwts[i] = 0;
02494       } else if(lhs.pixelwts!=NULL && lhs.pixelwts[i] == 0 && rhs.pixelwts[i] != 0){ 
02495         lhs.pixeldata[i] = -rhs.pixeldata[i];
02496         lhs.pixelwts[i] = rhs.pixelwts[i];
02497       } else if(lhs.pixelwts!=NULL){ 
02498         lhs.pixeldata[i] -= rhs.pixeldata[i];
02499         lhs.pixelwts[i] += rhs.pixelwts[i];
02500       } else {
02501         lhs.pixeldata[i] -= rhs.pixeldata[i];
02502       }
02503     }
02504     return(lhs);
02505   }
02506 
02507   template <> pixel_array<long> & operator*=<long, float>(
02508                                                           pixel_array<long> & lhs, const pixel_array<float> & rhs);
02509   template <> pixel_array<long> & operator*=<long, double>(
02510                                                            pixel_array<long> & lhs, const pixel_array<double> & rhs);
02511 
02512   template<class T, class U>
02513     pixel_array<T> & operator *= (
02514                                   pixel_array<T> & lhs, 
02515                                   const pixel_array<U> & rhs){
02516     if(lhs.get_axes()!=rhs.get_axes()){
02517       cerr << "pixel_array::operator*= error - " 
02518            << "mismatched array sizes\n";
02519       for(int i=0; i<lhs.get_axes().size(); i++){
02520         cerr << setw(8) << lhs.get_axes()[i];
02521       }
02522       cerr << endl;
02523       for(int i=0; i<rhs.get_axes().size(); i++){
02524         cerr << setw(8) << rhs.get_axes()[i];
02525       }
02526       cerr << endl;
02527       throw(string("pixel_array::operator*="));
02528     }
02529 
02530     if((lhs.pixelwts!=NULL && rhs.weights_allocated()==0) ||
02531        (lhs.pixelwts==NULL && rhs.weights_allocated()!=0)){
02532       cerr << "pixel_array<T> & operator*= error - "
02533            << "weights not defined for both objects\n";
02534       throw(string("pixel_array<T> & operator*="));
02535     }
02536 
02537     int nelem = lhs.total_space();
02538     if(pixel_array<T>::verbose_level)
02539       cout << "pixel_array::operator*= - multiplying data\n";
02540     for(int i=0; i<nelem; i++){
02541       if(lhs.pixelwts!=NULL && lhs.pixelwts[i] == 0 && rhs.pixelwts[i] == 0){
02542         lhs.pixeldata[i] = 0;   
02543         lhs.pixelwts[i] = 0;
02544       } else if(lhs.pixelwts!=NULL && lhs.pixelwts[i] == 0
02545                 && rhs.pixelwts[i] != 0){ 
02546         lhs.pixeldata[i] = rhs.pixeldata[i];
02547         lhs.pixelwts[i] = rhs.pixelwts[i];
02548       } else if(lhs.pixelwts!=NULL){ 
02549         lhs.pixeldata[i] *= rhs.pixeldata[i];
02550         lhs.pixelwts[i] = sqrt(1/((rhs.pixeldata[i]*rhs.pixeldata[i])/
02551                                   (lhs.pixelwts[i]*lhs.pixelwts[i])
02552                                   + (lhs.pixeldata[i]*lhs.pixeldata[i])/
02553                                   (rhs.pixelwts[i]*rhs.pixelwts[i])));
02554       } else {
02555         lhs.pixeldata[i] *= rhs.pixeldata[i];
02556       }
02557     }
02558     return(lhs);
02559   }
02560 
02561   template <> pixel_array<long> & operator/=<long, float>(
02562                                                           pixel_array<long> & lhs, const pixel_array<float> & rhs);
02563   template <> pixel_array<long> & operator/=<long, double>(
02564                                                            pixel_array<long> & lhs, const pixel_array<double> & rhs);
02565 
02566   template<class T, class U>
02567     pixel_array<T> & operator /= (
02568                                   pixel_array<T> & lhs, 
02569                                   const pixel_array<U> & rhs){
02570     if(lhs.get_axes()!=rhs.get_axes()){
02571       cerr << "pixel_array::operator/= error - " 
02572            << "mismatched array sizes\n";
02573       for(int i=0; i<lhs.get_axes().size(); i++){
02574         cerr << setw(8) << lhs.get_axes()[i];
02575       }
02576       cerr << endl;
02577       for(int i=0; i<rhs.get_axes().size(); i++){
02578         cerr << setw(8) << rhs.get_axes()[i];
02579       }
02580       cerr << endl;
02581       throw(string("pixel_array::operator/="));
02582     }
02583 
02584     if((lhs.pixelwts!=NULL && rhs.weights_allocated()==0) ||
02585        (lhs.pixelwts==NULL && rhs.weights_allocated()!=0)){
02586       cerr << "pixel_array<T> & operator/= error - "
02587            << "weights not defined for both objects\n";
02588       throw(string("pixel_array<T> & operator/="));
02589     }
02590 
02591     int nelem = lhs.total_space();
02592     if(pixel_array<T>::verbose_level)
02593       cout << "pixel_array::operator/= - dividing data\n";
02594     for(int i=0; i<nelem; i++){
02595       if(rhs.pixeldata[i]==0){
02596         lhs.pixeldata[i] = 0;   
02597         if(lhs.pixelwts!=NULL)
02598           lhs.pixelwts[i] = 0;
02599       } else if(lhs.pixelwts!=NULL){ 
02600         lhs.pixeldata[i] /= rhs.pixeldata[i];
02601         lhs.pixelwts[i] = 
02602           sqrt(1/(1/(lhs.pixelwts[i]*lhs.pixelwts[i]*
02603                      rhs.pixeldata[i]*rhs.pixeldata[i]) +
02604                   (lhs.pixeldata[i]*lhs.pixeldata[i])/
02605                   (rhs.pixeldata[i]*rhs.pixeldata[i]*
02606                    rhs.pixeldata[i]*rhs.pixeldata[i]
02607                    *lhs.pixelwts[i]*lhs.pixelwts[i])));
02608       } else lhs.pixeldata[i] /= rhs.pixeldata[i];
02609     }
02610     return(lhs);
02611   }
02612 
02613   template <> pixel_array<long> & pixel_array<long>::operator += (
02614                                                                   const double & fac);
02615 
02616   template<class T>
02617     pixel_array<T> & pixel_array<T>::operator += (const double & fac){
02618     if(fac==0) return(*this);
02619     int nelem = total_space();
02620     for(int i=0; i<nelem; i++)
02621       this->pixeldata[i] += fac;
02622     return(*this);
02623   }
02624 
02625   template <> pixel_array<long> & pixel_array<long>::operator -= (
02626                                                                   const double & fac);
02627 
02628   template<class T>
02629     pixel_array<T> & pixel_array<T>::operator -= (const double & fac){
02630     if(fac==0) return(*this);
02631     int nelem = total_space();
02632     for(int i=0; i<nelem; i++)
02633       this->pixeldata[i] -= fac;
02634     return(*this);
02635   }
02636 
02637   template <> pixel_array<long> & pixel_array<long>::operator *= (
02638                                                                   const double & fac);
02639 
02640   template<class T>
02641     pixel_array<T> & pixel_array<T>::operator *= (const double & fac){
02642     if(fac==1) return(*this);
02643     int nelem = total_space();
02644     for(int i=0; i<nelem; i++){
02645       this->pixeldata[i] *= fac;
02646     }   
02647     if(pixelwts!=NULL) 
02648       for(int i=0; i<nelem; i++)
02649         this->pixelwts[i] *= fac;
02650     return(*this);
02651   }
02652 
02653   template <> pixel_array<long> & pixel_array<long>::operator /= (
02654                                                                   const double & fac);
02655 
02656   template<class T>
02657     pixel_array<T> & pixel_array<T>::operator /= (const double & fac){
02658     if(fac==1) return(*this);
02659     if(fac==0){
02660       cerr << "pixel_array::operator /= division by zero error\n";
02661       throw(string("pixel_array::operator /="));
02662     }
02663     int nelem = total_space();
02664     for(int i=0; i<nelem; i++)
02665       this->pixeldata[i] /= fac;
02666     if(pixelwts!=NULL)
02667       for(int i=0; i<nelem; i++)
02668         this->pixelwts[i] /= fac;
02669 
02670     return(*this);
02671   }
02672 
02673   template<class T> 
02674     pixel_array<T> operator + (const pixel_array<T> &p1,
02675                                const pixel_array<T> &p2){
02676     if(p1.get_axes()!=p2.get_axes()){
02677       cerr << "pixel_array::operator+ error - " 
02678            << "mismatched array sizes\n";
02679       for(int i=0; i<p1.get_axes().size(); i++){
02680         cerr << setw(8) << p1.get_axes()[i];
02681       }
02682       cerr << endl;
02683       for(int i=0; i<p2.get_axes().size(); i++){
02684         cerr << setw(8) << p2.get_axes()[i];
02685       }
02686       cerr << endl;
02687       throw(string("pixel_array::operator+"));
02688     }
02689     pixel_array<T> pixarr(p1);
02690     pixarr += p2;
02691     return(pixarr);
02692   }
02693 
02694   template <class T> 
02695     pixel_array<T> operator - (const pixel_array<T> &p1,
02696                                const pixel_array<T> &p2){
02697     if(p1.get_axes()!=p2.get_axes()){
02698       cerr << "pixel_array::operator- error - " 
02699            << "mismatched array sizes\n";
02700       for(int i=0; i<p1.get_axes().size(); i++){
02701         cerr << setw(8) << p1.get_axes()[i];
02702       }
02703       cerr << endl;
02704       for(int i=0; i<p2.get_axes().size(); i++){
02705         cerr << setw(8) << p2.get_axes()[i];
02706       }
02707       cerr << endl;
02708       throw(string("pixel_array::operator-"));
02709     }
02710     pixel_array<T> pixarr(p1);
02711     pixarr -= p2;
02712     return(pixarr);
02713   }
02714 
02715   template <class T> 
02716     pixel_array<T> operator * (const pixel_array<T> &p1,
02717                                const pixel_array<T> &p2){
02718     if(p1.get_axes()!=p2.get_axes()){
02719       cerr << "pixel_array::operator* error - " 
02720            << "mismatched array sizes\n";
02721       for(int i=0; i<p1.get_axes().size(); i++){
02722         cerr << setw(8) << p1.get_axes()[i];
02723       }
02724       cerr << endl;
02725       for(int i=0; i<p2.get_axes().size(); i++){
02726         cerr << setw(8) << p2.get_axes()[i];
02727       }
02728       cerr << endl;
02729       throw(string("pixel_array::operator/"));
02730     }
02731     pixel_array<T> pixarr(p1);
02732     pixarr *= p2;
02733     return(pixarr);
02734   }
02735 
02736   template <class T> 
02737     pixel_array<T> operator / (const pixel_array<T> &p1,
02738                                const pixel_array<T> &p2){
02739     if(p1.get_axes()!=p2.get_axes()){
02740       cerr << "pixel_array::operator/ error - " 
02741            << "mismatched array sizes\n";
02742       for(int i=0; i<p1.get_axes().size(); i++){
02743         cerr << setw(8) << p1.get_axes()[i];
02744       }
02745       cerr << endl;
02746       for(int i=0; i<p2.get_axes().size(); i++){
02747         cerr << setw(8) << p2.get_axes()[i];
02748       }
02749       cerr << endl;
02750       throw(string("pixel_array::operator/"));
02751     }
02752     pixel_array<T> pixarr(p1);
02753     pixarr /= p2;
02754     return(pixarr);
02755   }
02756 
02757   template <class T> 
02758     pixel_array<T> operator + (const pixel_array<T> &p1, double & fac){
02759     pixel_array<T> pixarr(p1);
02760     pixarr += fac;
02761     return(pixarr);
02762   }
02763 
02764   template <class T> 
02765     pixel_array<T> operator - (const pixel_array<T> &p1, double & fac){
02766     pixel_array<T> pixarr(p1);
02767     pixarr -= fac;
02768     return(pixarr);
02769   }
02770 
02771   template <class T> 
02772     pixel_array<T> operator * (const pixel_array<T> &p1, double & fac){
02773     pixel_array<T> pixarr(p1);
02774     pixarr *= fac;
02775     return(pixarr);
02776   }
02777 
02778   template <class T> 
02779     pixel_array<T> operator / (const pixel_array<T> &p1, double & fac){
02780     pixel_array<T> pixarr(p1);
02781     pixarr /= fac;
02782     return(pixarr);
02783   }
02784 
02787   template <class T> 
02788     bool operator != (const pixel_array<T> &p1, const pixel_array<T> &p2){
02789     return(!(p1==p2));
02790   }
02791 }
02792 
02793 #endif 

Generated on Thu Nov 29 17:16:30 2007 for arroyo by  doxygen 1.3.9.1