• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jveitchmichaelis / rascal / 4515862454

pending completion
4515862454

push

github

cylammarco
added remarklint to pre-commit. linted everything.

1884 of 2056 relevant lines covered (91.63%)

3.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.62
/src/rascal/houghtransform.py
1
import numpy as np
4✔
2
import json
4✔
3

4

5
class HoughTransform:
4✔
6
    """
7
    This handles the hough transform operations on the pixel-wavelength space.
8

9
    """
10

11
    def __init__(self):
4✔
12
        self.hough_points = None
4✔
13
        self.hough_lines = None
4✔
14
        self.hist = None
4✔
15
        self.xedges = None
4✔
16
        self.yedges = None
4✔
17
        self.min_slope = None
4✔
18
        self.max_slope = None
4✔
19
        self.min_intercept = None
4✔
20
        self.max_intercept = None
4✔
21

22
    def set_constraints(
4✔
23
        self, min_slope, max_slope, min_intercept, max_intercept
24
    ):
25
        """
26
        Define the minimum and maximum of the intercepts (wavelength) and
27
        gradients (wavelength/pixel) that Hough pairs will be generated.
28

29
        Parameters
30
        ----------
31
        min_slope: int or float
32
            Minimum gradient for wavelength/pixel
33
        max_slope: int or float
34
            Maximum gradient for wavelength/pixel
35
        min_intercept: int/float
36
            Minimum interception point of the Hough line
37
        max_intercept: int/float
38
            Maximum interception point of the Hough line
39

40
        """
41

42
        assert np.isfinite(min_slope), (
4✔
43
            "min_slope has to be finite, %s is given " % min_slope
44
        )
45
        assert np.isfinite(max_slope), (
4✔
46
            "max_slope has to be finite, %s is given " % max_slope
47
        )
48
        assert np.isfinite(min_intercept), (
4✔
49
            "min_intercept has to be finite, %s is given " % min_intercept
50
        )
51
        assert np.isfinite(max_intercept), (
4✔
52
            "max_intercept has to be finite, %s is given " % max_intercept
53
        )
54

55
        self.min_slope = min_slope
4✔
56
        self.max_slope = max_slope
4✔
57
        self.min_intercept = min_intercept
4✔
58
        self.max_intercept = max_intercept
4✔
59

60
    def generate_hough_points(self, x, y, num_slopes):
4✔
61
        """
62
        Calculate the Hough transform for a set of input points and returns the
63
        2D Hough hough_points matrix.
64

65
        Parameters
66
        ----------
67
        x: 1D numpy array
68
            The x-axis represents peaks (pixel).
69
        y: 1D numpy array
70
            The y-axis represents lines (wavelength). Vertical lines
71
            (i.e. infinite gradient) are not accommodated.
72
        num_slopes: int
73
            The number of slopes to be generated.
74

75
        """
76

77
        # Getting all the slopes
78
        slopes = np.linspace(self.min_slope, self.max_slope, num_slopes)
4✔
79

80
        # Computing all the intercepts and gradients
81
        intercepts = np.concatenate(y - np.outer(slopes, x))
4✔
82
        gradients = np.concatenate(np.column_stack([slopes] * len(x)))
4✔
83

84
        # Apply boundaries
85
        mask = (self.min_intercept <= intercepts) & (
4✔
86
            intercepts <= self.max_intercept
87
        )
88
        intercepts = intercepts[mask]
4✔
89
        gradients = gradients[mask]
4✔
90

91
        # Create an array of Hough Points
92
        self.hough_points = np.column_stack((gradients, intercepts))
4✔
93

94
    def generate_hough_points_brute_force(self, x, y):
4✔
95
        """
96
        Calculate the Hough transform for a set of input points and returns the
97
        2D Hough hough_points matrix.
98

99
        Parameters
100
        ----------
101
        x: 1D numpy array
102
            The x-axis represents peaks (pixel).
103
        y: 1D numpy array
104
            The y-axis represents lines (wavelength). Vertical lines
105
            (i.e. infinite gradient) are not accommodated.
106
        num_slopes: int
107
            The number of slopes to be generated.
108

109
        """
110

111
        idx_sort = np.argsort(x)
4✔
112
        x = x[idx_sort]
4✔
113
        y = y[idx_sort]
