GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — NDTimeseries is jerky sometimes · Issue #1054 · fastplotlib/fastplotlib Open Descriptiongetting this when zoomed in on the ephys viz jerky-ephys-1.mp4jerky-ephys-3.mp4Not jerkyness when zoomed out, and it's only with the heatmap. Neither padding nor sampling onto a consistent x-grid chosen a-priori fixes this. Suspect it might be something to do with being very zoomed in in world space. def _compute_delta_x_avg(self) -> float | None: """ Estimate a stable x step by sampling local spacing at the 20%, 40%, 60%, and 80% positions of the p reference range (5 consecutive data points each). Returns the mean step if the four estimates agree within 10%, otherwise warns and returns None so _create_heatmap_data falls back to a per-window x step. """ p_dim = self.processor.spatial_dims[1] p_range = self._ref_index.ref_ranges[p_dim] p_map = self.processor.slider_dim_transforms[p_dim] p_span = p_range.stop - p_range.start p_mid = p_range.start + p_span / 2 i_mid = p_map(p_mid) i_mid_inc = p_map(p_mid + p_range.step) delta_p = p_range.step / max(1, i_mid_inc - i_mid) # probe window that yields ~5 consecutive data points probe_window = 5 * delta_p orig_dw = self.processor.display_window self.processor.display_window = probe_window base_indices = {d: self._ref_index[d] for d in self.processor.slider_dims} local_steps = list() for frac in (0.2, 0.4, 0.6, 0.8): ref_val = p_range.start + frac * p_span indices = {**base_indices, p_dim: ref_val} x = (run_sync(self.processor.get(indices)))["data"][0, :, 0] if x.size >= 2: local_steps.append((x[-1] - x[0]) / (x.size - 1)) self.processor.display_window = orig_dw if len(local_steps) < 2: return None steps = np.array(local_steps, dtype=np.float64) mean_step = float(steps.mean()) spread = float((steps.max() - steps.min()) / mean_step) if spread > 0.10: warn( f"Heatmap x step varies by {spread:.1%} across the recording " f"({steps.min():.4g} to {steps.max():.4g}). " f"Using per-window x step; heatmap may jitter." ) return None return mean_step def _create_heatmap_data(self, data_slice) -> tuple[np.ndarray, float, float]: """return padded [n_rows, cap] heatmap, world-space x offset, and x step (sample period)""" # data slice is of shape [n_timeseries, n_timepoints, xy] x = data_slice[0, :, 0] if self._delta_x_avg is not None: print("using x avg") x_step = self._delta_x_avg p_dim = self.processor.spatial_dims[1] center = self._ref_index[p_dim] dw = self.processor.display_window n_cols = max(2, round(dw / x_step)) x_canonical_start = center - dw / 2 x_canonical = x_canonical_start + np.arange(n_cols) * x_step y_interp = np.empty((data_slice.shape[0], n_cols), dtype=np.float32) for i in range(data_slice.shape[0]): y_interp[i] = np.interp(x_canonical, x, data_slice[i, :, 1], left=np.nan, right=np.nan) x0 = x_canonical_start else: # check if we need to interpolate norm = np.linalg.norm(np.diff(np.diff(x))) / x.size if norm > 1e-6: # x is not uniform up to float32 precision, must interpolate x_uniform = np.linspace(x[0], x[-1], num=x.size) y_interp = np.empty(shape=data_slice[..., 1].shape, dtype=np.float32) # this for loop is actually slightly faster than numpy.apply_along_axis() for i in range(data_slice.shape[0]): y_interp[i] = np.interp(x_uniform, x, data_slice[i, :, 1]) else: # x is sufficiently uniform y_interp = data_slice[..., 1] n_cols = y_interp.shape[1] x0 = x[0] # true sample period: n_cols-1 intervals between n_cols values x_step = (x[-1] - x0) / (n_cols - 1) # pad with NaN columns on each side so frame-to-frame fluctuations in n_cols don't # trigger an ImageGraphic texture rebuild. reuse existing capacity if it still fits, # rebuild only when too small or > 10% too big. the max() in `upper` guards the small-n # case where ceil(1.10 * n_cols) falls below `target` and would force a rebuild on the # frame right after allocating. pad = max(1, int(np.ceil(0.03 * n_cols))) target = n_cols + 2 * pad upper = max(int(np.ceil(1.10 * n_cols)), target) existing = self._graphic.data.shape[1] if self._graphic is not None else 0 if n_cols <= existing <= upper: cap = existing pad_left = (cap - n_cols) // 2 else: cap = target pad_left = pad image_data = np.full((y_interp.shape[0], cap), np.nan, dtype=np.float32) image_data[:, pad_left:pad_left + n_cols] = y_interp # shift offset by pad_left columns so data column at index pad_left lands at world-x = x0 return image_data, x0 - (pad_left * x_step), x_step Activity
MetadataMetadataAssigneesLabelsNo labels TypeProjectsMilestoneRelationshipsDevelopmentIssue actions |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |