import numpy as np
from typing import List, Union
[docs]class Beams:
"""
A class representing beams_dict.
- **Attributes** ::
:param beams_dict: beams_dict dictionary that contains information about the beams_dict in the format of
dict: {
'ID': list(int),
'gantry_angle': list(float),
'collimator_angle': list(float) }
}
:type beams_dict: dict
- **Methods** ::
:get_gantry_angle(beam_id: Optional(int, List[int]):
Get gantry angle in degrees
:get_collimator_angle(beam_id):
Get collimator angle in degrees
"""
def __init__(self, beams_dict):
"""
:param beams_dict: Beams dictionary containing information about beams
"""
self.beams_dict = beams_dict
self.preprocess_beams()
[docs] def get_beamlet_idx_2dgrid(self, beam_id: int) -> np.ndarray:
"""
:param beam_id: beam_id for the beam
:return: 2d grid of beamlets in 2.5*2.5 resolution
"""
ind = self.beams_dict['ID'].index(beam_id)
return self.beams_dict['beamlet_idx_2dgrid'][ind]
[docs] def get_gantry_angle(self, beam_id: Union[int, List[int]]) -> Union[int, List[int]]:
"""
Get gantry angle
:param beam_id: beam_id for the beam
:return: gantry angle for the beam_id
"""
if isinstance(beam_id, int):
ind = self.beams_dict['ID'].index(beam_id)
return self.beams_dict['gantry_angle'][ind]
elif isinstance(beam_id, list):
return [self.beams_dict['gantry_angle'][self.beams_dict['ID'].index(idx)] for idx in beam_id]
[docs] def get_collimator_angle(self, beam_id: Union[int, List[int]]) -> Union[int, List[int]]:
"""
Get collimator angle
:param beam_id: beam_id for the beam
:return: collimator angle for the beam_id
"""
if isinstance(beam_id, int):
ind = self.beams_dict['ID'].index(beam_id)
return self.beams_dict['collimator_angle'][ind]
elif isinstance(beam_id, list):
return [self.beams_dict['collimator_angle'][self.beams_dict['ID'].index(idx)] for idx in beam_id]
[docs] @staticmethod
def sort_beamlets(b_map):
c = b_map[b_map >= 0]
ind = np.arange(0, len(c))
c_sort = np.sort(c)
matrix_ind = [np.where(b_map == c_i) for c_i in c_sort]
map_copy = b_map.copy()
for i in range(len(ind)):
map_copy[matrix_ind[i]] = ind[i]
return map_copy
[docs] def preprocess_beams(self):
for i, beam_id in enumerate(self.beams_dict['ID']):
ind = self.beams_dict['ID'].index(beam_id)
beam_2d_grid = self.create_beamlet_idx_2d_grid(beam_id=beam_id)
self.beams_dict.setdefault('beamlet_idx_2dgrid', []).append(beam_2d_grid)
[docs] @staticmethod
def get_original_map(beam_map):
rowsNoRepeat = [0]
for i in range(1, np.size(beam_map, 0)):
if (beam_map[i, :] != beam_map[rowsNoRepeat[-1], :]).any():
rowsNoRepeat.append(i)
colsNoRepeat = [0]
for j in range(1, np.size(beam_map, 1)):
if (beam_map[:, j] != beam_map[:, colsNoRepeat[-1]]).any():
colsNoRepeat.append(j)
beam_map = beam_map[np.ix_(np.asarray(rowsNoRepeat), np.asarray(colsNoRepeat))]
return beam_map
[docs] def get_beamlet_width(self) -> float:
"""
:return: beamlet width in the original beam
"""
beamlets = self.beams_dict['beamlets'][0]
return beamlets['width_mm'][0][0][0]
[docs] def get_beamlet_height(self) -> float:
"""
:return: beamlet height in the original beam
"""
beamlets = self.beams_dict['beamlets'][0]
return beamlets['height_mm'][0][0][0]
[docs] def get_all_beam_ids(self) -> List[int]:
return self.beams_dict['ID']
[docs] def create_beamlet_idx_2d_grid(self, beam_id: int) -> np.ndarray:
"""
Create 2d grid for the beamlets where each element is 2.5mm*2.5mm for the given beam id from x and y coordinates of beamlets.
:param beam_id: beam_id for the beam
:return: 2d grid of beamlets for the beam
"""
ind = self.beams_dict['ID'].index(beam_id)
beamlets = self.beams_dict['beamlets'][ind]
x_positions = beamlets['position_x_mm'][0] - beamlets['width_mm'][0]/2 # x position is center of beamlet. Get left corner
y_positions = beamlets['position_y_mm'][0] + beamlets['height_mm'][0]/2 # y position is center of beamlet. Get top corner
right_ind = np.argmax(x_positions)
bottom_ind = np.argmin(y_positions)
w_all = np.column_stack((x_positions, y_positions)) # top left corners of all beamlets
x_coord = np.arange(np.min(x_positions), np.max(x_positions) + beamlets['width_mm'][0][right_ind], 2.5)
y_coord = np.arange(np.max(y_positions), np.min(y_positions) - beamlets['height_mm'][0][bottom_ind], -2.5)
# create mesh grid in 2.5 mm resolution
XX, YY = np.meshgrid(x_coord, y_coord)
beamlet_idx_2d_grid = np.ones_like(XX, dtype=int)
beamlet_idx_2d_grid = beamlet_idx_2d_grid * int(-1) # make all elements to -1
for row in range(XX.shape[0]):
for col in range(XX.shape[1]):
ind = np.where((w_all[:, 0] == XX[row, col]) & (w_all[:, 1] == YY[row, col])) # find the position in matrix where we find the beamlet
if np.size(ind) > 0:
ind = ind[0][0]
num_width = int(beamlets['width_mm'][0][ind]/2.5)
num_height = int(beamlets['height_mm'][0][ind] / 2.5)
beamlet_idx_2d_grid[row:row+num_height, col:col+num_width] = ind
return beamlet_idx_2d_grid