-
Notifications
You must be signed in to change notification settings - Fork 3
[ESSIMAGING] Move tiff dumping method to essimaging and make it more generic. #737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
YooSunYoung
wants to merge
5
commits into
main
Choose a base branch
from
imaging-io
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+170
−64
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b73f4d2
Move tiff dumping method to essimaging and make it more generic.
YooSunYoung 56f3f4b
Use new method in the deprecated one
YooSunYoung 11da0f4
Merge branch 'main' into imaging-io
YooSunYoung cfccae2
Move tests to relevant module.
YooSunYoung 49cc7df
Add pulse_stride_offset
YooSunYoung File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2026 Scipp contributors (https://github.com/scipp) | ||
| import io | ||
| from pathlib import Path | ||
|
|
||
| import scipp as sc | ||
| import scippnexus as snx | ||
| import scitiff | ||
|
|
||
|
|
||
| def _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| event_time_zero: sc.Variable, | ||
| pulse_stride: int, | ||
| pulse_period: sc.Variable, | ||
| pulse_stride_offset: int = 0, | ||
| ) -> sc.Variable: | ||
| _pulse_period = pulse_period.to(unit=event_time_zero.unit) | ||
| etz = event_time_zero - sc.datetime(0, unit=event_time_zero.unit) | ||
| # The offset is used to place some etz value in the center of a binning | ||
| # where the bins have constant width pulse_period. | ||
| # That way small deviations in etz will not move the etz to the next | ||
| # or previous bin and each subsequent pulse will have a different index. | ||
| offset = _pulse_period / 2 - etz.nanmin() % _pulse_period | ||
| index = (((etz + offset) // _pulse_period) + pulse_stride_offset) % pulse_stride | ||
| return index * pulse_period | ||
|
|
||
|
|
||
| def tiff_from_event_data( | ||
| nexus_file_name: str | Path | io.BytesIO, | ||
| output_path: str | Path | io.BytesIO, | ||
| *, | ||
| time_bins: int | sc.Variable, | ||
| pulse_stride: int, | ||
| pulse_stride_offset: int = 0, | ||
| detector_group_path: str = "/entry/instrument/event_mode_detectors/timepix3", | ||
| event_data_field_path: str = "timepix3_events", | ||
| ) -> None: | ||
| ''' | ||
| Write a tiff image file representing the data from the nexus file. | ||
|
|
||
| Parameters | ||
| ------------ | ||
| nexus_file_name: | ||
| The file name of the nexus file to write to tiff. | ||
| output_path: | ||
| Where to write the tiff file. | ||
| time_bins: | ||
| The number of time slices the image should have. | ||
| pulse_stride: | ||
| The pulse stride that was used when doing the measurement. | ||
| pulse_stride_offset: | ||
| The offset to the pulse stride. | ||
| For example, if pulse_stride is 2, offset should be | ||
| 0 if the first event_time_zero set is the beginning of the pulse, | ||
| 1 if the first event_time_zero set is the second half of the pulse. | ||
| detector_group_path: | ||
| Path to the event data group in the nexus file. | ||
| event_data_field_path: | ||
| Path to the event data group in the nexus file. | ||
| ''' | ||
| with snx.File(nexus_file_name) as f: | ||
| data = f[detector_group_path][()][event_data_field_path] | ||
|
|
||
| data.bins.coords['event_time_offset'] += ( | ||
| _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| data.bins.coords['event_time_zero'], | ||
| pulse_stride=pulse_stride, | ||
| pulse_stride_offset=pulse_stride_offset, | ||
| pulse_period=sc.scalar(1 / 14, unit="s").to( | ||
| unit=data.bins.coords['event_time_offset'].unit | ||
| ), | ||
| ) | ||
| ) | ||
| # In case dimension names fall back to default ones. | ||
| ydim_name = 'dim_0' if 'dim_0' in data.dims else 'y_pixel_offset' | ||
| xdim_name = 'dim_1' if 'dim_1' in data.dims else 'x_pixel_offset' | ||
| image = data.hist(event_time_offset=time_bins).rename_dims( | ||
| {'event_time_offset': 't', ydim_name: 'y', xdim_name: 'x'} | ||
| ) | ||
| image = image.drop_coords([c for c in image.coords if image.coords[c].ndim > 1]) | ||
| scitiff.save_scitiff(image, output_path) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2026 Scipp contributors (https://github.com/scipp) | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| import ess.odin.data | ||
| import scipp as sc | ||
| from scipp.testing import assert_identical | ||
| from scitiff.io import load_scitiff | ||
|
|
||
| from ess.imaging.io import ( | ||
| _add_to_event_time_offset_in_case_of_pulse_skipping, | ||
| tiff_from_event_data, | ||
| ) | ||
|
|
||
|
|
||
| def test_tiff_dumping_helper(tmp_path: Path): | ||
| small_iron_image = ess.odin.data.iron_simulation_sample_small() | ||
| output_path = tmp_path / "dump_timepix.tiff" | ||
|
|
||
| tiff_from_event_data( | ||
| small_iron_image, | ||
| output_path, | ||
| time_bins=20, | ||
| pulse_stride=2, | ||
| ) | ||
| # Test if the saved tiff file has expected output. | ||
| loaded = load_scitiff(file_path=output_path, only_image=True) | ||
| assert loaded.sizes['t'] == 20 | ||
| assert 'x_pixel_offset' in loaded.coords | ||
| assert 'y_pixel_offset' in loaded.coords | ||
|
|
||
|
|
||
| def test_correct_event_time_offset() -> None: | ||
| assert_identical( | ||
| _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| sc.datetimes(dims='t', values=[0, 1, 2], unit='s'), | ||
| pulse_stride=2, | ||
| pulse_period=sc.scalar(1, unit='s'), | ||
| ), | ||
| sc.array(dims='t', values=[0, 1.0, 0], unit='s'), | ||
| ) | ||
| assert_identical( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't want to drag this on any longer than it needs to, but ideally I would say this test should be split into 5 tests, so that we can know quickly from the pytest output which is the case that is failing. |
||
| _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| sc.datetimes(dims='t', values=[0, 1, 2], unit='s'), | ||
| pulse_stride=2, | ||
| pulse_period=sc.scalar(1, unit='s'), | ||
| pulse_stride_offset=1, | ||
| ), | ||
| sc.array(dims='t', values=[1.0, 0, 1.0], unit='s'), | ||
| ) | ||
| assert_identical( | ||
| _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| sc.datetimes(dims='t', values=[10, 999, 2100], unit='ms'), | ||
| pulse_stride=2, | ||
| pulse_period=sc.scalar(1, unit='s'), | ||
| ), | ||
| sc.array(dims='t', values=[0, 1.0, 0], unit='s'), | ||
| ) | ||
| assert_identical( | ||
| _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| sc.datetimes(dims='t', values=[-100, 999, 2100], unit='ms'), | ||
| pulse_stride=3, | ||
| pulse_period=sc.scalar(1, unit='s'), | ||
| ), | ||
| sc.array(dims='t', values=[2, 0.0, 1], unit='s'), | ||
| ) | ||
| assert_identical( | ||
| _add_to_event_time_offset_in_case_of_pulse_skipping( | ||
| sc.datetimes(dims='t', values=[-100, 999, 2100], unit='ms'), | ||
| pulse_stride=3, | ||
| pulse_stride_offset=2, | ||
| pulse_period=sc.scalar(1, unit='s'), | ||
| ), | ||
| sc.array(dims='t', values=[1, 2.0, 0], unit='s'), | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also here have the same problem as in
essreduce: we don't know if the firstevent_time_zeroin the file was at the start of a frame_period, or half-way through a frame_period (i.e. should the index be[0, 1, 0, 1, ...], or should it be[1, 0, 1, 0, ...]?)See https://github.com/scipp/ess/blob/main/packages/essreduce/src/ess/reduce/unwrap/to_wavelength.py#L228
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we potentially do.
But we wanted to keep this method naiive and stupid though.
Maybe we can just add a keyword argument that fixes the start of the index...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, do that. And have it set to 0 by default.
In the generic workflow, it's called
pulse_stride_offset: PulseStrideOffset.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still missing this part and then we can merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nvaytet Yes, sorry...! I just pushed.