Skip to content

Commit f491dfd

Browse files
committed
small fix to echogramviewer qt
1 parent 69db31e commit f491dfd

File tree

3 files changed

+91
-58
lines changed

3 files changed

+91
-58
lines changed

python/themachinethatgoesping/pingprocessing/watercolumn/echograms/echogrambuilder_refactored.py

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ def build_image(self, progress=None):
407407
def build_image_and_layer_image(self, progress=None):
408408
"""Build echogram image and combined layer image.
409409
410+
Uses fast vectorized get_image() for the main echogram when no main_layer
411+
is set. Falls back to per-column iteration only for layer processing.
412+
410413
Returns:
411414
Tuple of (image, layer_image, extent).
412415
"""
@@ -415,29 +418,34 @@ def build_image_and_layer_image(self, progress=None):
415418
ny = len(cs.y_coordinates)
416419
nx = len(cs.feature_mapper.get_feature_values("X coordinate"))
417420

418-
image = np.empty((nx, ny), dtype=np.float32)
419-
image.fill(np.nan)
420-
layer_image = image.copy()
421-
422-
image_indices, wci_indices = self.get_x_indices()
423-
image_indices = get_progress_iterator(image_indices, progress, desc="Building echogram image")
424-
425-
for image_index, wci_index in zip(image_indices, wci_indices):
426-
wci = self.get_column(wci_index)
427-
if len(wci) > 1:
428-
if self.main_layer is None:
429-
y1, y2 = self.get_y_indices(wci_index)
430-
if len(y1) > 0:
431-
image[image_index, y1] = wci[y2]
432-
else:
421+
# Fast path: use vectorized get_image for main echogram if no main_layer
422+
if self.main_layer is None:
423+
request = cs.make_image_request()
424+
image = self._backend.get_image(request)
425+
else:
426+
# Slow path: need per-column iteration for main_layer
427+
image = np.full((nx, ny), np.nan, dtype=np.float32)
428+
image_indices, wci_indices = self.get_x_indices()
429+
for image_index, wci_index in zip(image_indices, wci_indices):
430+
wci = self.get_column(wci_index)
431+
if len(wci) > 1:
433432
y1, y2 = self.main_layer.get_y_indices(wci_index)
434433
if y1 is not None and len(y1) > 0:
435434
image[image_index, y1] = wci[y2]
436435

437-
for k, layer in self.layers.items():
438-
y1_layer, y2_layer = layer.get_y_indices(wci_index)
439-
if y1_layer is not None and len(y1_layer) > 0:
440-
layer_image[image_index, y1_layer] = wci[y2_layer]
436+
# Build layer image (requires per-column iteration)
437+
layer_image = np.full((nx, ny), np.nan, dtype=np.float32)
438+
if len(self.layers) > 0:
439+
image_indices, wci_indices = self.get_x_indices()
440+
image_indices = get_progress_iterator(image_indices, progress, desc="Building layer image")
441+
442+
for image_index, wci_index in zip(image_indices, wci_indices):
443+
wci = self.get_column(wci_index)
444+
if len(wci) > 1:
445+
for k, layer in self.layers.items():
446+
y1_layer, y2_layer = layer.get_y_indices(wci_index)
447+
if y1_layer is not None and len(y1_layer) > 0:
448+
layer_image[image_index, y1_layer] = wci[y2_layer]
441449

442450
extent = deepcopy(cs.x_extent)
443451
extent.extend(cs.y_extent)
@@ -447,6 +455,9 @@ def build_image_and_layer_image(self, progress=None):
447455
def build_image_and_layer_images(self, progress=None):
448456
"""Build echogram image and individual layer images.
449457
458+
Uses fast vectorized get_image() for the main echogram when no main_layer
459+
is set. Falls back to per-column iteration only for layer processing.
460+
450461
Returns:
451462
Tuple of (image, layer_images_dict, extent).
452463
"""
@@ -455,32 +466,37 @@ def build_image_and_layer_images(self, progress=None):
455466
ny = len(cs.y_coordinates)
456467
nx = len(cs.feature_mapper.get_feature_values("X coordinate"))
457468

458-
image = np.empty((nx, ny), dtype=np.float32)
459-
image.fill(np.nan)
469+
# Fast path: use vectorized get_image for main echogram if no main_layer
470+
if self.main_layer is None:
471+
request = cs.make_image_request()
472+
image = self._backend.get_image(request)
473+
else:
474+
# Slow path: need per-column iteration for main_layer
475+
image = np.full((nx, ny), np.nan, dtype=np.float32)
476+
image_indices, wci_indices = self.get_x_indices()
477+
for image_index, wci_index in zip(image_indices, wci_indices):
478+
wci = self.get_column(wci_index)
479+
if len(wci) > 1:
480+
y1, y2 = self.main_layer.get_y_indices(wci_index)
481+
if y1 is not None and len(y1) > 0:
482+
image[image_index, y1] = wci[y2]
460483

484+
# Build layer images (requires per-column iteration)
461485
layer_images = {}
462486
for key in self.layers.keys():
463-
layer_images[key] = image.copy()
487+
layer_images[key] = np.full((nx, ny), np.nan, dtype=np.float32)
464488

465-
image_indices, wci_indices = self.get_x_indices()
466-
image_indices = get_progress_iterator(image_indices, progress, desc="Building echogram image")
489+
if len(self.layers) > 0:
490+
image_indices, wci_indices = self.get_x_indices()
491+
image_indices = get_progress_iterator(image_indices, progress, desc="Building layer images")
467492

468-
for image_index, wci_index in zip(image_indices, wci_indices):
469-
wci = self.get_column(wci_index)
470-
if len(wci) > 1:
471-
if self.main_layer is None:
472-
y1, y2 = self.get_y_indices(wci_index)
473-
if len(y1) > 0:
474-
image[image_index, y1] = wci[y2]
475-
else:
476-
y1, y2 = self.main_layer.get_y_indices(wci_index)
477-
if y1 is not None and len(y1) > 0:
478-
image[image_index, y1] = wci[y2]
479-
480-
for key, layer in self.layers.items():
481-
y1_layer, y2_layer = layer.get_y_indices(wci_index)
482-
if y1_layer is not None and len(y1_layer) > 0:
483-
layer_images[key][image_index, y1_layer] = wci[y2_layer]
493+
for image_index, wci_index in zip(image_indices, wci_indices):
494+
wci = self.get_column(wci_index)
495+
if len(wci) > 1:
496+
for key, layer in self.layers.items():
497+
y1_layer, y2_layer = layer.get_y_indices(wci_index)
498+
if y1_layer is not None and len(y1_layer) > 0:
499+
layer_images[key][image_index, y1_layer] = wci[y2_layer]
484500

485501
extent = deepcopy(cs.x_extent)
486502
extent.extend(cs.y_extent)
@@ -679,13 +695,18 @@ def get_raw_data_at_coordinates(self, x_coord, y_start, y_end):
679695

680696
ping_idx = ping_indices[0]
681697

682-
# Convert y coordinates to sample indices
683-
interpolator = cs.y_coordinate_indice_interpolator[ping_idx]
684-
if interpolator is None:
698+
# Convert y coordinates to sample indices using affine transform
699+
if cs._affine_y_to_sample is None:
700+
return None
701+
702+
a_inv, b_inv = cs._affine_y_to_sample
703+
a, b = a_inv[ping_idx], b_inv[ping_idx]
704+
705+
if not np.isfinite(a) or not np.isfinite(b):
685706
return None
686707

687-
sample_start = int(interpolator(y_start) + 0.5)
688-
sample_end = int(interpolator(y_end) + 0.5)
708+
sample_start = int(a + b * y_start + 0.5)
709+
sample_end = int(a + b * y_end + 0.5)
689710

690711
# Get raw data
691712
raw_column = self._backend.get_raw_column(ping_idx)

python/themachinethatgoesping/pingprocessing/watercolumn/echograms/layers/echolayer.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,16 @@ def __init__(self, echodata: "EchogramBuilder", vec_x_val, vec_min_y, vec_max_y)
8989
# Get n_pings from backend or pings list
9090
n_pings = self._get_n_pings()
9191

92-
# Create layer indices (i1 = last element + 1)
93-
i0 = np.empty(n_pings, dtype=int)
94-
i1 = np.empty(n_pings, dtype=int)
95-
for nr, interpolator in enumerate(cs.y_coordinate_indice_interpolator):
96-
if interpolator is not None:
97-
i0[nr] = int(interpolator(vec_min_y[nr]) + 0.5)
98-
i1[nr] = int(interpolator(vec_max_y[nr]) + 1.5)
92+
# Create layer indices using affine transforms (vectorized)
93+
# sample_index = a_inv + b_inv * y_coord (inverse of y = a + b * sample)
94+
if cs._affine_y_to_sample is not None:
95+
a_inv, b_inv = cs._affine_y_to_sample
96+
i0 = np.round(a_inv + b_inv * vec_min_y).astype(int)
97+
i1 = np.round(a_inv + b_inv * vec_max_y).astype(int) + 1
98+
else:
99+
# Fallback if affine not set
100+
i0 = np.zeros(n_pings, dtype=int)
101+
i1 = np.zeros(n_pings, dtype=int)
99102

100103
self.set_indices(i0, i1, vec_min_y, vec_max_y)
101104

@@ -223,6 +226,8 @@ def from_ping_param_offsets_relative(
223226
def get_y_indices(self, wci_nr: int) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]:
224227
"""Get Y indices constrained to layer bounds.
225228
229+
Uses precomputed affine coefficients for speed.
230+
226231
Args:
227232
wci_nr: Ping/column number.
228233
@@ -232,12 +237,18 @@ def get_y_indices(self, wci_nr: int) -> Tuple[Optional[np.ndarray], Optional[np.
232237
cs = self._cs
233238
n_samples = self._get_max_samples(wci_nr)
234239

235-
interpolator = cs.y_coordinate_indice_interpolator[wci_nr]
236-
if interpolator is None:
240+
# Use affine transform: sample_index = a + b * y_coord
241+
if cs._affine_y_to_sample is None:
242+
return None, None
243+
244+
a_inv, b_inv = cs._affine_y_to_sample
245+
a, b = a_inv[wci_nr], b_inv[wci_nr]
246+
247+
if not np.isfinite(a) or not np.isfinite(b):
237248
return None, None
238249

239250
y_indices_image = np.arange(len(cs.y_coordinates))
240-
y_indices_wci = np.round(interpolator(cs.y_coordinates)).astype(int)
251+
y_indices_wci = np.round(a + b * cs.y_coordinates).astype(int)
241252

242253
start_y = max(0, self.i0[wci_nr])
243254
end_y = min(n_samples, self.i1[wci_nr])

python/themachinethatgoesping/pingprocessing/widgets/echogramviewer_pyqtgraph.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def show_background_echogram(self) -> None:
267267
self.layer_images, self.layer_extents = [], []
268268
for idx, echogram in enumerate(self.echogramdata):
269269
self.progress.set_description(f"Updating echogram [{idx},{self.nechograms}]")
270-
if not echogram.layers and echogram.main_layer is None:
270+
if len(echogram.layers) == 0 and echogram.main_layer is None:
271271
image, extent = echogram.build_image(progress=self.progress)
272272
self.images_background.append(image)
273273
self.extents_background.append(extent)
@@ -303,7 +303,7 @@ def show_background_zoom(self, _event: Any = None) -> None:
303303
xmin, xmax = view.viewRange()[0]
304304
ymin, ymax = view.viewRange()[1]
305305
self._apply_axis_limits(echogram, xmin, xmax, ymin, ymax)
306-
if not echogram.layers and echogram.main_layer is None:
306+
if len(echogram.layers) == 0 and echogram.main_layer is None:
307307
image, extent = echogram.build_image(progress=self.progress)
308308
self.high_res_images[idx] = image
309309
self.high_res_extents[idx] = extent
@@ -419,7 +419,8 @@ def _update_plot_image(self, idx: int, key: str, data: np.ndarray, extent: Tuple
419419
y0, y1 = y1, y0
420420
rect = QtCore.QRectF(x0, y0, x1 - x0, y1 - y0)
421421
image_item.setRect(rect)
422-
colormap = self._colormap_layer if key != "background" else self._colormap
422+
# "background" and "high" use main colormap, "layer" uses layer colormap
423+
colormap = self._colormap_layer if key == "layer" else self._colormap
423424
if hasattr(image_item, "setColorMap"):
424425
image_item.setColorMap(colormap)
425426
else: # pragma: no cover - compatibility with older pyqtgraph

0 commit comments

Comments
 (0)