8#ifndef IMAGE_PROCESSING_H_
9#define IMAGE_PROCESSING_H_
16#include "image_info.h"
33enum 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};
35enum class UnitType {counts_x_sec, flux} ;
41 Obs(
size_t Npix_xx,
size_t Npix_yy
49 size_t getNxInput()
const {
return Npix_x_input;}
50 size_t getNyInput()
const {
return Npix_y_input;}
52 size_t getNxOutput()
const {
return Npix_x_output;}
53 size_t getNyOutput()
const {
return Npix_y_output;}
55 std::valarray<double> getPSF(){
return map_psf;}
57 void setPSF(std::string psf_file,
double resolution=0);
59 void setPSF(PixelMap<T> &psf_map);
61 void setSeeing(
double s
64 void rotatePSF(
double theta
70 void coaddPSF(
double f
78 void ApplyPSF(PixelMap<T> &map_in,PixelMap<T> &map_out);
80 float getPixelSize()
const {
return pix_size;}
81 void setNoiseCorrelation(std::string nc_file);
96 virtual float getBackgroundNoise()
const = 0;
108 template <
typename T>
109 void CorrelateNoise(PixelMap<T> &pmap);
113 size_t Npix_x_output,Npix_y_output;
115 size_t Npix_x_input,Npix_y_input;
117 float psf_oversample;
118 template <
typename T>
119 void downsample(PixelMap<T> &map_in,PixelMap<T> &map_out)
const;
122 double input_psf_pixel_size;
126 std::valarray<double> map_psf;
127 std::valarray<double> map_psfo;
129 std::vector<std::complex<double> > fft_psf;
130 std::vector<std::complex<double> > fft_padded;
131 std::vector<double> image_padded;
132 std::vector<double> sqrt_noise_power;
135 size_t nborder_x = 0;
136 size_t nborder_y = 0;
142 fftw_plan image_to_fft;
143 fftw_plan fft_to_image;
146 std::vector<std::complex<double> > noise_fft_image;
147 std::vector<double> noise_in_zeropad;
148 fftw_plan p_noise_r2c;
149 std::vector<double> noise_image_out;
150 fftw_plan p_noise_c2r;
181 double sigma_background2;
185 template <
typename T>
197 ,
double resolution = 0.1*arcsecTOradians
203 ,
const std::vector<double> &exposure_times
209 ,
const std::vector<double> &exposure_times
212 ,
double background_sigma
213 ,
double calibration_exposure_time
218 template <
typename T>
224 template <
typename T>
230 template <
typename T>
254 double dt = Utilities::vec_sum(t_exp);
256 return sqrt( sigma_background2 / dt );
261 std::vector<double> t_exp;
277 Observation(Telescope tel_name,
double exposure_time,
int exposure_num
278 ,
size_t Npix_x,
size_t Npix_y,
float oversample);
279 Observation(
float diameter,
float transmission,
float exp_time,
int exp_num,
float back_mag,
float read_out_noise
280 ,
size_t Npix_x,
size_t Npix_y,
double pix_size,
float seeing = 0.);
281 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.);
283 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);
284 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.);
288 float getExpTime()
const {
return exp_time;}
289 int getExpNum()
const {
return exp_num;}
290 float getBackMag()
const {
return back_mag;}
292 float getRon()
const {
return read_out_noise;}
295 float getZeropoint()
const {
return mag_zeropoint;}
296 void setZeropoint(
double zpoint){mag_zeropoint=zpoint;}
298 float getBackgroundNoise(
float resolution, UnitType unit = UnitType::counts_x_sec)
const;
301 template <
typename T>
302 void AddNoise(PixelMap<T> &pmap,PixelMap<T> &error_map
303 ,Utilities::RandomNumbers_NR &ran,
bool dummy);
305 template <
typename T>
306 void Convert(PixelMap<T> &map_in
307 ,PixelMap<T> &map_out
308 ,PixelMap<T> &error_map
311 ,Utilities::RandomNumbers_NR &ran
319 void setPixelSize(
float pixel_size){pix_size=pixel_size;}
321 double mag_to_counts(
double m)
const {
322 if(m == 100)
return 0;
323 return pow(10,-0.4*(m - mag_zeropoint));
325 double counts_to_mag(
double flux)
const{
326 if(flux <=0)
return 100;
327 return -2.5 * log10(flux) + mag_zeropoint;
329 double zeropoint()
const{
330 return mag_zeropoint;
340 float read_out_noise;
344 float e_per_s_to_ergs_s_cm2;
345 float background_flux;
349 template <
typename T>
350 void ToCounts(PixelMap<T> &pmap);
351 template <
typename T>
352 void ToSurfaceBrightness(PixelMap<T> &pmap);
353 template <
typename T>
354 void ToADU(PixelMap<T> &pmap);
357void pixelize(
double *map,
long Npixels,
double range,
double *center
358 ,
ImageInfo *imageinfo,
int Nimages,
bool constant_sb,
bool cleanmap
359 ,
bool write_for_skymaker =
false, std::string filename=
"");
360void _SplitFluxIntoPixels(TreeHndl ptree,
Branch *leaf,
double *leaf_sb);
366 void ReadFileNames(std::string dir,
const std::string filespec
367 ,std::vector<std::string> & filenames
368 ,
const std::string file_non_spec =
" "
369 ,
bool verbose =
false);
376 MultiGridSmoother(
double center[],std::size_t Nx,std::size_t Ny,
double resolution);
388 void add_particles(std::vector<PosType> x,std::vector<PosType> y);
395 std::vector<PixelMap<double> > maps;
396 std::vector<Utilities::Interpolator<PixelMap<double> >> interpolators;
405 double dt = Utilities::vec_sum(t_exp);
408 int missing_frame = (int)(ran()*t_exp.size());
410 dt -= t_exp[missing_frame];
413 for(
auto &p : pmap.data() ){
414 if(p>0) p = ran.poisson(p*dt)/dt;
423 if(pmap.getNx() != pmap.getNy()){
424 std::cerr <<
"Observation::AddNoise() Doesn't work on nonsquare maps" << std::endl;
425 throw std::runtime_error(
"nonsquare");
428 double dt = Utilities::vec_sum(t_exp);
429 int missing_frame = -1;
433 missing_frame = (int)(ran()*t_exp.size());
434 dt -= t_exp[missing_frame];
438 double inv_sigma2 = (dt)/sigma_background2;
439 size_t N = pmap.size();
440 for (
unsigned long i = 0; i < N ; i++){
441 error_map[i] = inv_sigma2;
447 double t = (int)(ran()*dt);
448 for(
int j=0 ; j<100 ; ++j){
457 if(i != missing_frame) cosmics(error_map,t_exp[i]/sigma_background2,1,ran);
466 for (
unsigned long i = 0; i < N ; i++){
467 pmap[i] = ran.poisson(MAX<float>(pmap[i] * dt,0))/dt;
468 pmap[i] += ran.
gauss() / sqrt(error_map[i]);
470 error_map[i] = sqrt( 1.0 / error_map[i] + MAX<float>(pmap[i] / dt,0) ) ;
483 size_t N = error_map.size();
484 size_t Nx = error_map.getNx();
485 size_t Ny = error_map.getNy();
492 size_t no = (size_t)(N*ran());
493 double theta = 2*PI*ran();
494 double c=cos(theta),s=sin(theta);
499 double L = MAX<double>(length/tan(PI*ran()/2),1);
503 double t = tan(theta);
506 long x1 = (long)(io + L * c );
508 x1 = MAX<long>(0,x1);
509 x1 = MIN<long>(Nx-1,x1);
510 for(x = io ; x != x1 ; x = x + sgn){
511 y = MAX<long>((
long)(t*(x-io) + jo),0);
512 y= MIN<long>(y,Ny-2);
513 error_map(x,y) += -inv_sigma2;
514 error_map(x,y+1) += -inv_sigma2;
517 long y1 = (long)(jo + L * s );
519 y1 = MAX<long>(0,y1);
520 y1 = MIN<long>(Ny-1,y1);
521 for(y = jo ; y != y1 ; y = y + sgn){
522 x = MAX<long>((
long)((y-jo)/t + io),0);
523 x = MIN<long>(x,Nx-2);
524 error_map(x,y) += -inv_sigma2;
525 error_map(x+1,y) += -inv_sigma2;
542 assert(map_in.getNx() == Npix_x_input);
543 assert(map_in.getNy() == Npix_y_input);
545 if (fabs(map_in.getResolution()*psf_oversample - pix_size) > pix_size*1.0e-5)
547 std::cout <<
"The resolution of the input map is different from the one of the simulated instrument in Observation::Convert!" << std::endl;
548 throw std::runtime_error(
"The resolution of the input map is different from the one of the simulated instrument!");
554 PixelMap<T> map_scratch(Point_2d(0,0).x
556 , Npix_y_input, pix_size);
559 ApplyPSF<T>(map_in,map_scratch);
561 downsample<T>(map_scratch,map_out);
564 downsample<T>(map_in,map_out);
567 if (noise ==
true)
AddNoise<T>(map_out,error_map,ran,cosmic);
578 input_psf_pixel_size = psf_map.getResolution();
580 if( (input_psf_pixel_size - pix_size/psf_oversample)/input_psf_pixel_size > 1.0e-3){
581 std::cout <<
"Obs::setPSF() - psf is not resolved." << std::endl;
582 std::cout << (input_psf_pixel_size - pix_size/psf_oversample)/input_psf_pixel_size << std::endl;
583 throw std::runtime_error(
"");
586 map_psf.resize(psf_map.size());
587 for(
size_t i=0 ; i<psf_map.size() ; ++i){
588 map_psf[i] = psf_map[i];
591 std::vector<size_t> size = {psf_map.getNx(),psf_map.getNy()};
596 double amax = map_psf[0];
597 for(
auto &a : map_psf){
598 if(a > amax){imax=i;amax=a;}
601 max_y = imax % size[0];
602 max_x = imax / size[0];
605 long Ly = Lx = 2*MIN<double>( MIN<double>(max_y,size[1]-max_y)
606 ,MIN<double>(max_x,size[0]-max_x));
608 std::valarray<double> tmp(Lx*Ly);
610 for(
long i=max_x - Lx/2 ; i<max_x + Lx/2 ;++i){
613 for(
long j=max_y - Ly/2 ; j<max_y + Ly/2 ;++j){
615 tmp[ii + Lx*jj] = map_psf[j + size[1] * i];
621 std::swap(tmp,map_psf);
630 assert(map_in.getNx() == Npix_x_input);
631 assert(map_in.getNy() == Npix_y_input);
632 assert(map_out.getNx() == Npix_x_output);
633 assert(map_out.getNy() == Npix_y_output);
635 if(psf_oversample == 1){
636 size_t n=map_in.size();
637 for(
size_t i=0; i<n ; ++i ) map_out[i] = map_in[i];
650 for(
size_t i=0 ; i<Npix_x_input ; ++i){
651 size_t ii = MIN<long>(i / psf_oversample + 0.5, Npix_x_output - 1 ) ;
652 for(
size_t j=0 ; j<Npix_y_input ; ++j){
653 size_t jj = MIN<long>(j / psf_oversample + 0.5, Npix_y_output - 1 ) ;
655 map_out(ii,jj) += map_in(i,j);
668 if(map_in.getNx() != map_in.getNy()){
669 std::cout <<
"Obs::ApplyPSF() Doesn't work on nonsquare maps" << std::endl;
670 throw std::runtime_error(
"nonsquare");
672 if(map_in.getNx() != Npix_y_input){
673 std::cout <<
"Obs::ApplyPSF() Input map it the wrong size." << std::endl;
674 throw std::runtime_error(
"nonsquare");
676 if(map_in.getNx() != map_out.getNx()){
677 std::cout <<
"Obs::ApplyPSF() Input map output are not the same size." << std::endl;
678 throw std::runtime_error(
"nonsquare");
682 if (map_psf.size() == 0){
688 map_out.
smooth(seeing/2.355);
695 map_out.ChangeUnits(map_in.getUnits());
698 for(
double &a :image_padded) a=0;
699 for(
int i=0 ; i<Npix_x_input ; ++i){
700 for(
int j=0 ; j<Npix_y_input ; ++j){
701 image_padded[ (i+nborder_x)*n_x + j + nborder_y] = map_in(i,j);
705 fftw_execute(image_to_fft);
708 assert(fft_psf.size() == fft_padded.size());
710 for(std::complex<double> &a : fft_padded) a *= fft_psf[i++];
713 fftw_execute(fft_to_image);
717 for(
int i=0 ; i< Npix_x_input ; ++i){
718 for(
int j=0 ; j< Npix_y_input ; ++j){
719 map_out(i,j) = image_padded[ (i+nborder_x)*n_x + j + nborder_y]/N;
735 if(sqrt_noise_power.size()==0)
return;
737 if(pmap.getNx() != pmap.getNy()){
738 std::cout <<
"Observation::CorrelateNoise() Doesn't work on nonsquare maps" << std::endl;
739 throw std::runtime_error(
"nonsquare");
741 if(pmap.getNx() != Npix_x_output){
742 std::cout <<
"Observation::CorrelateNoise() Map must have the same dimensions as the observation." << std::endl;
743 throw std::runtime_error(
"nonsquare");
747 assert(Npix_x_output == Npix_y_output);
748 size_t Npix = Npix_x_output;
749 size_t ncorr_big_zeropad_Npixels = Npix + side_ncorr;
750 long Npix_zeropad = Npix + side_ncorr;
752 size_t fftsize = sqrt_noise_power.size();
755 long first_p = side_ncorr/2;
756 long last_p = first_p + (Npix-1);
759 for (
int i = 0; i < Npix_zeropad*Npix_zeropad; i++)
761 long ix = i/Npix_zeropad;
762 long iy = i%Npix_zeropad;
763 if (ix >= first_p && ix <= last_p && iy >= first_p && iy <= last_p)
764 noise_in_zeropad[i] = pmap[(ix-side_ncorr/2)*Npix+(iy-side_ncorr/2)];
766 noise_in_zeropad[i] = 0.;
769 fftw_execute(p_noise_r2c);
772 for (
unsigned long i = 0; i < fftsize ; i++)
774 size_t ix = i/(Npix_zeropad/2+1);
775 size_t iy = i%(Npix_zeropad/2+1);
776 if (ix>Npix_zeropad/2)
777 noise_fft_image[i] *= sqrt_noise_power[(ncorr_big_zeropad_Npixels-(Npix_zeropad-ix))*(ncorr_big_zeropad_Npixels/2+1)+iy];
779 noise_fft_image[i] *= sqrt_noise_power[ix*(ncorr_big_zeropad_Npixels/2+1)+iy];
785 fftw_execute(p_noise_c2r);
788 for (
unsigned long i = 0; i < Npix_zeropad*Npix_zeropad; i++)
790 size_t ix = i/Npix_zeropad;
791 size_t iy = i%Npix_zeropad;
792 if (ix >= first_p && ix <= last_p && iy >= first_p && iy <= last_p)
794 int ii = (ix-side_ncorr/2)*Npix + (iy-side_ncorr/2);
795 pmap[ii] = noise_image_out[i]/(Npix_zeropad*Npix_zeropad);
819 assert(map_in.getNx() == Npix_x_input);
820 assert(map_in.getNy() == Npix_y_input);
822 if(cosmic)
throw std::runtime_error(
"cosmics not implemented");
823 if (fabs(map_in.getResolution()*psf_oversample - pix_size) > pix_size*1.0e-5)
825 std::cout <<
"The resolution of the input map is different from the one of the simulated instrument in Observation::Convert!" << std::endl;
826 throw std::runtime_error(
"The resolution of the input map is different from the one of the simulated instrument!");
833 , Npix_y_input, pix_size);
837 ApplyPSF<T>(map_in,map_scratch);
839 downsample<T>(map_scratch,map_out);
843 downsample<T>(map_in,map_out);
847 if (noise ==
true)
AddNoise<T>(map_out,error_map,ran,
true);
861 if(pmap.getNx() != pmap.getNy()){
862 std::cerr <<
"Observation::AddNoise() Doesn't work on nonsquare maps" << std::endl;
863 throw std::runtime_error(
"nonsquare");
865 if(pmap.getUnits() != PixelMapUnits::count_per_sec){
866 std::cerr <<
"Units need to be in counts per second in Observation::AddNoise." << std::endl;
867 throw std::runtime_error(
"wrong units.");
870 double res_in_arcsec = pmap.getResolution() / arcsecTOradians;
871 double back_mean = background_flux * res_in_arcsec*res_in_arcsec * exp_time ;
873 double var_readout = exp_num*read_out_noise*read_out_noise;
878 size_t N = pmap.size();
879 for (
unsigned long i = 0; i < N ; i++)
881 norm_map = pmap[i]*exp_time;
882 if (norm_map+back_mean > 500.)
884 double sdv = sqrt(var_readout + norm_map + back_mean);
885 noise_map[i] = ran.
gauss()*sdv/exp_time;
886 error_map[i] = sdv*sdv/exp_time/exp_time;
891 int k = ran.poisson(norm_map + back_mean);
892 double noise = ran.
gauss()*sqrt(var_readout);
894 noise_map[i] = (k + noise - back_mean - norm_map)/exp_time;
895 error_map[i] = (back_mean + norm_map + var_readout)/exp_time;
899 CorrelateNoise(noise_map);
914 PixelMapUnits units = pmap.getUnits();
915 if(units == PixelMapUnits::count_per_sec)
return;
917 if(units == PixelMapUnits::surfb){
918 Q = e_per_s_to_ergs_s_cm2;
919 }
else if(pmap.getUnits() == PixelMapUnits::ADU){
922 std::cerr <<
"Map needs to be in photon flux units." << std::endl;
923 throw std::runtime_error(
"wrong units");
927 pmap.ChangeUnits(PixelMapUnits::count_per_sec);
933void Observation::ToSurfaceBrightness(
PixelMap<T> &pmap)
937 if(pmap.getUnits() == PixelMapUnits::count_per_sec){
938 Q = 1.0/e_per_s_to_ergs_s_cm2 ;
939 }
else if(pmap.getUnits() == PixelMapUnits::ADU){
940 Q = 1.0/gain/e_per_s_to_ergs_s_cm2;
942 std::cerr <<
"Map needs to be in photon flux units." << std::endl;
943 throw std::runtime_error(
"wrong units");
947 pmap.ChangeUnits(PixelMapUnits::surfb);
958 pmap.ChangeUnits(PixelMapUnits::ADU);
PosType getLowestRes()
resolution of coarsest grid from which interpolation is done
Definition image_processing.h:385
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:273
PosType getHighestRes()
resolution of finest grid from which interpolation is done
Definition image_processing.h:383
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:291
MultiGridSmoother(double center[], std::size_t Nx, std::size_t Ny, double resolution)
Definition pixelize.cpp:223
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:419
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:401
float getBackgroundNoise() const
returns std of pixels in e-
Definition image_processing.h:253
float getRon() const
read-out noise in electrons/pixel
Definition image_processing.h:292
float getBackgroundNoise(float resolution, UnitType unit=UnitType::counts_x_sec) const
pixel size in radians
Definition observation.cpp:730
void setExpTime(float time)
returns factor by which code image units need to be multiplied by to get flux units
Definition image_processing.h:318
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:810
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:856
float getSeeing() const
seeing in arcsecs
Definition image_processing.h:294
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:419
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:509
void smooth(double sigma)
Smoothes a map with a Gaussian kernel of width sigma (in arcseconds)
Definition pixelmap.cpp:1465
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:181
The box representing a branch of a binary tree structure. Used specifically in TreeStruct for organiz...
Definition point.h:638
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