8 #ifndef IMAGE_PROCESSING_H_
9 #define IMAGE_PROCESSING_H_
16 #include "image_info.h"
27 enum class Telescope {Euclid_VIS,Euclid_Y,Euclid_J,Euclid_H,KiDS_u,KiDS_g,KiDS_r,KiDS_i,HST_ACS_I,CFHT_u,CFHT_g,CFHT_r,CFHT_i,CFHT_z};
29 enum class UnitType {counts_x_sec, flux} ;
35 Obs(
size_t Npix_xx,
size_t Npix_yy
43 size_t getNxInput()
const {
return Npix_x_input;}
44 size_t getNyInput()
const {
return Npix_y_input;}
46 size_t getNxOutput()
const {
return Npix_x_output;}
47 size_t getNyOutput()
const {
return Npix_y_output;}
49 std::valarray<double> getPSF(){
return map_psf;}
51 void setPSF(std::string psf_file,
double resolution=0);
55 void setSeeing(
double s
58 void rotatePSF(
double theta
64 void coaddPSF(
double f
74 float getPixelSize()
const {
return pix_size;}
75 void setNoiseCorrelation(std::string nc_file);
90 virtual float getBackgroundNoise()
const = 0;
102 template <
typename T>
107 size_t Npix_x_output,Npix_y_output;
109 size_t Npix_x_input,Npix_y_input;
111 float psf_oversample;
112 template <
typename T>
116 double input_psf_pixel_size;
120 std::valarray<double> map_psf;
121 std::valarray<double> map_psfo;
123 std::vector<std::complex<double> > fft_psf;
124 std::vector<std::complex<double> > fft_padded;
125 std::vector<double> image_padded;
126 std::vector<double> sqrt_noise_power;
129 size_t nborder_x = 0;
130 size_t nborder_y = 0;
136 fftw_plan image_to_fft;
137 fftw_plan fft_to_image;
140 std::vector<std::complex<double> > noise_fft_image;
141 std::vector<double> noise_in_zeropad;
142 fftw_plan p_noise_r2c;
143 std::vector<double> noise_image_out;
144 fftw_plan p_noise_c2r;
175 double sigma_background2;
178 template <
typename T>
190 ,
double resolution = 0.1*arcsecTOradians
196 ,
const std::vector<double> &exposure_times
202 ,
const std::vector<double> &exposure_times
205 ,
double background_sigma
206 ,
double calibration_exposure_time
211 template <
typename T>
217 template <
typename T>
223 template <
typename T>
235 double dt = Utilities::vec_sum(t_exp);
237 return sqrt( sigma_background2 / dt );
240 std::vector<double> t_exp;
255 Observation(Telescope tel_name,
double exposure_time,
int exposure_num
256 ,
size_t Npix_x,
size_t Npix_y,
float oversample);
257 Observation(
float diameter,
float transmission,
float exp_time,
int exp_num,
float back_mag,
float read_out_noise
258 ,
size_t Npix_x,
size_t Npix_y,
double pix_size,
float seeing = 0.);
259 Observation(
float diameter,
float transmission,
float exp_time,
int exp_num,
float back_mag,
float read_out_noise ,std::string psf_file,
size_t Npix_x,
size_t Npix_y,
double pix_size,
float oversample = 1.);
261 Observation(
float zeropoint_mag,
float exp_time,
int exp_num,
float back_mag,
float read_out_noise,
size_t Npix_x,
size_t Npix_y,
double pix_size,
float seeing=0);
262 Observation(
float zeropoint_mag,
float exp_time,
int exp_num,
float back_mag,
float read_out_noise ,std::string psf_file,
size_t Npix_x,
size_t Npix_y,
double pix_size,
float oversample = 1.);
266 float getExpTime()
const {
return exp_time;}
267 int getExpNum()
const {
return exp_num;}
268 float getBackMag()
const {
return back_mag;}
270 float getRon()
const {
return read_out_noise;}
273 float getZeropoint()
const {
return mag_zeropoint;}
274 void setZeropoint(
double zpoint){mag_zeropoint=zpoint;}
276 float getBackgroundNoise(
float resolution, UnitType unit = UnitType::counts_x_sec)
const;
279 template <
typename T>
283 template <
typename T>
297 void setPixelSize(
float pixel_size){pix_size=pixel_size;}
299 double mag_to_counts(
double m)
const {
300 if(m == 100)
return 0;
301 return pow(10,-0.4*(m - mag_zeropoint));
303 double counts_to_mag(
double flux)
const{
304 if(flux <=0)
return 100;
305 return -2.5 * log10(flux) + mag_zeropoint;
307 double zeropoint()
const{
308 return mag_zeropoint;
318 float read_out_noise;
322 float e_per_s_to_ergs_s_cm2;
323 float background_flux;
327 template <
typename T>
329 template <
typename T>
331 template <
typename T>
335 void pixelize(
double *map,
long Npixels,
double range,
double *center
336 ,
ImageInfo *imageinfo,
int Nimages,
bool constant_sb,
bool cleanmap
337 ,
bool write_for_skymaker =
false, std::string filename=
"");
338 void _SplitFluxIntoPixels(
TreeHndl ptree,
Branch *leaf,
double *leaf_sb);
344 void ReadFileNames(std::string dir,
const std::string filespec
345 ,std::vector<std::string> & filenames
346 ,
const std::string file_non_spec =
" "
347 ,
bool verbose =
false);
354 MultiGridSmoother(
double center[],std::size_t Nx,std::size_t Ny,
double resolution);
366 void add_particles(std::vector<PosType> x,std::vector<PosType> y);
373 std::vector<PixelMap<double> > maps;
374 std::vector<Utilities::Interpolator<PixelMap<double> >> interpolators;
378 template <
typename T>
383 double dt = Utilities::vec_sum(t_exp);
386 int missing_frame = (int)(ran()*t_exp.size());
388 dt -= t_exp[missing_frame];
391 for(
auto &p : pmap.data() ){
392 if(p>0) p = ran.poisson(p*dt)/dt;
396 template <
typename T>
401 if(pmap.getUnits() != PixelMapUnits::count_per_sec){
402 std::cerr <<
"ObsVIS::AddNoise() Map must be in counts_per_sec at this stage." << std::endl;
403 throw std::runtime_error(
"wrong uits");
406 if(pmap.getNx() != pmap.getNy()){
407 std::cerr <<
"ObsVIS::AddNoise() Doesn't work on nonsquare maps" << std::endl;
408 throw std::runtime_error(
"nonsquare");
411 double dt = Utilities::vec_sum(t_exp);
412 int missing_frame = -1;
416 missing_frame = (int)(ran()*t_exp.size());
417 dt -= t_exp[missing_frame];
421 double inv_sigma2 = (dt)/sigma_background2;
422 size_t N = pmap.size();
423 for (
unsigned long i = 0; i < N ; i++){
424 error_map[i] = inv_sigma2;
430 double t = (int)(ran()*dt);
431 for(
int j=0 ; j<100 ; ++j){
440 if(i != missing_frame) cosmics(error_map,t_exp[i]/sigma_background2,1,ran);
449 for (
unsigned long i = 0; i < N ; i++){
450 pmap[i] = ran.poisson(MAX<float>(pmap[i] * dt,0))/dt;
451 pmap[i] += ran.
gauss() / sqrt(error_map[i]);
453 error_map[i] = sqrt( 1.0 / error_map[i] + MAX<float>(pmap[i] / dt,0) ) ;
459 template <
typename T>
460 void ObsVIS::cosmics(
466 size_t N = error_map.size();
467 size_t Nx = error_map.getNx();
468 size_t Ny = error_map.getNy();
475 size_t no = (size_t)(N*ran());
476 double theta = 2*PI*ran();
477 double c=cos(theta),s=sin(theta);
482 double L = MAX<double>(length/tan(PI*ran()/2),1);
486 double t = tan(theta);
489 long x1 = (long)(io + L * c );
491 x1 = MAX<long>(0,x1);
492 x1 = MIN<long>(Nx-1,x1);
493 for(x = io ; x != x1 ; x = x + sgn){
494 y = MAX<long>((
long)(t*(x-io) + jo),0);
495 y= MIN<long>(y,Ny-2);
496 error_map(x,y) += -inv_sigma2;
497 error_map(x,y+1) += -inv_sigma2;
500 long y1 = (long)(jo + L * s );
502 y1 = MAX<long>(0,y1);
503 y1 = MIN<long>(Ny-1,y1);
504 for(y = jo ; y != y1 ; y = y + sgn){
505 x = MAX<long>((
long)((y-jo)/t + io),0);
506 x = MIN<long>(x,Nx-2);
507 error_map(x,y) += -inv_sigma2;
508 error_map(x+1,y) += -inv_sigma2;
515 template <
typename T>
516 void ObsVIS::Convert(
525 assert(map_in.getNx() == Npix_x_input);
526 assert(map_in.getNy() == Npix_y_input);
528 if (fabs(map_in.getResolution()*psf_oversample - pix_size) > pix_size*1.0e-5)
530 std::cout <<
"The resolution of the input map is different from the one of the simulated instrument in Observation::Convert!" << std::endl;
531 throw std::runtime_error(
"The resolution of the input map is different from the one of the simulated instrument!");
539 , Npix_y_input, pix_size);
542 ApplyPSF<T>(map_in,map_scratch);
544 downsample<T>(map_scratch,map_out);
547 downsample<T>(map_in,map_out);
550 if (noise ==
true) AddNoise<T>(map_out,error_map,ran,cosmic);
557 template <
typename T>
561 input_psf_pixel_size = psf_map.getResolution();
563 if( (input_psf_pixel_size - pix_size/psf_oversample)/input_psf_pixel_size > 1.0e-3){
564 std::cout <<
"Obs::setPSF() - psf is not resolved." << std::endl;
565 std::cout << (input_psf_pixel_size - pix_size/psf_oversample)/input_psf_pixel_size << std::endl;
566 throw std::runtime_error(
"");
569 map_psf.resize(psf_map.size());
570 for(
size_t i=0 ; i<psf_map.size() ; ++i){
571 map_psf[i] = psf_map[i];
574 std::vector<size_t> size = {psf_map.getNx(),psf_map.getNy()};
579 double amax = map_psf[0];
580 for(
auto &a : map_psf){
581 if(a > amax){imax=i;amax=a;}
584 max_y = imax % size[0];
585 max_x = imax / size[0];
588 long Ly = Lx = 2*MIN<double>( MIN<double>(max_y,size[1]-max_y)
589 ,MIN<double>(max_x,size[0]-max_x));
591 std::valarray<double> tmp(Lx*Ly);
593 for(
long i=max_x - Lx/2 ; i<max_x + Lx/2 ;++i){
596 for(
long j=max_y - Ly/2 ; j<max_y + Ly/2 ;++j){
598 tmp[ii + Lx*jj] = map_psf[j + size[1] * i];
604 std::swap(tmp,map_psf);
610 template <
typename T>
613 assert(map_in.getNx() == Npix_x_input);
614 assert(map_in.getNy() == Npix_y_input);
615 assert(map_out.getNx() == Npix_x_output);
616 assert(map_out.getNy() == Npix_y_output);
618 if(psf_oversample == 1){
619 size_t n=map_in.size();
620 for(
size_t i=0; i<n ; ++i ) map_out[i] = map_in[i];
633 for(
size_t i=0 ; i<Npix_x_input ; ++i){
634 size_t ii = MIN<long>(i / psf_oversample + 0.5, Npix_x_output - 1 ) ;
635 for(
size_t j=0 ; j<Npix_y_input ; ++j){
636 size_t jj = MIN<long>(j / psf_oversample + 0.5, Npix_y_output - 1 ) ;
638 map_out(ii,jj) += map_in(i,j);
648 template <
typename T>
651 if(map_in.getNx() != map_in.getNy()){
652 std::cout <<
"Obs::ApplyPSF() Doesn't work on nonsquare maps" << std::endl;
653 throw std::runtime_error(
"nonsquare");
655 if(map_in.getNx() != Npix_y_input){
656 std::cout <<
"Obs::ApplyPSF() Input map it the wrong size." << std::endl;
657 throw std::runtime_error(
"nonsquare");
659 if(map_in.getNx() != map_out.getNx()){
660 std::cout <<
"Obs::ApplyPSF() Input map output are not the same size." << std::endl;
661 throw std::runtime_error(
"nonsquare");
665 if (map_psf.size() == 0){
671 map_out.
smooth(seeing/2.355);
678 map_out.ChangeUnits(map_in.getUnits());
681 for(
double &a :image_padded) a=0;
682 for(
int i=0 ; i<Npix_x_input ; ++i){
683 for(
int j=0 ; j<Npix_y_input ; ++j){
684 image_padded[ (i+nborder_x)*n_x + j + nborder_y] = map_in(i,j);
688 fftw_execute(image_to_fft);
691 assert(fft_psf.size() == fft_padded.size());
693 for(std::complex<double> &a : fft_padded) a *= fft_psf[i++];
696 fftw_execute(fft_to_image);
700 for(
int i=0 ; i< Npix_x_input ; ++i){
701 for(
int j=0 ; j< Npix_y_input ; ++j){
702 map_out(i,j) = image_padded[ (i+nborder_x)*n_x + j + nborder_y]/N;
714 template <
typename T>
718 if(sqrt_noise_power.size()==0)
return;
720 if(pmap.getNx() != pmap.getNy()){
721 std::cout <<
"Observation::CorrelateNoise() Doesn't work on nonsquare maps" << std::endl;
722 throw std::runtime_error(
"nonsquare");
724 if(pmap.getNx() != Npix_x_output){
725 std::cout <<
"Observation::CorrelateNoise() Map must have the same dimensions as the observation." << std::endl;
726 throw std::runtime_error(
"nonsquare");
730 assert(Npix_x_output == Npix_y_output);
731 size_t Npix = Npix_x_output;
732 size_t ncorr_big_zeropad_Npixels = Npix + side_ncorr;
733 long Npix_zeropad = Npix + side_ncorr;
735 size_t fftsize = sqrt_noise_power.size();
738 long first_p = side_ncorr/2;
739 long last_p = first_p + (Npix-1);
742 for (
int i = 0; i < Npix_zeropad*Npix_zeropad; i++)
744 long ix = i/Npix_zeropad;
745 long iy = i%Npix_zeropad;
746 if (ix >= first_p && ix <= last_p && iy >= first_p && iy <= last_p)
747 noise_in_zeropad[i] = pmap[(ix-side_ncorr/2)*Npix+(iy-side_ncorr/2)];
749 noise_in_zeropad[i] = 0.;
752 fftw_execute(p_noise_r2c);
755 for (
unsigned long i = 0; i < fftsize ; i++)
757 size_t ix = i/(Npix_zeropad/2+1);
758 size_t iy = i%(Npix_zeropad/2+1);
759 if (ix>Npix_zeropad/2)
760 noise_fft_image[i] *= sqrt_noise_power[(ncorr_big_zeropad_Npixels-(Npix_zeropad-ix))*(ncorr_big_zeropad_Npixels/2+1)+iy];
762 noise_fft_image[i] *= sqrt_noise_power[ix*(ncorr_big_zeropad_Npixels/2+1)+iy];
768 fftw_execute(p_noise_c2r);
771 for (
unsigned long i = 0; i < Npix_zeropad*Npix_zeropad; i++)
773 size_t ix = i/Npix_zeropad;
774 size_t iy = i%Npix_zeropad;
775 if (ix >= first_p && ix <= last_p && iy >= first_p && iy <= last_p)
777 int ii = (ix-side_ncorr/2)*Npix + (iy-side_ncorr/2);
778 pmap[ii] = noise_image_out[i]/(Npix_zeropad*Npix_zeropad);
792 template <
typename T>
802 assert(map_in.getNx() == Npix_x_input);
803 assert(map_in.getNy() == Npix_y_input);
805 if(cosmic)
throw std::runtime_error(
"cosmics not implemented");
806 if (fabs(map_in.getResolution()*psf_oversample - pix_size) > pix_size*1.0e-5)
808 std::cout <<
"The resolution of the input map is different from the one of the simulated instrument in Observation::Convert!" << std::endl;
809 throw std::runtime_error(
"The resolution of the input map is different from the one of the simulated instrument!");
816 , Npix_y_input, pix_size);
820 ApplyPSF<T>(map_in,map_scratch);
822 downsample<T>(map_scratch,map_out);
826 downsample<T>(map_in,map_out);
830 if (noise ==
true) AddNoise<T>(map_out,error_map,ran,
true);
838 template <
typename T>
844 if(pmap.getNx() != pmap.getNy()){
845 std::cerr <<
"Observation::AddNoise() Doesn't work on nonsquare maps" << std::endl;
846 throw std::runtime_error(
"nonsquare");
848 if(pmap.getUnits() != PixelMapUnits::count_per_sec){
849 std::cerr <<
"Units need to be in counts per second in Observation::AddNoise." << std::endl;
850 throw std::runtime_error(
"wrong units.");
853 double res_in_arcsec = pmap.getResolution() / arcsecTOradians;
854 double back_mean = background_flux * res_in_arcsec*res_in_arcsec * exp_time ;
856 double var_readout = exp_num*read_out_noise*read_out_noise;
861 size_t N = pmap.size();
862 for (
unsigned long i = 0; i < N ; i++)
864 norm_map = pmap[i]*exp_time;
865 if (norm_map+back_mean > 500.)
867 double sdv = sqrt(var_readout + norm_map + back_mean);
868 noise_map[i] = ran.
gauss()*sdv/exp_time;
869 error_map[i] = sdv*sdv/exp_time/exp_time;
874 int k = ran.poisson(norm_map + back_mean);
875 double noise = ran.
gauss()*sqrt(var_readout);
877 noise_map[i] = (k + noise - back_mean - norm_map)/exp_time;
878 error_map[i] = (back_mean + norm_map + var_readout)/exp_time;
882 CorrelateNoise(noise_map);
889 template <
typename T>
897 PixelMapUnits units = pmap.getUnits();
898 if(units == PixelMapUnits::count_per_sec)
return;
900 if(units == PixelMapUnits::surfb){
901 Q = e_per_s_to_ergs_s_cm2;
902 }
else if(pmap.getUnits() == PixelMapUnits::ADU){
905 std::cerr <<
"Map needs to be in photon flux units." << std::endl;
906 throw std::runtime_error(
"wrong units");
910 pmap.ChangeUnits(PixelMapUnits::count_per_sec);
915 template <
typename T>
916 void Observation::ToSurfaceBrightness(
PixelMap<T> &pmap)
920 if(pmap.getUnits() == PixelMapUnits::count_per_sec){
921 Q = 1.0/e_per_s_to_ergs_s_cm2 ;
922 }
else if(pmap.getUnits() == PixelMapUnits::ADU){
923 Q = 1.0/gain/e_per_s_to_ergs_s_cm2;
925 std::cerr <<
"Map needs to be in photon flux units." << std::endl;
926 throw std::runtime_error(
"wrong units");
930 pmap.ChangeUnits(PixelMapUnits::surfb);
935 template <
typename T>
941 pmap.ChangeUnits(PixelMapUnits::ADU);