Coverage for src/ufig/psf_estimation/cutouts_utils.py: 100%
41 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-07 15:17 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-07 15:17 +0000
1# Copyright (c) 2017 ETH Zurich, Cosmology Research Group
2"""
3Created on Feb 23, 2021
4@author: Tomasz Kacprzak
5"""
7import numba as nb
8import numpy as np
11@nb.jit(nopython=True)
12def find_max_flux_pos(x, y, delta_x, delta_y, image):
13 """
14 Find the position of the maximum flux in a 2D image around a given position (x, y).
16 :param x: x-coordinate of the center position
17 :param y: y-coordinate of the center position
18 :param delta_x: array of x offsets to check
19 :param delta_y: array of y offsets to check
20 :param image: 2D numpy array representing the image
21 :return: (xi_max, yi_max) - coordinates of the pixel with maximum flux
22 """
24 max_flux = -np.inf
25 xi_max = 0
26 yi_max = 0
28 for dx in delta_x:
29 for dy in delta_y:
30 xi = x + dx
31 yi = y + dy
33 if (
34 0 <= xi < image.shape[1]
35 and 0 <= yi < image.shape[0]
36 and image[yi, xi] > max_flux
37 ):
38 max_flux = image[yi, xi]
39 xi_max = xi
40 yi_max = yi
42 return xi_max, yi_max
45def get_cutouts(x_peak, y_peak, image, pointings_maps, stamp_shape):
46 """
47 Get cube of coutouts around a list of coordinates.
49 :param x_peak: list of x positions
50 :param y_peak: list of y positions
51 :param image: 2D image
52 :param pointings_maps: an array with binary pointings indicators
53 :param stamp_shape: (nx, ny) shape of stamp, fixed for all objects
54 """
56 stamp_half_x = stamp_shape[1] // 2
57 stamp_half_y = stamp_shape[0] // 2
59 delta_x_check = np.arange(-1, 2, dtype=np.int32)
60 delta_y_check = np.arange(-1, 2, dtype=np.int32)
62 cube = np.zeros((len(x_peak),) + stamp_shape, dtype=np.float32)
64 select_image_boundary = np.zeros(len(x_peak), dtype=bool)
65 select_coadd_boundary = np.zeros(len(x_peak), dtype=bool)
66 select_max_flux_centered = np.zeros(len(x_peak), dtype=bool)
68 for i_obj in range(len(x_peak)):
69 # 1) select pixel with maximum intensity
70 xi_max, yi_max = find_max_flux_pos(
71 x_peak[i_obj], y_peak[i_obj], delta_x_check, delta_y_check, image
72 )
74 # 2) compute indices for cutting out
75 x_start = xi_max - stamp_half_x
76 y_start = yi_max - stamp_half_y
77 x_stop = xi_max + stamp_half_x + 1
78 y_stop = yi_max + stamp_half_y + 1
80 # 3) check if star is completely on image
81 select_image_boundary[i_obj] = (
82 (x_start >= 0)
83 & (y_start >= 0)
84 & (x_stop < image.shape[1])
85 & (y_stop < image.shape[0])
86 )
88 # 4) check if star hits any chip boundaries
89 pointings = pointings_maps[y_start:y_stop, x_start:x_stop]
90 select_coadd_boundary[i_obj] = len(np.unique(pointings)) == 1
92 # 5) check if cutout is centered on maximum flux (if full stamp is in image)
93 if select_image_boundary[i_obj]:
94 cutout = image[y_start:y_stop, x_start:x_stop]
95 select_max_flux_centered[i_obj] = cutout[
96 stamp_half_y, stamp_half_x
97 ] == np.amax(cutout)
98 else:
99 select_max_flux_centered[i_obj] = False
100 if select_max_flux_centered[i_obj]:
101 cube[i_obj, :, :] = cutout
103 return cube, select_image_boundary, select_coadd_boundary, select_max_flux_centered