from math import pi
import torch
[docs]
def bmul(vec, mat, axis=0):
"""Expand vector for batchwise matrix multiplication.
Parameters
----------
vec : 2dtensor
vector for multiplication
mat : 3dtensor
matrix for multiplication
axis : int, optional
batch axis, by default 0
Returns
-------
3dtensor
Product of matrix multiplication. (bs, n, m)
"""
mat = mat.transpose(axis, -1)
return (mat * vec.expand_as(mat)).transpose(axis, -1)
[docs]
def pca(image):
"""Compute the major components of an image. The Image is treated as a
distribution.
Parameters
----------
image : Image or 2DArray (N, M)
Image to be used as distribution
Returns
-------
cog_x :
X-position of the distributions center of gravity
cog_y :
Y-position of the distributions center of gravity
psi :
Angle between first mjor component and x-axis
"""
torch.set_printoptions(precision=16)
pix_x, pix_y, image = im_to_array_value(image)
cog_x = (torch.sum(pix_x * image, axis=1) / torch.sum(image, axis=1)).unsqueeze(-1)
cog_y = (torch.sum(pix_y * image, axis=1) / torch.sum(image, axis=1)).unsqueeze(-1)
delta_x = pix_x - cog_x
delta_y = pix_y - cog_y
inp = torch.cat([delta_x.unsqueeze(1), delta_y.unsqueeze(1)], dim=1)
cov_w = bmul(
(cog_x - 1 * torch.sum(image * image, axis=1).unsqueeze(-1) / cog_x).squeeze(1),
(torch.matmul(image.unsqueeze(1) * inp, inp.transpose(1, 2))),
)
eig_vals_torch, eig_vecs_torch = torch.linalg.eigh(cov_w, UPLO="U")
psi_torch = torch.atan(eig_vecs_torch[:, 1, 1] / eig_vecs_torch[:, 0, 1])
return cog_x, cog_y, psi_torch
[docs]
def calc_jet_angle(image):
"""Caluclate the jet angle from an image created with gaussian sources. This
is achieved by a PCA.
Parameters
----------
image : ndarray
input image
Returns
-------
float
slope of the line
float
intercept of the line
float
angle between the horizontal axis and the jet axis
"""
if not isinstance(image, torch.Tensor):
image = torch.tensor(image)
image = image.clone()
img_size = image.shape[-1]
# ignore negative pixels, which can appear in predictions
image[image < 0] = 0
if len(image.shape) == 2:
image = image.unsqueeze(0)
batch_size = image.shape[0]
# only use brightest pixel
max_val = torch.tensor([(i.max() * 0.4) for i in image])
max_arr = (torch.ones(img_size, img_size, batch_size) * max_val).permute(2, 0, 1)
image[image < max_arr] = 0
_, _, alpha_pca = pca(image)
# Search for sources with two maxima
maxima = []
for i in range(image.shape[0]):
a = torch.where(image[i] == image[i].max())
if len(a[0]) > 1:
# if two maxima are found, interpolate to the middle in x and y direction
mid_x = (a[0][1] - a[0][0]) // 2 + a[0][0]
mid_y = (a[1][1] - a[1][0]) // 2 + a[1][0]
maxima.extend([(mid_x, mid_y)])
else:
maxima.extend([a])
vals = torch.tensor(maxima)
x_mid = vals[:, 0]
y_mid = vals[:, 1]
m = torch.tan(pi / 2 - alpha_pca)
n = y_mid - m * x_mid
alpha = (alpha_pca) * 180 / pi
return m, n, alpha
[docs]
def im_to_array_value(image):
"""Transforms the image to an array of pixel coordinates and the containt
intensity
Parameters
----------
image: Image or 2DArray (N, M)
Image to be transformed
Returns
-------
x_coords : array_like
Contains the x-pixel-position of every pixel in the image
y_coords: array_like
Contains the y-pixel-position of every pixel in the image
value: array_like
Contains the image-value corresponding to every x-y-pair
"""
num = image.shape[0]
pix = image.shape[-1]
a = torch.arange(0, pix, 1)
grid_x, grid_y = torch.meshgrid(a, a, indexing="xy")
x_coords = torch.cat(num * [grid_x.flatten().unsqueeze(0)])
y_coords = torch.cat(num * [grid_y.flatten().unsqueeze(0)])
value = image.reshape(-1, pix**2)
return x_coords, y_coords, value