def align_image(image_in: np.ndarray, im_index: int, interpolation: str, accuracy: float, resize: Optional[float], num_references: int, subframe: Optional[float], ref_images_reshape: np.ndarray, ref_images_shape: Tuple[int, int, int]) -> np.ndarray: offset = np.array([0., 0.]) # Reshape the reference images back to their original 3D shape # The original shape can not be used directly because of util.module.update_arguments ref_images = ref_images_reshape.reshape(ref_images_shape) for i in range(num_references): if subframe is None: tmp_offset, _, _ = phase_cross_correlation(ref_images[i, :, :], image_in, upsample_factor=accuracy) else: sub_in = crop_image(image_in, None, subframe) sub_ref = crop_image(ref_images[i, :, :], None, subframe) tmp_offset, _, _ = phase_cross_correlation(sub_ref, sub_in, upsample_factor=accuracy) offset += tmp_offset offset /= float(num_references) if resize is not None: offset *= resize sum_before = np.sum(image_in) tmp_image = rescale(image_in, (resize, resize), order=5, mode='reflect', multichannel=False, anti_aliasing=True) sum_after = np.sum(tmp_image) # Conserve flux because the rescale function normalizes all values to [0:1]. tmp_image = tmp_image*(sum_before/sum_after) else: tmp_image = image_in return shift_image(tmp_image, offset, interpolation)
def locate_star(image, center, width, fwhm): """ Function to locate the star by finding the brightest pixel. :param image: Input image. :type image: ndarray :param center: Pixel center (y, x) of the subframe. The full image is used if set to None. :type center: (int, int) :param width: The width (pixel) of the subframe. The full image is used if set to None. :type width: int :param fwhm: Full width at half maximum of the Gaussian kernel. :type fwhm: int :return: Position (y, x) of the brightest pixel. :rtype: (int, int) """ if width is not None: if center is None: center = image_center_pixel(image) image = crop_image(image, center, width) sigma = fwhm / math.sqrt(8. * math.log(2.)) kernel = (fwhm * 2 + 1, fwhm * 2 + 1) smooth = cv2.GaussianBlur(image, kernel, sigma) # argmax[0] is the y position and argmax[1] is the y position argmax = np.asarray(np.unravel_index(smooth.argmax(), smooth.shape)) if center is not None and width is not None: argmax[0] += center[0] - (image.shape[0] - 1) // 2 # y argmax[1] += center[1] - (image.shape[1] - 1) // 2 # x return argmax
def _photometry(images, starpos, aperture): check_pos_in = any(np.floor(starpos[:]-aperture[1]) < 0.) check_pos_out = any(np.ceil(starpos[:]+aperture[1]) > images.shape[0]) if check_pos_in or check_pos_out: phot = np.nan else: im_crop = crop_image(images, tuple(starpos), 2*int(math.ceil(aperture[1]))) npix = im_crop.shape[0] x_grid = y_grid = np.linspace(-(npix-1)/2, (npix-1)/2, npix) xx_grid, yy_grid = np.meshgrid(x_grid, y_grid) rr_grid = np.sqrt(xx_grid*xx_grid+yy_grid*yy_grid) if self.m_aperture[0] == 'circular': phot = np.sum(im_crop[rr_grid < aperture[1]]) elif self.m_aperture[0] == 'annulus': phot = np.sum(im_crop[(rr_grid > aperture[0]) & (rr_grid < aperture[1])]) elif self.m_aperture[0] == 'ratio': phot = np.sum(im_crop[rr_grid < aperture[0]]) / \ np.sum(im_crop[(rr_grid > aperture[0]) & (rr_grid < aperture[1])]) return phot
def _crop_rotating_star(image, position, im_size, filter_size): starpos = locate_star(image=image, center=position, width=self.m_search_size, fwhm=filter_size) return crop_image(image=image, center=starpos, size=im_size)
def _crop_around_star(image, position, im_size, fwhm): if position is None: center = None width = None else: if position.ndim == 1: if position[0] is None and position[1] is None: center = None else: center = (int(position[1]), int(position[0])) width = int(math.ceil(position[2] / pixscale)) elif position.ndim == 2: center = (int(position[self.m_count, 1]), int(position[self.m_count, 0])) width = int(math.ceil(position[self.m_count, 2] / pixscale)) starpos = locate_star(image, center, width, fwhm) try: im_crop = crop_image(image, starpos, im_size) except ValueError: warnings.warn( "PSF size is too large to crop the image around the brightest " "pixel (image index = " + str(self.m_count) + ", pixel [x, y] = " + str([starpos[0]] + [starpos[1]]) + "). Using the center of the " "image instead.") index.append(self.m_count) starpos = image_center_pixel(image) im_crop = crop_image(image, starpos, im_size) star.append((starpos[1], starpos[0])) self.m_count += 1 return im_crop
def _crop_around_star(image: np.ndarray, position: Optional[Union[Tuple[int, int, float], Tuple[None, None, float]]], im_size: int, fwhm: int) -> np.ndarray: if position is None: center = None width = None else: if position[0] is None and position[1] is None: center = None else: center = (position[1], position[0]) # (y, x) width = int(math.ceil(position[2]/pixscale)) starpos = locate_star(image, center, width, fwhm) try: im_crop = crop_image(image, tuple(starpos), im_size) except ValueError: if cpu == 1: warnings.warn(f'Chosen image size is too large to crop the image around the ' f'brightest pixel (image index = {self.m_count}, pixel [x, y] ' f'= [{starpos[0]}, {starpos[1]}]). Using the center of the ' f'image instead.') index.append(self.m_count) else: warnings.warn('Chosen image size is too large to crop the image around the ' 'brightest pixel. Using the center of the image instead.') starpos = center_pixel(image) im_crop = crop_image(image, tuple(starpos), im_size) if cpu == 1: star.append((starpos[1], starpos[0])) self.m_count += 1 return im_crop
def aperture_phot( image: np.ndarray, position: np.ndarray, aperture: Union[Tuple[str, float, float], Tuple[str, None, float]] ) -> np.float64: """ Parameters ---------- image : np.ndarray Input image (2D). position : np.ndarray Center position (y, x) of the aperture. aperture : tuple(str, float, float) Tuple with the aperture properties for measuring the photometry around the location of the brightest pixel. The first element contains the aperture type ('circular', 'annulus', or 'ratio'). For a circular aperture, the second element is empty and the third element contains the aperture radius (pix). For the other two types, the second and third element are the inner and outer radii (pix) of the aperture. Returns ------- np.float64 Photometry value. """ check_pos_in = any(np.floor(position[:] - aperture[2]) < 0.) check_pos_out = any( np.ceil(position[:] + aperture[2]) > image.shape[0]) if check_pos_in or check_pos_out: phot = np.nan else: im_crop = crop_image(image, tuple(position), 2 * int(math.ceil(aperture[2]))) npix = im_crop.shape[0] x_grid = y_grid = np.linspace(-(npix - 1) / 2, (npix - 1) / 2, npix) xx_grid, yy_grid = np.meshgrid(x_grid, y_grid) rr_grid = np.sqrt(xx_grid**2 + yy_grid**2) if aperture[0] == 'circular': phot = np.sum(im_crop[rr_grid < aperture[2]]) elif aperture[0] == 'annulus': phot = np.sum(im_crop[(rr_grid > aperture[1]) & (rr_grid < aperture[2])]) elif aperture[0] == 'ratio': phot = np.sum(im_crop[rr_grid < aperture[1]]) / \ np.sum(im_crop[(rr_grid > aperture[1]) & (rr_grid < aperture[2])]) return phot
def _align_image(image_in): offset = np.array([0., 0.]) for i in range(self.m_num_references): if self.m_subframe is None: tmp_offset, _, _ = register_translation( ref_images[i, :, :], image_in, upsample_factor=self.m_accuracy) else: sub_in = crop_image(image_in, None, self.m_subframe) sub_ref = crop_image(ref_images[i, :, :], None, self.m_subframe) tmp_offset, _, _ = register_translation( sub_ref, sub_in, upsample_factor=self.m_accuracy) offset += tmp_offset offset /= float(self.m_num_references) if self.m_resize is not None: offset *= self.m_resize sum_before = np.sum(image_in) tmp_image = rescale(image=np.asarray(image_in, dtype=np.float64), scale=(self.m_resize, self.m_resize), order=5, mode='reflect', anti_aliasing=True, multichannel=False) sum_after = np.sum(tmp_image) # Conserve flux because the rescale function normalizes all values to [0:1]. tmp_image = tmp_image * (sum_before / sum_after) else: tmp_image = image_in return shift_image(tmp_image, offset, self.m_interpolation)
def crop_around_star(image: np.ndarray, im_index: int, position: Optional[Union[Tuple[int, int, float], Tuple[None, None, float]]], im_size: int, fwhm: int, pixscale: float, index_out_port: Optional[OutputPort], image_out_port: OutputPort) -> np.ndarray: if position is None: center = None width = None else: if position[0] is None and position[1] is None: center = None else: center = (position[1], position[0]) # (y, x) width = int(math.ceil(position[2]/pixscale)) starpos = locate_star(image, center, width, fwhm) try: im_crop = crop_image(image, tuple(starpos), im_size) except ValueError: warnings.warn(f'Chosen image size is too large to crop the image around the ' f'brightest pixel (image index = {im_index}, pixel [x, y] ' f'= [{starpos[0]}, {starpos[1]}]). Using the center of the ' f'image instead.') if index_out_port is not None: index_out_port.append([im_index], data_dim=1) starpos = center_pixel(image) im_crop = crop_image(image, tuple(starpos), im_size) return im_crop
def _crop_rotating_star(image: np.ndarray, position: Union[Tuple[float, float], np.ndarray], im_size: int, filter_size: Optional[int]) -> np.ndarray: starpos = locate_star(image=image, center=tuple(position), width=self.m_search_size, fwhm=filter_size) return crop_image(image=image, center=tuple(starpos), size=im_size)
def run(self) -> None: """ Run method of the module. Decreases the image size by cropping around an given position. The module always returns odd-sized images. Returns ------- NoneType None """ self.m_image_out_port.del_all_attributes() self.m_image_out_port.del_all_data() # Get memory and number of images to split the frames into chunks memory = self._m_config_port.get_attribute('MEMORY') nimages = self.m_image_in_port.get_shape()[0] frames = memory_frames(memory, nimages) # Convert size parameter from arcseconds to pixels pixscale = self.m_image_in_port.get_attribute('PIXSCALE') self.m_size = int(math.ceil(self.m_size / pixscale)) # Crop images chunk by chunk start_time = time.time() for i in range(len(frames[:-1])): # Update progress bar progress(i, len(frames[:-1]), 'Running CropImagesModule...', start_time) # Select and crop images in the current chunk images = self.m_image_in_port[frames[i]:frames[i + 1], ] images = crop_image(images, self.m_center, self.m_size, copy=False) # Write cropped images to output port self.m_image_out_port.append(images, data_dim=3) # Update progress bar (cropping of images is finished) sys.stdout.write('Running CropImagesModule... [DONE]\n') sys.stdout.flush() # Save history and copy attributes history = f'image size [pix] = {self.m_size}' self.m_image_out_port.add_history('CropImagesModule', history) self.m_image_out_port.copy_attributes(self.m_image_in_port) self.m_image_out_port.close_port()
def locate_star(image: np.ndarray, center: Union[tuple, None], width: Union[int, None], fwhm: Union[int, None]) -> np.ndarray: """ Function to locate the star by finding the brightest pixel. Parameters ---------- image : numpy.ndarray Input image (2D). center : tuple(int, int), None Pixel center (y, x) of the subframe. The full image is used if set to None. width : int, None The width (pix) of the subframe. The full image is used if set to None. fwhm : int, None Full width at half maximum (pix) of the Gaussian kernel. Not used if set to None. Returns ------- numpy.ndarray Position (y, x) of the brightest pixel. """ if width is not None: if center is None: center = center_pixel(image) image = crop_image(image, center, width) if fwhm is None: smooth = np.copy(image) else: sigma = fwhm / math.sqrt(8. * math.log(2.)) kernel = (fwhm * 2 + 1, fwhm * 2 + 1) smooth = cv2.GaussianBlur(image, kernel, sigma) # argmax[0] is the y position and argmax[1] is the y position argmax = np.asarray(np.unravel_index(smooth.argmax(), smooth.shape)) if center is not None and width is not None: argmax[0] += center[0] - (image.shape[0] - 1) // 2 # y argmax[1] += center[1] - (image.shape[1] - 1) // 2 # x return argmax
def run(self) -> None: """ Run the module. The FITS files are collected from the input directory and uncompressed if needed. The images are then sorted by the two chop positions (chop A and chop B). The required FITS header keywords (which should be set in the configuration file) are also imported and stored as attributes to the two output datasets in the HDF5 database. Returns ------- NoneType None """ # clear the output ports self.m_chopa_out_port.del_all_data() self.m_chopa_out_port.del_all_attributes() self.m_chopb_out_port.del_all_data() self.m_chopb_out_port.del_all_attributes() # uncompress the FITS files if needed self.uncompress() # find and sort the FITS files files = [] for filename in os.listdir(self.m_input_location): if filename.endswith('.fits'): files.append(os.path.join(self.m_input_location, filename)) files.sort() # check if there are FITS files present in the input location assert (files), f'No FITS files found in {self.m_input_location}.' # if cropping chop A, get pixscale and convert crop_size to pixels and swap x/y if self.m_crop is not None: pixscale = self._m_config_port.get_attribute('PIXSCALE') self.m_crop = (self.m_crop[1], self.m_crop[0], int(math.ceil(self.m_crop[2] / pixscale))) start_time = time.time() for i, filename in enumerate(files): progress(i, len(files), 'Preprocessing NEAR data...', start_time) # get the primary header data and the image shape header, im_shape = self.read_header(filename) # get the images of chop A and chop B chopa, chopb = self.read_images(filename, im_shape) if self.m_subtract: chopa = chopa - chopb chopb = -1. * np.copy(chopa) if self.m_crop is not None: chopa = crop_image(chopa, center=self.m_crop[0:2], size=self.m_crop[2], copy=False) chopb = crop_image(chopb, center=self.m_crop[0:2], size=self.m_crop[2], copy=False) if self.m_combine is not None: if self.m_combine == 'mean': chopa = np.mean(chopa, axis=0) chopb = np.mean(chopb, axis=0) elif self.m_combine == 'median': chopa = np.median(chopa, axis=0) chopb = np.median(chopb, axis=0) header[self._m_config_port.get_attribute('NFRAMES')] = 1 # append the images of chop A and B self.m_chopa_out_port.append(chopa, data_dim=3) self.m_chopb_out_port.append(chopb, data_dim=3) # starting value for the INDEX attribute first_index = 0 for port in (self.m_chopa_out_port, self.m_chopb_out_port): # set the static attributes set_static_attr(fits_file=filename, header=header, config_port=self._m_config_port, image_out_port=port, check=True) # set the non-static attributes set_nonstatic_attr(header=header, config_port=self._m_config_port, image_out_port=port, check=True) # set the remaining attributes set_extra_attr(fits_file=filename, nimages=im_shape[0] // 2, config_port=self._m_config_port, image_out_port=port, first_index=first_index) # increase the first value of the INDEX attribute first_index += im_shape[0] // 2 # flush the output port port.flush() # add history information self.m_chopa_out_port.add_history('NearReadingModule', 'Chop A') self.m_chopb_out_port.add_history('NearReadingModule', 'Chop B') # close all connections to the database self.m_chopa_out_port.close_port()
def run(self) -> None: """ Run method of the module. Computes the similarity between frames based on the Mean Squared Error (MSE), the Pearson Correlation Coefficient (PCC), or the Structural Similarity (SSIM). The correlation values are stored as non-static attribute (``MSE``, ``PCC``, or ``SSIM``) to the input data. After running this module, the :class:`~pynpoint.processing.frameselection.SelectByAttributeModule` can be used to select the images with the highest correlation. Returns ------- NoneType None """ cpu = self._m_config_port.get_attribute('CPU') pixscale = self.m_image_in_port.get_attribute('PIXSCALE') # get number of images nimages = self.m_image_in_port.get_shape()[0] # convert arcsecs to pixels self.m_mask_radii = (math.floor(self.m_mask_radii[0] / pixscale), math.floor(self.m_mask_radii[1] / pixscale)) self.m_window_size = int(self.m_window_size / pixscale) # overlay the same mask over all images images = self.m_image_in_port.get_all() # close the port during the calculations self.m_image_out_port.close_port() images = crop_image(images, None, int(self.m_mask_radii[1])) if self.m_temporal_median == 'constant': temporal_median = np.median(images, axis=0) else: temporal_median = False # compare images and store similarity similarities = np.zeros(nimages) pool = mp.Pool(cpu) async_results = [] for i in range(nimages): async_results.append( pool.apply_async(FrameSimilarityModule._similarity, args=(images, i, self.m_method, self.m_window_size, temporal_median))) pool.close() start_time = time.time() # wait for all processes to finish while mp.active_children(): # number of finished processes nfinished = sum([i.ready() for i in async_results]) progress(nfinished, nimages, 'Calculating image similarity...', start_time) # check if new processes have finished every 5 seconds time.sleep(5) if nfinished != nimages: print('\r ') print('\rCalculating image similarity... [DONE]') # get the results for every async_result object for async_result in async_results: reference, similarity = async_result.get() similarities[reference] = similarity pool.terminate() # reopen the port after the calculation self.m_image_out_port.open_port() self.m_image_out_port.add_attribute(f'{self.m_method}', similarities, static=False) self.m_image_out_port.close_port()
def run(self) -> None: """ Run method of the module. Locates the position of the calibration spots in the center frame. From the four spots, the position of the star behind the coronagraph is fitted, and the images are shifted and cropped. Returns ------- NoneType None """ @typechecked def _get_center( image_number: int, center: Optional[Tuple[int, int]] ) -> Tuple[np.ndarray, Tuple[int, int]]: if center_shape[-3] > 1: warnings.warn( 'Multiple center images found. Using the first image of the stack.' ) if ndim == 3: center_frame = self.m_center_in_port[0, ] elif ndim == 4: center_frame = self.m_center_in_port[image_number, 0, ] if center is None: center = center_pixel(center_frame) else: center = (int(np.floor(center[0])), int(np.floor(center[1]))) return center_frame, center center_shape = self.m_center_in_port.get_shape() im_shape = self.m_image_in_port.get_shape() ndim = self.m_image_in_port.get_ndim() center_frame, self.m_center = _get_center(0, self.m_center) # Read in wavelength information or set it to default values if ndim == 4: wavelength = self.m_image_in_port.get_attribute('WAVELENGTH') if wavelength is None: raise ValueError( 'The wavelength information is required to centre IFS data. ' 'Please add it via the WavelengthReadingModule before using ' 'the WaffleCenteringModule.') if im_shape[0] != center_shape[0]: raise ValueError( f'Number of science wavelength channels: {im_shape[0]}. ' f'Number of center wavelength channels: {center_shape[0]}. ' 'Exactly one center image per wavelength is required.') wavelength_min = np.min(wavelength) elif ndim == 3: # for none ifs data, use default value wavelength = [1.] wavelength_min = 1. # check if science and center images have the same shape if im_shape[-2:] != center_shape[-2:]: raise ValueError( 'Science and center images should have the same shape.') # Setting angle via pattern (used for backwards compability) if self.m_pattern is not None: if self.m_pattern == 'x': self.m_angle = 45. elif self.m_pattern == '+': self.m_angle = 0. else: raise ValueError( f'The pattern {self.m_pattern} is not valid. Please select ' f'either \'x\' or \'+\'.') warnings.warn( f'The \'pattern\' parameter will be deprecated in a future release. ' f'Please Use the \'angle\' parameter instead and set it to ' f'{self.m_angle} degrees.', DeprecationWarning) pixscale = self.m_image_in_port.get_attribute('PIXSCALE') self.m_sigma /= pixscale if self.m_size is not None: self.m_size = int(math.ceil(self.m_size / pixscale)) if self.m_dither: dither_x = self.m_image_in_port.get_attribute('DITHER_X') dither_y = self.m_image_in_port.get_attribute('DITHER_Y') nframes = self.m_image_in_port.get_attribute('NFRAMES') nframes = np.cumsum(nframes) nframes = np.insert(nframes, 0, 0) # size of center image, only works with odd value ref_image_size = 21 # Arrays for the positions x_pos = np.zeros(4) y_pos = np.zeros(4) # Arrays for the center position for each wavelength x_center = np.zeros((len(wavelength))) y_center = np.zeros((len(wavelength))) # Loop for 4 waffle spots for w, wave_nr in enumerate(wavelength): # Prapre centering frame center_frame, _ = _get_center(w, self.m_center) center_frame_unsharp = center_frame - gaussian_filter( input=center_frame, sigma=self.m_sigma) for i in range(4): # Approximate positions of waffle spots radius = self.m_radius * wave_nr / wavelength_min x_0 = np.floor(self.m_center[0] + radius * np.cos(self.m_angle * np.pi / 180 + np.pi / 4. * (2 * i))) y_0 = np.floor(self.m_center[1] + radius * np.sin(self.m_angle * np.pi / 180 + np.pi / 4. * (2 * i))) tmp_center_frame = crop_image(image=center_frame_unsharp, center=(int(y_0), int(x_0)), size=ref_image_size) # find maximum in tmp image coords = np.unravel_index(indices=np.argmax(tmp_center_frame), shape=tmp_center_frame.shape) y_max, x_max = coords[0], coords[1] pixmax = tmp_center_frame[y_max, x_max] max_pos = np.array([x_max, y_max]).reshape(1, 2) # Check whether it is the correct maximum: second brightest pixel should be nearby tmp_center_frame[y_max, x_max] = 0. # introduce distance parameter dist = np.inf while dist > 2: coords = np.unravel_index( indices=np.argmax(tmp_center_frame), shape=tmp_center_frame.shape) y_max_new, x_max_new = coords[0], coords[1] pixmax_new = tmp_center_frame[y_max_new, x_max_new] # Caculate minimal distance to previous points tmp_center_frame[y_max_new, x_max_new] = 0. dist = np.amin( np.linalg.norm(np.vstack((max_pos[:, 0] - x_max_new, max_pos[:, 1] - y_max_new)), axis=0)) if dist <= 2 and pixmax_new < pixmax: break max_pos = np.vstack((max_pos, [x_max_new, y_max_new])) x_max = x_max_new y_max = y_max_new pixmax = pixmax_new x_0 = x_0 - (ref_image_size - 1) / 2 + x_max y_0 = y_0 - (ref_image_size - 1) / 2 + y_max # create reference image around determined maximum ref_center_frame = crop_image(image=center_frame_unsharp, center=(int(y_0), int(x_0)), size=ref_image_size) # Fit the data using astropy.modeling gauss_init = models.Gaussian2D( amplitude=np.amax(ref_center_frame), x_mean=x_0, y_mean=y_0, x_stddev=1., y_stddev=1., theta=0.) fit_gauss = fitting.LevMarLSQFitter() y_grid, x_grid = np.mgrid[y_0 - (ref_image_size - 1) / 2:y_0 + (ref_image_size - 1) / 2 + 1, x_0 - (ref_image_size - 1) / 2:x_0 + (ref_image_size - 1) / 2 + 1] gauss = fit_gauss(gauss_init, x_grid, y_grid, ref_center_frame) x_pos[i] = gauss.x_mean.value y_pos[i] = gauss.y_mean.value # Find star position as intersection of two lines x_center[w] = ((y_pos[0]-x_pos[0]*(y_pos[2]-y_pos[0])/(x_pos[2]-float(x_pos[0]))) - (y_pos[1]-x_pos[1]*(y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3])))) / \ ((y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3])) - (y_pos[2]-y_pos[0])/(x_pos[2]-float(x_pos[0]))) y_center[w] = x_center[w]*(y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3])) + \ (y_pos[1]-x_pos[1]*(y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3]))) # Adjust science images nimages = self.m_image_in_port.get_shape()[-3] npix = self.m_image_in_port.get_shape()[-2] nwavelengths = len(wavelength) start_time = time.time() for i in range(nimages): im_storage = [] for j in range(nwavelengths): im_index = i * nwavelengths + j progress(im_index, nimages * nwavelengths, 'Centering the images...', start_time) if ndim == 3: image = self.m_image_in_port[i, ] elif ndim == 4: image = self.m_image_in_port[j, i, ] shift_yx = np.array([ (float(im_shape[-2]) - 1.) / 2. - y_center[j], (float(im_shape[-1]) - 1.) / 2. - x_center[j] ]) if self.m_dither: index = np.digitize(i, nframes, right=False) - 1 shift_yx[0] -= dither_y[index] shift_yx[1] -= dither_x[index] if npix % 2 == 0 and self.m_size is not None: im_tmp = np.zeros((image.shape[0] + 1, image.shape[1] + 1)) im_tmp[:-1, :-1] = image image = im_tmp shift_yx[0] += 0.5 shift_yx[1] += 0.5 im_shift = shift_image(image, shift_yx, 'spline') if self.m_size is not None: im_crop = crop_image(im_shift, None, self.m_size) im_storage.append(im_crop) else: im_storage.append(im_shift) if ndim == 3: self.m_image_out_port.append(im_storage[0], data_dim=3) elif ndim == 4: self.m_image_out_port.append(np.asarray(im_storage), data_dim=4) print(f'Center [x, y] = [{x_center}, {y_center}]') history = f'[x, y] = [{round(x_center[j], 2)}, {round(y_center[j], 2)}]' self.m_image_out_port.copy_attributes(self.m_image_in_port) self.m_image_out_port.add_history('WaffleCenteringModule', history) self.m_image_out_port.close_port()
def run(self) -> None: """ Run method of the module. Locates the position of the calibration spots in the center frame. From the four spots, the position of the star behind the coronagraph is fitted, and the images are shifted and cropped. Returns ------- NoneType None """ def _get_center(center): center_frame = self.m_center_in_port[0, ] if center_shape[0] > 1: warnings.warn( 'Multiple center images found. Using the first image of the stack.' ) if center is None: center = center_pixel(center_frame) else: center = (np.floor(center[0]), np.floor(center[1])) return center_frame, center self.m_image_out_port.del_all_data() self.m_image_out_port.del_all_attributes() center_shape = self.m_center_in_port.get_shape() im_shape = self.m_image_in_port.get_shape() center_frame, self.m_center = _get_center(self.m_center) if im_shape[-2:] != center_shape[-2:]: raise ValueError( 'Science and center images should have the same shape.') pixscale = self.m_image_in_port.get_attribute('PIXSCALE') self.m_sigma /= pixscale if self.m_size is not None: self.m_size = int(math.ceil(self.m_size / pixscale)) if self.m_dither: dither_x = self.m_image_in_port.get_attribute('DITHER_X') dither_y = self.m_image_in_port.get_attribute('DITHER_Y') nframes = self.m_image_in_port.get_attribute('NFRAMES') nframes = np.cumsum(nframes) nframes = np.insert(nframes, 0, 0) center_frame_unsharp = center_frame - gaussian_filter( input=center_frame, sigma=self.m_sigma) # size of center image, only works with odd value ref_image_size = 21 # Arrays for the positions x_pos = np.zeros(4) y_pos = np.zeros(4) # Loop for 4 waffle spots for i in range(4): # Approximate positions of waffle spots if self.m_pattern == 'x': x_0 = np.floor(self.m_center[0] + self.m_radius * np.cos(np.pi / 4. * (2 * i + 1))) y_0 = np.floor(self.m_center[1] + self.m_radius * np.sin(np.pi / 4. * (2 * i + 1))) elif self.m_pattern == '+': x_0 = np.floor(self.m_center[0] + self.m_radius * np.cos(np.pi / 4. * (2 * i))) y_0 = np.floor(self.m_center[1] + self.m_radius * np.sin(np.pi / 4. * (2 * i))) tmp_center_frame = crop_image(image=center_frame_unsharp, center=(int(y_0), int(x_0)), size=ref_image_size) # find maximum in tmp image coords = np.unravel_index(indices=np.argmax(tmp_center_frame), shape=tmp_center_frame.shape) y_max, x_max = coords[0], coords[1] pixmax = tmp_center_frame[y_max, x_max] max_pos = np.array([x_max, y_max]).reshape(1, 2) # Check whether it is the correct maximum: second brightest pixel should be nearby tmp_center_frame[y_max, x_max] = 0. # introduce distance parameter dist = np.inf while dist > 2: coords = np.unravel_index(indices=np.argmax(tmp_center_frame), shape=tmp_center_frame.shape) y_max_new, x_max_new = coords[0], coords[1] pixmax_new = tmp_center_frame[y_max_new, x_max_new] # Caculate minimal distance to previous points tmp_center_frame[y_max_new, x_max_new] = 0. dist = np.amin( np.linalg.norm(np.vstack((max_pos[:, 0] - x_max_new, max_pos[:, 1] - y_max_new)), axis=0)) if dist <= 2 and pixmax_new < pixmax: break max_pos = np.vstack((max_pos, [x_max_new, y_max_new])) x_max = x_max_new y_max = y_max_new pixmax = pixmax_new x_0 = x_0 - (ref_image_size - 1) / 2 + x_max y_0 = y_0 - (ref_image_size - 1) / 2 + y_max # create reference image around determined maximum ref_center_frame = crop_image(image=center_frame_unsharp, center=(int(y_0), int(x_0)), size=ref_image_size) # Fit the data using astropy.modeling gauss_init = models.Gaussian2D(amplitude=np.amax(ref_center_frame), x_mean=x_0, y_mean=y_0, x_stddev=1., y_stddev=1., theta=0.) fit_gauss = fitting.LevMarLSQFitter() y_grid, x_grid = np.mgrid[y_0 - (ref_image_size - 1) / 2:y_0 + (ref_image_size - 1) / 2 + 1, x_0 - (ref_image_size - 1) / 2:x_0 + (ref_image_size - 1) / 2 + 1] gauss = fit_gauss(gauss_init, x_grid, y_grid, ref_center_frame) x_pos[i] = gauss.x_mean.value y_pos[i] = gauss.y_mean.value # Find star position as intersection of two lines x_center = ((y_pos[0]-x_pos[0]*(y_pos[2]-y_pos[0])/(x_pos[2]-float(x_pos[0]))) - (y_pos[1]-x_pos[1]*(y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3])))) / \ ((y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3])) - (y_pos[2]-y_pos[0])/(x_pos[2]-float(x_pos[0]))) y_center = x_center*(y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3])) + \ (y_pos[1]-x_pos[1]*(y_pos[1]-y_pos[3])/(x_pos[1]-float(x_pos[3]))) nimages = self.m_image_in_port.get_shape()[0] npix = self.m_image_in_port.get_shape()[1] start_time = time.time() for i in range(nimages): progress(i, nimages, 'Centering the images...', start_time) image = self.m_image_in_port[i, ] shift_yx = np.array([(float(im_shape[-2]) - 1.) / 2. - y_center, (float(im_shape[-1]) - 1.) / 2. - x_center]) if self.m_dither: index = np.digitize(i, nframes, right=False) - 1 shift_yx[0] -= dither_y[index] shift_yx[1] -= dither_x[index] if npix % 2 == 0 and self.m_size is not None: im_tmp = np.zeros((image.shape[0] + 1, image.shape[1] + 1)) im_tmp[:-1, :-1] = image image = im_tmp shift_yx[0] += 0.5 shift_yx[1] += 0.5 im_shift = shift_image(image, shift_yx, 'spline') if self.m_size is not None: im_crop = crop_image(im_shift, None, self.m_size) self.m_image_out_port.append(im_crop, data_dim=3) else: self.m_image_out_port.append(im_shift, data_dim=3) print(f'Center [x, y] = [{x_center}, {y_center}]') history = f'[x, y] = [{round(x_center, 2)}, {round(y_center, 2)}]' self.m_image_out_port.copy_attributes(self.m_image_in_port) self.m_image_out_port.add_history('WaffleCenteringModule', history) self.m_image_out_port.close_port()
def _crop(image_in, size, center): return crop_image(image_in, center, size)
def run(self) -> None: """ Run method of the module. Computes the similarity between frames based on the Mean Squared Error (MSE), the Pearson Correlation Coefficient (PCC), or the Structural Similarity (SSIM). Returns ------- NoneType None """ # get image number and image shapes nimages = self.m_image_in_port.get_shape()[0] im_shape = self.m_image_in_port.get_shape()[1:] cpu = self._m_config_port.get_attribute('CPU') pixscale = self.m_image_in_port.get_attribute('PIXSCALE') # convert arcsecs to pixels self.m_mask_radii = (math.floor(self.m_mask_radii[0] / pixscale), math.floor(self.m_mask_radii[1] / pixscale)) self.m_window_size = int(self.m_window_size / pixscale) # overlay the same mask over all images mask = create_mask(im_shape, self.m_mask_radii) images = self.m_image_in_port.get_all() # close the port during the calculations self.m_image_out_port.close_port() if self.m_temporal_median == 'constant': temporal_median = np.median(images, axis=0) else: temporal_median = False if self.m_method == 'SSIM': images = crop_image(images, None, int(self.m_mask_radii[1])) temporal_median = crop_image(temporal_median, None, int(self.m_mask_radii[1])) else: images *= mask # compare images and store similarity similarities = np.zeros(nimages) pool = mp.Pool(cpu) async_results = [] for i in range(nimages): async_results.append(pool.apply_async(FrameSimilarityModule._similarity, args=(images, i, self.m_method, self.m_window_size, temporal_median))) pool.close() start_time = time.time() # wait for all processes to finish while mp.active_children(): # number of finished processes nfinished = sum([i.ready() for i in async_results]) progress(nfinished, nimages, 'Running FrameSimilarityModule', start_time) # check if new processes have finished every 5 seconds time.sleep(5) # get the results for every async_result object for async_result in async_results: reference, similarity = async_result.get() similarities[reference] = similarity pool.terminate() # reopen the port after the calculation self.m_image_out_port.open_port() self.m_image_out_port.add_attribute(f'{self.m_method}', similarities, static=False) self.m_image_out_port.close_port()
def _image_cutting(image_in, size, center): return crop_image(image_in, center, size)
def run(self) -> None: """ Run method of the module. Decreases the image size by cropping around an given position. The module always returns odd-sized images. Returns ------- NoneType None """ # Get memory and number of images to split the frames into chunks memory = self._m_config_port.get_attribute('MEMORY') nimages = self.m_image_in_port.get_shape()[0] # Get the numnber of dimensions and shape ndim = self.m_image_in_port.get_ndim() im_shape = self.m_image_in_port.get_shape() if ndim == 3: # Number of images nimages = im_shape[-3] # Split into batches to comply with memory constraints frames = memory_frames(memory, nimages) elif ndim == 4: # Process all wavelengths per exposure at once frames = np.linspace(0, im_shape[-3], im_shape[-3] + 1) # Convert size parameter from arcseconds to pixels pixscale = self.m_image_in_port.get_attribute('PIXSCALE') print(f'New image size (arcsec) = {self.m_size}') self.m_size = int(math.ceil(self.m_size / pixscale)) print(f'New image size (pixels) = {self.m_size}') if self.m_center is not None: print(f'New image center (x, y) = {self.m_center}') # Crop images chunk by chunk start_time = time.time() for i in range(len(frames[:-1])): # Update progress bar progress(i, len(frames[:-1]), 'Cropping images...', start_time) # Select images in the current chunk if ndim == 3: images = self.m_image_in_port[frames[i]:frames[i + 1], ] elif ndim == 4: # Process all wavelengths per exposure at once images = self.m_image_in_port[:, i, ] # crop images according to input parameters images = crop_image(images, self.m_center, self.m_size, copy=False) # Write processed images to output port if ndim == 3: self.m_image_out_port.append(images, data_dim=3) elif ndim == 4: self.m_image_out_port.append(images, data_dim=4) # Save history and copy attributes history = f'image size (pix) = {self.m_size}' self.m_image_out_port.add_history('CropImagesModule', history) self.m_image_out_port.copy_attributes(self.m_image_in_port) self.m_image_out_port.close_port()