4✔
114

115
        gradients = []
4✔
116
        intercepts = []
4✔
117

118
        # For each point (x, y), computes the gradient and intercept for all
119
        # (X, Y) with positive gradients
120
        for i in range(len(x) - 1):
4✔
121
            gradient_tmp = (y[i + 1 :] - y[i]) / (x[i + 1 :] - x[i])
4✔
122
            intercept_tmp = y[i + 1 :] - gradient_tmp * x[i + 1 :]
4✔
123

124
            gradients.append(gradient_tmp)
4✔
125
            intercepts.append(intercept_tmp)
4✔
126

127
        gradients = np.concatenate(gradients)
4✔
128
        intercepts = np.concatenate(intercepts)
4✔
129

130
        mask = (
4✔
131
            (gradients > self.min_slope)
132
            & (gradients < self.max_slope)
133
            & (intercepts > self.min_intercept)
134
            & (intercepts < self.max_intercept)
135
        )
136
        gradients = gradients[mask]
4✔
137
        intercepts = intercepts[mask]
4✔
138

139
        # Create an array of Hough Points
140
        self.hough_points = np.column_stack((gradients, intercepts))
4✔
141

142
    def add_hough_points(self, hp):
4✔
143
        """
144
        Extending the Hough pairs with an externally supplied HoughTransform
145
        object. This can be useful if the arc lines are very concentrated in
146
        some wavelength ranges while nothing in available in another part.
147

148
        Parameters
149
        ----------
150
        hp: numpy.ndarray with 2 columns or HoughTransform object
151
            An externally supplied HoughTransform object that contains
152
            hough_points.
153

154
        """
155

156
        if isinstance(hp, HoughTransform):
4✔
157
            points = hp.hough_points
4✔
158

159
        elif isinstance(hp, np.ndarray):
4✔
160
            points = hp
4✔
161

162
        else:
163
            raise TypeError("Unsupported type for extending hough points.")
×
164

165
        self.hough_points = np.vstack((self.hough_points, points))
4✔
166

167
    def bin_hough_points(self, xbins, ybins):
4✔
168
        """
169
        Bin up data by using a 2D histogram method.
170

171
        Parameters
172
        ----------
173
        xbins: int
174
            The number of bins in the pixel direction.
175
        ybins: int
176
            The number of bins in the wavelength direction.
177

178
        """
179

180
        assert self.hough_points is not None, "Please load an hough_points or "
4✔
181
        "create an hough_points with hough_points() first."
2✔
182

183
        self.hist, self.xedges, self.yedges = np.histogram2d(
4✔
184
            self.hough_points[:, 0],
185
            self.hough_points[:, 1],
186
            bins=(xbins, ybins),
187
        )
188

189
        # Get the line fit_coeffients from the promising bins in the
190
        # histogram
191
        self.hist_sorted_arg = np.dstack(
4✔
192
            np.unravel_index(
193
                np.argsort(self.hist.ravel())[::-1], self.hist.shape
194
            )
195
        )[0]
196

197
        xbin_width = (self.xedges[1] - self.xedges[0]) / 2
4✔
198
        ybin_width = (self.yedges[1] - self.yedges[0]) / 2
4✔
199

200
        lines = []
4✔
201

202
        for b in self.hist_sorted_arg:
4✔
203
            lines.append(
4✔
204
                (
205
                    self.xedges[b[0]] + xbin_width,
206
                    self.yedges[b[1]] + ybin_width,
207
                )
208
            )
209

210
        self.hough_lines = lines
4✔
211

212
    def save(
4✔
213
        self,
214
        filename="hough_transform",
215
        fileformat="npy",
216
        delimiter="+",
217
        to_disk=True,
218
    ):
219
        """
220
        Store the binned Hough space and/or the raw Hough pairs.
221

222
        Parameters
223
        ----------
224
        filename: str
225
            The filename of the output, not used if to_disk is False. It
226
            will be appended with the content type.
227
        format: str (default: 'npy')
228
            Choose from 'npy' and json'
229
        delimiter: str (default: '+')
230
            Delimiter for format and content types
231
        to_disk: boolean
232
            Set to True to save to disk, else return a numpy array object
233

234
        Returns
235
        -------
236
        hp_hough_points: numpy.ndarray
237
            only return if to_disk is False.
238

239
        """
240

241
        fileformat_split = fileformat.split(delimiter)
4✔
242

243
        if "npy" in fileformat_split:
4✔
244
            output_npy = []
4✔
245

246
            output_npy.append(self.hough_points)
4✔
247
            output_npy.append(self.hist)
4✔
248
            output_npy.append(self.xedges)
4✔
249
            output_npy.append(self.yedges)
4✔
250
            output_npy.append([self.min_slope])
4✔
251
            output_npy.append([self.max_slope])
4✔
252
            output_npy.append([self.min_intercept])
4✔
253
            output_npy.append([self.max_intercept])
4✔
254

255
            if to_disk:
4✔
256
                np.save(filename + ".npy", output_npy)
4✔
257

258
        if "json" in fileformat_split:
4✔
259
            output_json = {}
4✔
260

261
            output_json["hough_points"] = self.hough_points.tolist()
4✔
262
            output_json["hist"] = self.hist.tolist()
4✔
263
            output_json["xedges"] = self.xedges.tolist()
4✔
264
            output_json["yedges"] = self.yedges.tolist()
4✔
265
            output_json["min_slope"] = self.min_slope
4✔
266
            output_json["max_slope"] = self.max_slope
4✔
267
            output_json["min_intercept"] = self.min_intercept
4✔
268
            output_json["max_intercept"] = self.max_intercept
4✔
269

270
            if to_disk:
4✔
271
                with open(filename + ".json", "w+") as f:
4✔
272
                    json.dump(output_json, f)
4✔
273

274
        if not to_disk:
4✔
275
            if ("npy" in fileformat_split) and (
4✔
276
                "json" not in fileformat_split
277
            ):
278
                return output_npy
4✔
279

280
            elif ("npy" not in fileformat_split) and (
4✔
281
                "json" in fileformat_split
282
            ):
283
                return output_json
4✔
284

285
            elif ("npy" in fileformat_split) and ("json" in fileformat_split):
4✔
286
                return output_npy, output_json
4✔
287

288
            else:
289
                return None
×
290

291
    def load(self, filename="hough_transform", filetype="npy"):
4✔
292
        """
293
        Store the binned Hough space and/or the raw Hough pairs.
294

295
        Parameters
296
        ----------
297
        filename: str (default: 'hough_transform')
298
            The filename of the output, not used if to_disk is False. It
299
            will be appended with the content type.
300
        filetype: str (default: 'npy')
301
            The file type of the saved hough transform. Choose from 'npy'
302
            and 'json'.
303

304
        """
305

306
        if filetype == "npy":
4✔
307
            if filename[-4:] != ".npy":
4✔
308
                filename += ".npy"
4✔
309

310
            input_npy = np.load(filename, allow_pickle=True)
4✔
311

312
            self.hough_points = input_npy[0]
4✔
313
            self.hist = input_npy[1].astype("float")
4✔
314
            self.xedges = input_npy[2].astype("float")
4✔
315
            self.yedges = input_npy[3].astype("float")
4✔
316
            self.min_slope = float(input_npy[4][0])
4✔
317
            self.max_slope = float(input_npy[5][0])
4✔
318
            self.min_intercept = float(input_npy[6][0])
4✔
319
            self.max_intercept = float(input_npy[7][0])
4✔
320

321
        elif filetype == "json":
4✔
322
            if filename[-5:] != ".json":
4✔
323
                filename += ".json"
×
324

325
            input_json = json.load(open(filename))
4✔
326

327
            self.hough_points = input_json["hough_points"]
4✔
328
            self.hist = np.array(input_json["hist"]).astype("float")
4✔
329
            self.xedges = np.array(input_json["xedges"]).astype("float")
4✔
330
            self.yedges = np.array(input_json["yedges"]).astype("float")
4✔
331
            self.min_slope = float(input_json["min_slope"])
4✔
332
            self.max_slope = float(input_json["max_slope"])
4✔
333
            self.min_intercept = float(input_json["min_intercept"])
4✔
334
            self.max_intercept = float(input_json["max_intercept"])
4✔
335

336
        else:
337
            raise ValueError(
4✔
338
                "Unknown filetype %s, it has to be npy or json" % filetype
339
            )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc