Coverage for src/spectroflat/utils/line_detection.py: 92%
38 statements
« prev ^ index » next coverage.py v7.3.2, created at 2024-03-28 07:59 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2024-03-28 07:59 +0000
1import numpy as np
2from scipy.signal import find_peaks
4from ..base.config import SmileConfig
5from ..fitting.line_fit import LineFit
8def find_line_cores(row: np.array, conf: SmileConfig, peaks: list = None) -> tuple:
9 allow_shift = False if peaks is None else True
10 if not conf.emission_spectrum:
11 row = row * -1
12 row, peaks = _find_peaks(row, conf, peaks)
13 return peaks, _find_minima(peaks, row, conf.line_distance // 2, allow_shift, conf.error_threshold)
16def _find_peaks(row: np.array, conf: SmileConfig, peaks: list = None) -> np.array:
17 row = row / np.abs(row.mean())
18 row = row - row.min()
19 h = row.mean() + conf.height_sigma * row.std()
20 if peaks is None:
21 peaks, _ = find_peaks(row, height=h, prominence=conf.line_prominence, distance=conf.line_distance)
22 return row, peaks
25def _find_minima(peaks: np.array, row: np.array, win: int, allow_shift: bool = False, max_error: float = 0.5) -> list:
26 return [_fit_peak(p, row, win, allow_shift, max_error) for p in peaks]
29def _fit_peak(peak: int, row: np.array, win: int, allow_shift: bool, max_error: float) -> float:
30 thresholds = [0.25, 0.5, 0.75, 1, 1.5, 2, max_error] if allow_shift else [0.3, max_error]
31 thresholds = sorted(thresholds)
32 if allow_shift:
33 a, b = _get_borders(peak, row, win)
34 alt = np.argmax(row[a:b])
35 peaks = [peak, a + alt]
36 else:
37 peaks = [peak]
38 for t in thresholds:
39 for p in peaks:
40 a, b = _get_borders(p, row, win)
41 lf = LineFit(np.arange(a, b), row[a:b], error_threshold=t)
42 try:
43 return lf.run().max_location
44 except RuntimeError:
45 pass
47 return peaks[1] if allow_shift else None
50def _get_borders(peak: int, row: np.array, win: int):
51 return int(max(0, peak - win)), int(min(len(row), peak + win))