def patchPrior(im, beta, patchPriorFile='naturalPrior.mat', patchSize=8): # load data ldata = scipy.io.loadmat(patchPriorFile) # reassign and reshape data nmodels = ldata['nmodels'].ravel() nmodels = nmodels[0] mixweights = ldata['mixweights'].ravel() covs = np.array(ldata['covs']) means = np.array(ldata['means']) # reshape image img = np.reshape(im.imvec, (im.ydim, im.xdim)) I1, counts = cleanImage(img, beta, nmodels, covs, mixweights, means, patchSize) if not all(counts[0][0] == item for item in np.reshape(counts, (-1))): raise TypeError( "The counts are not the same for every pixel in the image") I1 = I1 / counts[0][0] out = image.Image(I1, im.psize, im.ra, im.dec, rf=im.rf, source=im.source, mjd=im.mjd, pulse=im.pulse) return (out, counts[0][0])
def Ensemble_Average_Blur(self, im, wavelength_cm = None, ker = None): """Blurs an input Image with the ensemble-average scattering kernel. Args: im (Image): The unscattered image. wavelength_cm (float): The observing wavelength for the scattering kernel in cm. If unspecified, this will default to the wavelength of the input image. ker (2D ndarray): The user can optionally pass a pre-computed ensemble-average blurring kernel. Returns: out (Image): The ensemble-average scattered image. """ # Inputs an unscattered image and an ensemble-average blurring kernel (2D array); returns the ensemble-average image # The pre-computed kernel can optionally be specified (ker) if wavelength_cm == None: wavelength_cm = C/im.rf*100.0 #Observing wavelength [cm] if ker is None: ker = self.Ensemble_Average_Kernel(im, wavelength_cm) Iim = Wrapped_Convolve((im.imvec).reshape(im.ydim, im.xdim), ker) out = image.Image(Iim, im.psize, im.ra, im.dec, rf=C/(wavelength_cm/100.0), source=im.source, mjd=im.mjd, pulse=im.pulse) if len(im.qvec): Qim = Wrapped_Convolve((im.qvec).reshape(im.ydim, im.xdim), ker) Uim = Wrapped_Convolve((im.uvec).reshape(im.ydim, im.xdim), ker) out.add_qu(Qim, Uim) if len(im.vvec): Vim = Wrapped_Convolve((im.vvec).reshape(im.ydim, im.xdim), ker) out.add_v(Vim) return out
def linearizedSol_bs(Obsdata, currImage, Prior, alpha=100, beta=100, reg="patch"): # note what beta is # normalize the prior # TODO: SHOULD THIS BE DONE?? zbl = np.max(np.abs(Obsdata.unpack(['vis'])['vis'])) nprior = zbl * Prior.imvec / np.sum(Prior.imvec) if reg == "patch": linRegTerm, constRegTerm = spatchlingrad(currImage.imvec, nprior) # Get bispectra data biarr = Obsdata.bispectra(mode="all", count="max") uv1 = np.hstack((biarr['u1'].reshape(-1, 1), biarr['v1'].reshape(-1, 1))) uv2 = np.hstack((biarr['u2'].reshape(-1, 1), biarr['v2'].reshape(-1, 1))) uv3 = np.hstack((biarr['u3'].reshape(-1, 1), biarr['v3'].reshape(-1, 1))) bispec = biarr['bispec'] sigs = biarr['sigmab'] # Compute the fourier matrices A3 = (ftmatrix(currImage.psize, currImage.xdim, currImage.ydim, uv1, pulse=currImage.pulse), ftmatrix(currImage.psize, currImage.xdim, currImage.ydim, uv2, pulse=currImage.pulse), ftmatrix(currImage.psize, currImage.xdim, currImage.ydim, uv3, pulse=currImage.pulse)) Alin, blin = computeLinTerms_bi(currImage.imvec, A3, bispec, sigs, currImage.xdim * currImage.ydim, alpha=alpha, reg=reg) out = np.linalg.solve(Alin + beta * linRegTerm, blin + beta * constRegTerm) return image.Image(out.reshape((currImage.ydim, currImage.xdim)), currImage.psize, currImage.ra, currImage.dec, rf=currImage.rf, source=currImage.source, mjd=currImage.mjd, pulse=currImage.pulse)
def MakePhaseScreen(self, EpsilonScreen, Reference_Image, obs_frequency_Hz=0.0, Vx_km_per_s=50.0, Vy_km_per_s=0.0, t_hr=0.0, sqrtQ_init=None): """Create a refractive phase screen from standardized Fourier components (the EpsilonScreen). All lengths should be specified in centimeters If the observing frequency (obs_frequency_Hz) is not specified, then it will be taken to be equal to the frequency of the Reference_Image Note: an odd image dimension is required! Args: EpsilonScreen (2D ndarray): Optionally, the scattering screen can be specified. If none is given, a random one will be generated. Reference_Image (Image): The reference image. obs_frequency_Hz (float): The observing frequency, in Hz. By default, it will be taken to be equal to the frequency of the Unscattered_Image. Vx_km_per_s (float): Velocity of the scattering screen in the x direction (toward East) in km/s. Vy_km_per_s (float): Velocity of the scattering screen in the y direction (toward North) in km/s. t_hr (float): The current time of the scattering in hours. ea_ker (2D ndarray): The used can optionally pass a precomputed array of the ensemble-average blurring kernel. sqrtQ_init (2D ndarray): The used can optionally pass a precomputed array of the square root of the power spectrum. Returns: phi_Image (Image): The phase screen. """ #Observing wavelength if obs_frequency_Hz == 0.0: obs_frequency_Hz = Reference_Image.rf wavelength = C/obs_frequency_Hz*100.0 #Observing wavelength [cm] wavelengthbar = wavelength/(2.0*np.pi) #lambda/(2pi) [cm] #Derived parameters FOV = Reference_Image.psize * Reference_Image.xdim * self.observer_screen_distance #Field of view, in cm, at the scattering screen rF = self.rF(wavelength) Nx = EpsilonScreen.shape[1] Ny = EpsilonScreen.shape[0] if Nx%2 == 0: print("The image dimension should really be odd...") #Now we'll calculate the power spectrum for each pixel in Fourier space screen_x_offset_pixels = (Vx_km_per_s*1.e5) * (t_hr*3600.0) / (FOV/float(Nx)) screen_y_offset_pixels = (Vy_km_per_s*1.e5) * (t_hr*3600.0) / (FOV/float(Nx)) if sqrtQ_init is None: sqrtQ = self.sqrtQ_Matrix(Reference_Image, Vx_km_per_s=Vx_km_per_s, Vy_km_per_s=Vy_km_per_s, t_hr=t_hr) else: #If a matrix for sqrtQ_init is passed, we still need to potentially rotate it if screen_x_offset_pixels != 0.0 or screen_y_offset_pixels != 0.0: s, t = np.meshgrid(np.fft.fftfreq(Nx, d=1.0/Nx), np.fft.fftfreq(Ny, d=1.0/Ny)) sqrtQ = sqrtQ_init * np.exp(2.0*np.pi*1j*(s*screen_x_offset_pixels + t*screen_y_offset_pixels)/float(Nx)) else: sqrtQ = sqrtQ_init #Now calculate the phase screen phi = np.real(wavelengthbar/FOV*EpsilonScreen.shape[0]*EpsilonScreen.shape[1]*np.fft.ifft2(sqrtQ*EpsilonScreen)) phi_Image = image.Image(phi, Reference_Image.psize, Reference_Image.ra, Reference_Image.dec, rf=Reference_Image.rf, source=Reference_Image.source, mjd=Reference_Image.mjd) return phi_Image
def get_frame(j): if type(Unscattered_Movie) == movie.Movie: im = image.Image(Unscattered_Movie.frames[j].reshape((N,N)), psize, ra, dec, rf, pulse, source, mjd) if len(Unscattered_Movie.qframes) > 0: im.add_qu(Unscattered_Movie.qframes[j].reshape((N,N)), Unscattered_Movie.uframes[j].reshape((N,N))) return im elif type(Unscattered_Movie) == list: return Unscattered_Movie[j] else: return Unscattered_Movie
def get_frame(self, frame_num): """Return one movie frame as an image object""" import ehtim.image as image if frame_num < 0 or frame_num > len(self.frames): raise Exception("Invalid frame number!") im = image.Image( self.frames[frame_num].reshape((self.ydim, self.xdim)), self.psize, self.ra, self.dec, self.rf, self.pulse, self.source, self.mjd) if len(self.qframes) > 0: im.add_qu(self.qframes[frame_num].reshape((self.ydim, self.xdim)), self.uframes[frame_num].reshape((self.ydim, self.xdim))) if len(self.vframes) > 0: im.add_v(self.vframes[frame_num].reshape((self.ydim, self.xdim))) return im
def Scatter(self, Unscattered_Image, Epsilon_Screen=np.array([]), obs_frequency_Hz=0.0, Vx_km_per_s=50.0, Vy_km_per_s=0.0, t_hr=0.0, ea_ker=None, sqrtQ=None, Linearized_Approximation=False, DisplayImage=False, Force_Positivity=False, use_approximate_form=True): """Scatter an image using the specified epsilon screen. All lengths should be specified in centimeters If the observing frequency (obs_frequency_Hz) is not specified, then it will be taken to be equal to the frequency of the Unscattered_Image Note: an odd image dimension is required! Args: Unscattered_Image (Image): The unscattered image. Epsilon_Screen (2D ndarray): Optionally, the scattering screen can be specified. If none is given, a random one will be generated. obs_frequency_Hz (float): The observing frequency, in Hz. By default, it will be taken to be equal to the frequency of the Unscattered_Image. Vx_km_per_s (float): Velocity of the scattering screen in the x direction (toward East) in km/s. Vy_km_per_s (float): Velocity of the scattering screen in the y direction (toward North) in km/s. t_hr (float): The current time of the scattering in hours. ea_ker (2D ndarray): The used can optionally pass a precomputed array of the ensemble-average blurring kernel. sqrtQ (2D ndarray): The used can optionally pass a precomputed array of the square root of the power spectrum. Linearized_Approximation (bool): If True, uses a linearized approximation for the scattering (Eq. 10 of Johnson & Narayan 2016). If False, uses Eq. 9 of that paper. DisplayImage (bool): If True, show a plot of the unscattered, ensemble-average, and scattered images as well as the phase screen. Force_Positivity (bool): If True, eliminates negative flux from the scattered image from the linearized approximation. Return_Image_List (bool): If True, returns a list of the scattered frames. If False, returns a movie object. Returns: AI_Image (Image): The scattered image. """ # Observing wavelength if obs_frequency_Hz == 0.0: obs_frequency_Hz = Unscattered_Image.rf wavelength = C / obs_frequency_Hz * 100.0 # Observing wavelength [cm] wavelengthbar = wavelength / (2.0 * np.pi) # lambda/(2pi) [cm] # Derived parameters FOV = Unscattered_Image.psize * Unscattered_Image.xdim * \ self.observer_screen_distance # Field of view, in cm, at the scattering screen rF = self.rF(wavelength) Nx = Unscattered_Image.xdim Ny = Unscattered_Image.ydim # First we need to calculate the ensemble-average image by blurring the # unscattered image with the correct kernel EA_Image = self.Ensemble_Average_Blur( Unscattered_Image, wavelength, ker=ea_ker, use_approximate_form=use_approximate_form) # If no epsilon screen is specified, then generate a random realization if Epsilon_Screen.shape[0] == 0: Epsilon_Screen = MakeEpsilonScreen(Nx, Ny) # We'll now calculate the phase screen. phi_Image = self.MakePhaseScreen(Epsilon_Screen, Unscattered_Image, obs_frequency_Hz, Vx_km_per_s=Vx_km_per_s, Vy_km_per_s=Vy_km_per_s, t_hr=t_hr, sqrtQ_init=sqrtQ) phi = phi_Image.imvec.reshape(Ny, Nx) # Next, we need the gradient of the ensemble-average image phi_Gradient = Wrapped_Gradient(phi / (FOV / Nx)) # The gradient signs don't actually matter, but let's make them match # intuition (i.e., right to left, bottom to top) phi_Gradient_x = -phi_Gradient[1] phi_Gradient_y = -phi_Gradient[0] # Use Equation 10 of Johnson & Narayan (2016) if Linearized_Approximation: # Calculate the gradient of the ensemble-average image EA_Gradient = Wrapped_Gradient( (EA_Image.imvec / (FOV / Nx)).reshape(EA_Image.ydim, EA_Image.xdim)) # The gradient signs don't actually matter, but let's make them # match intuition (i.e., right to left, bottom to top) EA_Gradient_x = -EA_Gradient[1] EA_Gradient_y = -EA_Gradient[0] # Now we can patch together the average image AI = (EA_Image.imvec).reshape(Ny, Nx) + rF**2.0 * \ (EA_Gradient_x * phi_Gradient_x + EA_Gradient_y * phi_Gradient_y) if len(Unscattered_Image.qvec): # Scatter the Q image EA_Gradient = Wrapped_Gradient( (EA_Image.qvec / (FOV / Nx)).reshape( EA_Image.ydim, EA_Image.xdim)) EA_Gradient_x = -EA_Gradient[1] EA_Gradient_y = -EA_Gradient[0] AI_Q = (EA_Image.qvec).reshape(Ny, Nx) + rF**2.0 * \ (EA_Gradient_x * phi_Gradient_x + EA_Gradient_y * phi_Gradient_y) # Scatter the U image EA_Gradient = Wrapped_Gradient( (EA_Image.uvec / (FOV / Nx)).reshape( EA_Image.ydim, EA_Image.xdim)) EA_Gradient_x = -EA_Gradient[1] EA_Gradient_y = -EA_Gradient[0] AI_U = (EA_Image.uvec).reshape(Ny, Nx) + rF**2.0 * \ (EA_Gradient_x * phi_Gradient_x + EA_Gradient_y * phi_Gradient_y) if len(Unscattered_Image.vvec): # Scatter the V image EA_Gradient = Wrapped_Gradient( (EA_Image.vvec / (FOV / Nx)).reshape( EA_Image.ydim, EA_Image.xdim)) EA_Gradient_x = -EA_Gradient[1] EA_Gradient_y = -EA_Gradient[0] AI_V = (EA_Image.vvec).reshape(Ny, Nx) + rF**2.0 * \ (EA_Gradient_x * phi_Gradient_x + EA_Gradient_y * phi_Gradient_y) else: # Use Equation 9 of Johnson & Narayan (2016) EA_im = (EA_Image.imvec).reshape(Ny, Nx) AI = np.copy((EA_Image.imvec).reshape(Ny, Nx)) if len(Unscattered_Image.qvec): AI_Q = np.copy((EA_Image.imvec).reshape(Ny, Nx)) AI_U = np.copy((EA_Image.imvec).reshape(Ny, Nx)) EA_im_Q = (EA_Image.qvec).reshape(Ny, Nx) EA_im_U = (EA_Image.uvec).reshape(Ny, Nx) if len(Unscattered_Image.vvec): AI_V = np.copy((EA_Image.imvec).reshape(Ny, Nx)) EA_im_V = (EA_Image.vvec).reshape(Ny, Nx) for rx in range(Nx): for ry in range(Ny): # Annoyingly, the signs here must be negative to match the # other approximation. I'm not sure which is correct, but # it really shouldn't matter anyway because -phi has the # same power spectrum as phi. However, getting the # *relative* sign for the x- and y-directions correct is # important. rxp = int( np.round(rx - rF**2.0 * phi_Gradient_x[ry, rx] / self.observer_screen_distance / Unscattered_Image.psize)) % Nx ryp = int( np.round(ry - rF**2.0 * phi_Gradient_y[ry, rx] / self.observer_screen_distance / Unscattered_Image.psize)) % Ny AI[ry, rx] = EA_im[ryp, rxp] if len(Unscattered_Image.qvec): AI_Q[ry, rx] = EA_im_Q[ryp, rxp] AI_U[ry, rx] = EA_im_U[ryp, rxp] if len(Unscattered_Image.vvec): AI_V[ry, rx] = EA_im_V[ryp, rxp] # Optional: eliminate negative flux if Force_Positivity: AI = abs(AI) # Make it into a proper image format AI_Image = image.Image(AI, EA_Image.psize, EA_Image.ra, EA_Image.dec, rf=EA_Image.rf, source=EA_Image.source, mjd=EA_Image.mjd) if len(Unscattered_Image.qvec): AI_Image.add_qu(AI_Q, AI_U) if len(Unscattered_Image.vvec): AI_Image.add_v(AI_V) if DisplayImage: plot_scatt(Unscattered_Image.imvec, EA_Image.imvec, AI_Image.imvec, phi_Image.imvec, Unscattered_Image, 0, 0, ipynb=False) return AI_Image