8#ifndef IMAGE_PROCESSING_H_
9#define IMAGE_PROCESSING_H_
16#include "image_info.h"
32enum 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};
34enum class UnitType {counts_x_sec, flux} ;
40 Obs(
size_t Npix_xx,
size_t Npix_yy
48 size_t getNxInput()
const {
return Npix_x_input;}
49 size_t getNyInput()
const {
return Npix_y_input;}
51 size_t getNxOutput()
const {
return Npix_x_output;}
52 size_t getNyOutput()
const {
return Npix_y_output;}
54 std::valarray<double> getPSF(){
return map_psf;}
56 void setPSF(std::string psf_file,
double resolution=0);
60 void setSeeing(
double s
63 void rotatePSF(
double theta
69 void coaddPSF(
double f
79 float getPixelSize()
const {
return pix_size;}
80 void setNoiseCorrelation(std::string nc_file);
95 virtual float getBackgroundNoise()
const = 0;
98 virtual double mag_to_counts(
double m)
const = 0;
99 virtual double counts_to_mag(
double flux)
const = 0;
100 virtual double zeropoint()
const = 0;
101 virtual void setZeropoint(
double zpoint) = 0;
107 template <
typename T>
112 size_t Npix_x_output,Npix_y_output;
114 size_t Npix_x_input,Npix_y_input;
116 float psf_oversample;
117 template <
typename T>
121 double input_psf_pixel_size;
125 std::valarray<double> map_psf;
126 std::valarray<double> map_psfo;
128 std::vector<std::complex<double> > fft_psf;
129 std::vector<std::complex<double> > fft_padded;
130 std::vector<double> image_padded;
131 std::vector<double> sqrt_noise_power;
134 size_t nborder_x = 0;
135 size_t nborder_y = 0;
141 fftw_plan image_to_fft;
142 fftw_plan fft_to_image;
145 std::vector<std::complex<double> > noise_fft_image;
146 std::vector<double> noise_in_zeropad;
147 fftw_plan p_noise_r2c;
148 std::vector<double> noise_image_out;
149 fftw_plan p_noise_c2r;
169 double zero_point = 24.4;
180 double sigma_background2;
184 template <
typename T>
196 ,
double resolution = 0.1*arcsecTOradians
202 ,
const std::vector<double> &exposure_times
208 ,
const std::vector<double> &exposure_times
211 ,
double background_sigma
212 ,
double calibration_exposure_time
217 template <
typename T>
223 template <
typename T>
229 template <
typename T>
239 double mag_to_counts(
double m)
const{
240 if(m == 100)
return 0;
241 return pow(10,-0.4*(m + zero_point));
243 double counts_to_mag(
double flux)
const{
244 if(flux <=0)
return 100;
245 return -2.5 * log10(flux) - zero_point;
248 double zeropoint()
const {
return zero_point;}
249 void setZeropoint(
double zpoint){zero_point=zpoint;}
253 double dt = Utilities::vec_sum(t_exp);
255 return sqrt( sigma_background2 / dt );
260 std::vector<double> t_exp;
276 Observation(Telescope tel_name,
double exposure_time,
int exposure_num
277 ,
size_t Npix_x,
size_t Npix_y,
float oversample);
278 Observation(
float diameter,
float transmission,
float exp_time,
int exp_num,
float back_mag,
float read_out_noise
279 ,
size_t Npix_x,
size_t Npix_y,
double pix_size,
float seeing = 0.);
280 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.);
282 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);
283 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.);
287 float getExpTime()
const {
return exp_time;}
288 int getExpNum()
const {
return exp_num;}
289 float getBackMag()
const {
return back_mag;}
291 float getRon()
const {
return read_out_noise;}
294 float getZeropoint()
const {
return mag_zeropoint;}
295 void setZeropoint(
double zpoint){mag_zeropoint=zpoint;}
297 float getBackgroundNoise(
float resolution, UnitType unit = UnitType::counts_x_sec)
const;
300 template <
typename T>
304 template <
typename T>
318 void setPixelSize(
float pixel_size){pix_size=pixel_size;}
320 double mag_to_counts(
double m)
const {
321 if(m == 100)
return 0;
322 return pow(10,-0.4*(m - mag_zeropoint));
324 double counts_to_mag(
double flux)
const{
325 if(flux <=0)
return 100;
326 return -2.5 * log10(flux) + mag_zeropoint;
328 double zeropoint()
const{
329 return mag_zeropoint;
339 float read_out_noise;
343 float e_per_s_to_ergs_s_cm2;
344 float background_flux;
348 template <
typename T>
350 template <
typename T>
352 template <
typename T>
356void pixelize(
double *map,
long Npixels,
double range,
double *center
357 ,
ImageInfo *imageinfo,
int Nimages,
bool constant_sb,
bool cleanmap
358 ,
bool write_for_skymaker =
false, std::string filename=
"");
359void _SplitFluxIntoPixels(
TreeHndl ptree,
Branch *leaf,
double *leaf_sb);
365 void ReadFileNames(std::string dir,
const std::string filespec
366 ,std::vector<std::string> & filenames
367 ,
const std::string file_non_spec =
" "
368 ,
bool verbose =
false);
375 MultiGridSmoother(
double center[],std::size_t Nx,std::size_t Ny,
double resolution);
387 void add_particles(std::vector<PosType> x,std::vector<PosType> y);
394 std::vector<PixelMap<double> > maps;
395 std::vector<Utilities::Interpolator<PixelMap<double> >> interpolators;
404 double dt = Utilities::vec_sum(t_exp);
407 int missing_frame = (int)(ran()*t_exp.size());
409 dt -= t_exp[missing_frame];
412 for(
auto &p : pmap.data() ){
413 if(p>0) p = ran.poisson(p*dt)/dt;
422 if(pmap.getNx() != pmap.getNy()){
423 std::cerr <<
"Observation::AddNoise() Doesn't work on nonsquare maps" << std::endl;
424 throw std::runtime_error(
"nonsquare");
427 double dt = Utilities::vec_sum(t_exp);
428 int missing_frame = -1;
432 missing_frame = (int)(ran()*t_exp.size());
433 dt -= t_exp[missing_frame];
437 double inv_sigma2 = (dt)/sigma_background2;
438 size_t N = pmap.size();
439 for (
unsigned long i = 0; i < N ; i++){
440 error_map[i] = inv_sigma2;
446 double t = (int)(ran()*dt);
447 for(
int j=0 ; j<100 ; ++j){
456 if(i != missing_frame) cosmics(error_map,t_exp[i]/sigma_background2,1,ran);
465 for (
unsigned long i = 0; i < N ; i++){
466 pmap[i] = ran.poisson(MAX<float>(pmap[i] * dt,0))/dt;
467 pmap[i] += ran.
gauss() / sqrt(error_map[i]);
469 error_map[i] = sqrt( 1.0 / error_map[i] + MAX<float>(pmap[i] / dt,0) ) ;
482 size_t N = error_map.size();
483 size_t Nx = error_map.getNx();
484 size_t Ny = error_map.getNy();
491 size_t no = (size_t)(N*ran());
492 double theta = 2*PI*ran();
493 double c=cos(theta),s=sin(theta);
498 double L = MAX<double>(length/tan(PI*ran()/2),1);
502 double t = tan(theta);
505 long x1 = (long)(io + L * c );
507 x1 = MAX<long>(0,x1);
508 x1 = MIN<long>(Nx-1,x1);
509 for(x = io ; x != x1 ; x = x + sgn){
510 y = MAX<long>((
long)(t*(x-io) + jo),0);
511 y= MIN<long>(y,Ny-2);
512 error_map(x,y) += -inv_sigma2;
513 error_map(x,y+1) += -inv_sigma2;
516 long y1 = (long)(jo + L * s );
518 y1 = MAX<long>(0,y1);
519 y1 = MIN<long>(Ny-1,y1);
520 for(y = jo ; y != y1 ; y = y + sgn){
521 x = MAX<long>((
long)((y-jo)/t + io),0);
522 x = MIN<long>(x,Nx-2);
523 error_map(x,y) += -inv_sigma2;
524 error_map(x+1,y) += -inv_sigma2;
541 assert(map_in.getNx() == Npix_x_input);
542 assert(map_in.getNy() == Npix_y_input);
544 if (fabs(map_in.getResolution()*psf_oversample - pix_size) > pix_size*1.0e-5)
546 std::cout <<
"The resolution of the input map is different from the one of the simulated instrument in Observation::Convert!" << std::endl;
547 throw std::runtime_error(
"The resolution of the input map is different from the one of the simulated instrument!");
555 , Npix_y_input, pix_size);
556 ApplyPSF<T>(map_in,map_scratch);
557 downsample<T>(map_scratch,map_out);
559 downsample<T>(map_in,map_out);
562 if (noise ==
true)
AddNoise<T>(map_out,error_map,ran,cosmic);
573 input_psf_pixel_size = psf_map.getResolution();
575 if( (input_psf_pixel_size - pix_size/psf_oversample)/input_psf_pixel_size > 1.0e-3){
576 std::cout <<
"Obs::setPSF() - psf is not resolved." << std::endl;
577 throw std::runtime_error(
"");
580 map_psf.resize(psf_map.size());
581 for(
size_t i=0 ; i<psf_map.size() ; ++i){
582 map_psf[i] = psf_map[i];
585 std::vector<size_t> size = {psf_map.getNx(),psf_map.getNy()};
590 double amax = map_psf[0];
591 for(
auto &a : map_psf){
592 if(a > amax){imax=i;amax=a;}
595 max_y = imax % size[0];
596 max_x = imax / size[0];
599 long Ly = Lx = 2*MIN<double>( MIN<double>(max_y,size[1]-max_y)
600 ,MIN<double>(max_x,size[0]-max_x));
602 std::valarray<double> tmp(Lx*Ly);
604 for(
long i=max_x - Lx/2 ; i<max_x + Lx/2 ;++i){
607 for(
long j=max_y - Ly/2 ; j<max_y + Ly/2 ;++j){
609 tmp[ii + Lx*jj] = map_psf[j + size[1] * i];
615 std::swap(tmp,map_psf);
624 assert(map_in.getNx() == Npix_x_input);
625 assert(map_in.getNy() == Npix_y_input);
626 assert(map_out.getNx() == Npix_x_output);
627 assert(map_out.getNy() == Npix_y_output);
629 if(psf_oversample == 1){
630 size_t n=map_in.size();
631 for(
size_t i=0; i<n ; ++i ) map_out[i] = map_in[i];
644 for(
size_t i=0 ; i<Npix_x_input ; ++i){
645 size_t ii = MIN<long>(i / psf_oversample + 0.5, Npix_x_output - 1 ) ;
646 for(
size_t j=0 ; j<Npix_y_input ; ++j){
647 size_t jj = MIN<long>(j / psf_oversample + 0.5, Npix_y_output - 1 ) ;
649 map_out(ii,jj) += map_in(i,j);
662 if(map_in.getNx() != map_in.getNy()){
663 std::cout <<
"Obs::ApplyPSF() Doesn't work on nonsquare maps" << std::endl;
664 throw std::runtime_error(
"nonsquare");
666 if(map_in.getNx() != Npix_y_input){
667 std::cout <<
"Obs::ApplyPSF() Input map it the wrong size." << std::endl;
668 throw std::runtime_error(
"nonsquare");
670 if(map_in.getNx() != map_out.getNx()){
671 std::cout <<
"Obs::ApplyPSF() Input map output are not the same size." << std::endl;
672 throw std::runtime_error(
"nonsquare");
676 if (map_psf.size() == 0){
682 map_out.
smooth(seeing/2.355);
689 map_out.ChangeUnits(map_in.getUnits());
692 for(
double &a :image_padded) a=0;
693 for(
int i=0 ; i<Npix_x_input ; ++i){
694 for(
int j=0 ; j<Npix_y_input ; ++j){
695 image_padded[ (i+nborder_x)*n_x + j + nborder_y] = map_in(i,j);
699 fftw_execute(image_to_fft);
702 assert(fft_psf.size() == fft_padded.size());
704 for(std::complex<double> &a : fft_padded) a *= fft_psf[i++];
707 fftw_execute(fft_to_image);
711 for(
int i=0 ; i< Npix_x_input ; ++i){
712 for(
int j=0 ; j< Npix_y_input ; ++j){
713 map_out(i,j) = image_padded[ (i+nborder_x)*n_x + j + nborder_y]/N;
729 if(sqrt_noise_power.size()==0)
return;
731 if(pmap.getNx() != pmap.getNy()){
732 std::cout <<
"Observation::CorrelateNoise() Doesn't work on nonsquare maps" << std::endl;
733 throw std::runtime_error(
"nonsquare");
735 if(pmap.getNx() != Npix_x_output){
736 std::cout <<
"Observation::CorrelateNoise() Map must have the same dimensions as the observation." << std::endl;
737 throw std::runtime_error(
"nonsquare");
741 assert(Npix_x_output == Npix_y_output);
742 size_t Npix = Npix_x_output;
743 size_t ncorr_big_zeropad_Npixels = Npix + side_ncorr;
744 long Npix_zeropad = Npix + side_ncorr;
746 size_t fftsize = sqrt_noise_power.size();
749 long first_p = side_ncorr/2;
750 long last_p = first_p + (Npix-1);
753 for (
int i = 0; i < Npix_zeropad*Npix_zeropad; i++)
755 long ix = i/Npix_zeropad;
756 long iy = i%Npix_zeropad;
757 if (ix >= first_p && ix <= last_p && iy >= first_p && iy <= last_p)
758 noise_in_zeropad[i] = pmap[(ix-side_ncorr/2)*Npix+(iy-side_ncorr/2)];
760 noise_in_zeropad[i] = 0.;
763 fftw_execute(p_noise_r2c);
766 for (
unsigned long i = 0; i < fftsize ; i++)
768 size_t ix = i/(Npix_zeropad/2+1);
769 size_t iy = i%(Npix_zeropad/2+1);
770 if (ix>Npix_zeropad/2)
771 noise_fft_image[i] *= sqrt_noise_power[(ncorr_big_zeropad_Npixels-(Npix_zeropad-ix))*(ncorr_big_zeropad_Npixels/2+1)+iy];
773 noise_fft_image[i] *= sqrt_noise_power[ix*(ncorr_big_zeropad_Npixels/2+1)+iy];
779 fftw_execute(p_noise_c2r);
782 for (
unsigned long i = 0; i < Npix_zeropad*Npix_zeropad; i++)
784 size_t ix = i/Npix_zeropad;
785 size_t iy = i%Npix_zeropad;
786 if (ix >= first_p && ix <= last_p && iy >= first_p && iy <= last_p)
788 int ii = (ix-side_ncorr/2)*Npix + (iy-side_ncorr/2);
789 pmap[ii] = noise_image_out[i]/(Npix_zeropad*Npix_zeropad);
813 assert(map_in.getNx() == Npix_x_input);
814 assert(map_in.getNy() == Npix_y_input);
816 if(cosmic)
throw std::runtime_error(
"cosmics not implemented");
817 if (fabs(map_in.getResolution()*psf_oversample - pix_size) > pix_size*1.0e-5)
819 std::cout <<
"The resolution of the input map is different from the one of the simulated instrument in Observation::Convert!" << std::endl;
820 throw std::runtime_error(
"The resolution of the input map is different from the one of the simulated instrument!");
827 , Npix_y_input, pix_size);
828 ApplyPSF<T>(map_in,map_scratch);
829 downsample<T>(map_scratch,map_out);
831 downsample<T>(map_in,map_out);
835 if (noise ==
true)
AddNoise<T>(map_out,error_map,ran,
true);
849 if(pmap.getNx() != pmap.getNy()){
850 std::cerr <<
"Observation::AddNoise() Doesn't work on nonsquare maps" << std::endl;
851 throw std::runtime_error(
"nonsquare");
853 if(pmap.getUnits() != PixelMapUnits::count_per_sec){
854 std::cerr <<
"Units need to be in counts per second in Observation::AddNoise." << std::endl;
855 throw std::runtime_error(
"wrong units.");
858 double res_in_arcsec = pmap.getResolution() / arcsecTOradians;
859 double back_mean = background_flux * res_in_arcsec*res_in_arcsec * exp_time ;
861 double var_readout = exp_num*read_out_noise*read_out_noise;
866 size_t N = pmap.size();
867 for (
unsigned long i = 0; i < N ; i++)
869 norm_map = pmap[i]*exp_time;
870 if (norm_map+back_mean > 500.)
872 double sdv = sqrt(var_readout + norm_map + back_mean);
873 noise_map[i] = ran.
gauss()*sdv/exp_time;
874 error_map[i] = sdv*sdv/exp_time/exp_time;
879 int k = ran.poisson(norm_map + back_mean);
880 double noise = ran.
gauss()*sqrt(var_readout);
882 noise_map[i] = (k + noise - back_mean - norm_map)/exp_time;
883 error_map[i] = (back_mean + norm_map + var_readout)/exp_time;
887 CorrelateNoise(noise_map);
902 PixelMapUnits units = pmap.getUnits();
903 if(units == PixelMapUnits::count_per_sec)
return;
905 if(units == PixelMapUnits::surfb){
906 Q = e_per_s_to_ergs_s_cm2;
907 }
else if(pmap.getUnits() == PixelMapUnits::ADU){
910 std::cerr <<
"Map needs to be in photon flux units." << std::endl;
911 throw std::runtime_error(
"wrong units");
915 pmap.ChangeUnits(PixelMapUnits::count_per_sec);
921void Observation::ToSurfaceBrightness(
PixelMap<T> &pmap)
925 if(pmap.getUnits() == PixelMapUnits::count_per_sec){
926 Q = 1.0/e_per_s_to_ergs_s_cm2 ;
927 }
else if(pmap.getUnits() == PixelMapUnits::ADU){
928 Q = 1.0/gain/e_per_s_to_ergs_s_cm2;
930 std::cerr <<
"Map needs to be in photon flux units." << std::endl;
931 throw std::runtime_error(
"wrong units");
935 pmap.ChangeUnits(PixelMapUnits::surfb);
946 pmap.ChangeUnits(PixelMapUnits::ADU);
Warning: Not tested yet. Class for doing adaptive smoothing using multiply resolution grids.
Definition image_processing.h:373
PosType getLowestRes()
resolution of coarsest grid from which interpolation is done
Definition image_processing.h:384
void add_particles(std::vector< PosType > x, std::vector< PosType > y)
Add particles to the map. These do not need to be kept in memory after they are added.
Definition pixelize.cpp:272
PosType getHighestRes()
resolution of finest grid from which interpolation is done
Definition image_processing.h:382
void output_map(PixelMap< double > &map, int Nsmooth)
Output a map at the resolution of the map smoothed so that no superpixel as less than Nsmooth particl...
Definition pixelize.cpp:290
MultiGridSmoother(double center[], std::size_t Nx, std::size_t Ny, double resolution)
Definition pixelize.cpp:222
It creates a realistic image from the output of a ray-tracing simulation.
Definition image_processing.h:165
ObsVIS(size_t Npix_x, size_t Npix_y, int oversample, double resolution=0.1 *arcsecTOradians)
exposure times are set to wide survey expectations
Definition observation.cpp:17
void AddNoise(PixelMap< T > &pmap, PixelMap< T > &error_map, Utilities::RandomNumbers_NR &ran, bool cosmic=true)
Applies noise (read-out + Poisson) on an image, returns noise map.
Definition image_processing.h:418
void AddPoisson(PixelMap< T > &pmap, Utilities::RandomNumbers_NR &ran)
add poisson noise to an image that is in units of electrons
Definition image_processing.h:400
float getBackgroundNoise() const
returns std of pixels in e-
Definition image_processing.h:252
It creates a realistic image from the output of a ray-tracing simulation.
Definition image_processing.h:274
float getRon() const
read-out noise in electrons/pixel
Definition image_processing.h:291
float getBackgroundNoise(float resolution, UnitType unit=UnitType::counts_x_sec) const
pixel size in radians
Definition observation.cpp:726
void setExpTime(float time)
returns factor by which code image units need to be multiplied by to get flux units
Definition image_processing.h:317
void Convert(PixelMap< T > &map_in, PixelMap< T > &map_out, PixelMap< T > &error_map, bool psf, bool noise, Utilities::RandomNumbers_NR &ran, bool cosmic=false)
Converts the input map to a realistic image.
Definition image_processing.h:804
void AddNoise(PixelMap< T > &pmap, PixelMap< T > &error_map, Utilities::RandomNumbers_NR &ran, bool dummy)
Applies realistic noise (read-out + Poisson) on an image, returns noise map.
Definition image_processing.h:844
float getSeeing() const
seeing in arcsecs
Definition image_processing.h:293
Observation(Telescope tel_name, double exposure_time, int exposure_num, size_t Npix_x, size_t Npix_y, float oversample)
Creates an observation setup that mimics a known instrument.
Definition observation.cpp:417
Image structure that can be manipulated and exported to/from fits files.
Definition pixelmap.h:42
void Renormalize(T factor)
Multiplies the whole map by a scalar factor.
Definition pixelmap.cpp:506
void smooth(double sigma)
Smoothes a map with a Gaussian kernel of width sigma (in arcseconds)
Definition pixelmap.cpp:1453
Base class for all sources.
Definition source.h:44
This is a class for generating random numbers. It simplifies and fool proofs initialization and allow...
Definition utilities_slsim.h:1059
PosType gauss()
generates a Gaussian distributed number with unit variance by polar Box-Muller transform
Definition utilities_slsim.h:1068
Definition utilities.h:39
void ReadFileNames(std::string dir, const std::string filespec, std::vector< std::string > &filenames, const std::string file_non_spec=" ", bool verbose=false)
Reads all the fits files in a directory into a vector of PixelMaps.
Definition pixelize.cpp:180
The box representing a branch of a binary tree structure. Used specifically in TreeStruct for organiz...
Definition point.h:628
Structure for storing information about images or curves.
Definition image_info.h:20
Class for representing points or vectors in 2 dimensions. Not that the dereferencing operator is over...
Definition point.h:48
Tree: Exported struct.
Definition Tree.h:31