diff --git a/python/ouroboros/pipeline/pipeline_input.py b/python/ouroboros/pipeline/pipeline_input.py index 188712d..3fd472e 100644 --- a/python/ouroboros/pipeline/pipeline_input.py +++ b/python/ouroboros/pipeline/pipeline_input.py @@ -30,6 +30,7 @@ class PipelineInput(BasePipelineInput): backproject_options: BackprojectOptions | None = None source_url: str | None = None sample_points: np.ndarray | None = None + annotation_points: np.ndarray | None = None slice_rects: np.ndarray | None = None volume_cache: VolumeCache | None = None output_file_path: str | None = None @@ -37,11 +38,11 @@ class PipelineInput(BasePipelineInput): config_file_path: str | None = None backprojection_offset: str | None = None - @field_serializer("sample_points", "slice_rects") + @field_serializer("sample_points", "annotation_points", "slice_rects") def to_list(self, value) -> list | None: return value.tolist() if value is not None else None - @field_validator("sample_points", "slice_rects", mode="before") + @field_validator("sample_points", "annotation_points", "slice_rects", mode="before") @classmethod def validate_list(cls, value: any) -> np.ndarray | None: if isinstance(value, list): diff --git a/python/ouroboros/pipeline/slices_geom_pipeline.py b/python/ouroboros/pipeline/slices_geom_pipeline.py index 481d8d4..35cf205 100644 --- a/python/ouroboros/pipeline/slices_geom_pipeline.py +++ b/python/ouroboros/pipeline/slices_geom_pipeline.py @@ -65,6 +65,28 @@ def _process(self, input_data: tuple[any]) -> None | str: equidistant_points = spline(equidistant_params) + # Convert annotation points to straightened-volume coordinates. + # `spline.u` are the fit parameters corresponding to these input points. + annotation_projection_points = spline(spline.u).T + + _, normal_vectors, binormal_vectors = spline.calculate_rotation_minimizing_vectors(spline.u) + + annotation_offsets = sample_points - annotation_projection_points + lateral_offsets_x = np.einsum("ij,ij->i", annotation_offsets, normal_vectors.T) + lateral_offsets_y = np.einsum("ij,ij->i", annotation_offsets, binormal_vectors.T) + + straightened_depth = np.interp(spline.u, equidistant_params, + np.arange(len(equidistant_params), dtype=np.float32)) + + straightened_annotation_points = np.column_stack( + ( + (config.slice_width - 1) / 2.0 + lateral_offsets_x, + (config.slice_height - 1) / 2.0 - lateral_offsets_y, + straightened_depth, + ) + ) + pipeline_input.annotation_points = straightened_annotation_points + self.update_progress(0.5) # Calculate the slice rects for each t value