Skip to content

io

load_adjusters(path, mirror)

Get nominal adjuster locations from file.

Parameters:

Name Type Description Default
path str

Path to the data file.

required
mirror str

The mirror that these points belong to. Should be either: 'primary' or 'secondary'.

'primary'

Returns:

Name Type Description
adjusters dict[tuple[int, int], NDArray[float32]]

Nominal adjuster locations. This is indexed by a (row, col) tuple. Each entry is (5, 3) array where each row is an adjuster.

Source code in lat_alignment/io.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def load_adjusters(
    path: str, mirror: str
) -> dict[tuple[int, int], NDArray[np.float32]]:
    """
    Get nominal adjuster locations from file.

    Parameters
    ----------
    path : str
        Path to the data file.
    mirror : str, default: 'primary'
        The mirror that these points belong to.
        Should be either: 'primary' or 'secondary'.

    Returns
    -------
    adjusters : dict[tuple[int, int], NDArray[np.float32]]
        Nominal adjuster locations.
        This is indexed by a (row, col) tuple.
        Each entry is `(5, 3)` array where each row is an adjuster.
    """
    if mirror not in ["primary", "secondary"]:
        raise ValueError(f"Invalid mirror: {mirror}")

    def _transform(coords):
        coords = np.atleast_2d(coords)
        coords -= np.array([120, 0, 0])  # cancel out shift
        return coord_transform(coords, "va_global", f"opt_{mirror}")

    # TODO: cleaner transform call
    adjusters = defaultdict(list)
    c_points = np.genfromtxt(path, dtype=str)
    for point in c_points:
        row = point[0][6]
        col = point[0][7]
        adjusters[(row, col)] += [_transform(np.array(point[2:], dtype=np.float32))[0]]
    adjusters = {rc: np.vstack(pts) for rc, pts in adjusters.items()}

    return adjusters

load_corners(path)

Get panel corners from file.

Parameters:

Name Type Description Default
path str

Path to the data file.

required

Returns:

Name Type Description
corners dict[tuple[int, int], ndarray[float32]]

The corners. This is indexed by a (row, col) tuple. Each entry is (4, 3) array where each row is a corner.

Source code in lat_alignment/io.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def load_corners(path: str) -> dict[tuple[int, int], NDArray[np.float32]]:
    """
    Get panel corners from file.

    Parameters
    ----------
    path : str
        Path to the data file.

    Returns
    -------
    corners : dict[tuple[int, int], ndarray[np.float32]]
        The corners. This is indexed by a (row, col) tuple.
        Each entry is `(4, 3)` array where each row is a corner.
    """
    with open(path) as file:
        corners_raw = yaml.safe_load(file)

    corners = {
        (panel[7], panel[9]): np.vstack(
            [np.array(coord.split(), np.float32) for coord in coords]
        )
        for panel, coords in corners_raw.items()
    }
    return corners

load_photo(path, align=True, reference={}, err_thresh=2, plot=True, **kwargs)

Load photogrammetry data. Assuming first column is target names and next three are (x, y , z).

Parameters:

Name Type Description Default
path str

The path to the photogrammetry data.

required
align bool

If True align using the invar points.

True
reference dict

Reference dictionary for alignment. See transforms.align_photo for details. This is only used is align is True.

{}
err_thresh float

How many times the median photogrammetry error a target need to have to be cut.

2
plot bool

If True display a scatter plot of targets.

True
**kwargs

Arguments to pass to align_photo.

{}

Returns:

Name Type Description
data dict[str, NDArray[float32]]

The photogrammetry data. Dict is indexed by the target names.

alignment tuple[NDArray[float32], NDArray[float32]]

The transformation that aligned the points. The first element is a rotation matrix and the second is the shift.

Source code in lat_alignment/io.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def load_photo(
    path: str,
    align: bool = True,
    reference: dict = {},
    err_thresh: float = 2,
    plot: bool = True,
    **kwargs,
) -> tuple[
    dict[str, NDArray[np.float32]], tuple[NDArray[np.float32], NDArray[np.float32]]
]:
    """
    Load photogrammetry data.
    Assuming first column is target names and next three are (x, y , z).

    Parameters
    ----------
    path : str
        The path to the photogrammetry data.
    align : bool, default: True
        If True align using the invar points.
    reference : dict, default: {}
        Reference dictionary for alignment.
        See `transforms.align_photo` for details.
        This is only used is `align` is `True`.
    err_thresh : float, default: 2
        How many times the median photogrammetry error
        a target need to have to be cut.
    plot: bool, default: True
        If True display a scatter plot of targets.
    **kwargs
        Arguments to pass to `align_photo`.

    Returns
    -------
    data : dict[str, NDArray[np.float32]]
        The photogrammetry data.
        Dict is indexed by the target names.
    alignment : tuple[NDArray[np.float32], NDArray[np.float32]]
        The transformation that aligned the points.
        The first element is a rotation matrix and
        the second is the shift.
    """
    logger.info("Loading measurement data")
    labels = np.genfromtxt(path, dtype=str, delimiter=",", usecols=(0,))
    coords = np.genfromtxt(path, dtype=np.float32, delimiter=",", usecols=(1, 2, 3))
    errs = np.genfromtxt(path, dtype=np.float32, delimiter=",", usecols=(4, 5, 6))
    msk = (np.char.find(labels, "TARGET") >= 0) + (np.char.find(labels, "CODE") >= 0)

    labels, coords, errs = labels[msk], coords[msk], errs[msk]
    err = np.linalg.norm(errs, axis=-1)

    if align:
        labels, coords, msk, alignment = align_photo(
            labels, coords, reference, plot=plot, **kwargs
        )
        err = err[msk]
    else:
        alignment = (np.eye(3, dtype=np.float32), np.zeros(3, dtype=np.float32))
    trg_msk = np.char.find(labels, "TARGET") >= 0
    labels = labels[trg_msk]
    coords = coords[trg_msk]
    err = err[trg_msk]

    err_msk = err < err_thresh * np.median(err)
    labels, coords, err = labels[err_msk], coords[err_msk], err[err_msk]
    logger.info("\t%d points loaded", len(coords))

    # Lets find and remove doubles
    # Dumb brute force
    edm = make_edm(coords[:, :2])
    np.fill_diagonal(edm, np.nan)
    to_kill = []
    for i in range(len(edm)):
        if i in to_kill:
            continue
        imin = np.nanargmin(edm[i])
        if edm[i][imin] > 20:
            continue
        if err[i] < err[imin]:
            to_kill += [imin]
        else:
            to_kill += [i]
    msk = ~np.isin(np.arange(len(coords), dtype=int), to_kill)
    logger.info("\tFound and removed %d doubles", len(to_kill))
    labels, coords = labels[msk], coords[msk]

    if plot:
        plt.scatter(coords[:, 0], coords[:, 1], c=coords[:, 2], marker="x")
        plt.colorbar()
        plt.show()

    data = {label: coord for label, coord in zip(labels, coords)}
    return data, alignment