-
Notifications
You must be signed in to change notification settings - Fork 10
feat: Add saving and reading of time scale in ImageJ and OME TIFF files #1323
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
Conversation
Reviewer's GuideThis PR introduces support for reading and writing time scale metadata (time_increment) across ImageJ and OME TIFF formats by extending the image reader, writer, and Image model, adding unit conversions, and corresponding tests. ER diagram for time_increment and unit conversion in metadataerDiagram
IMAGE {
float time_increment
}
METADATA {
string TimeIncrement
string TimeIncrementUnit
float finterval
float fps
}
IMAGE ||--|| METADATA : "has"
Class diagram for updated Image class with time_increment propertyclassDiagram
class Image {
- _time_increment: float
+ time_increment: float
+ time_increment: float (getter)
+ time_increment(value: float) (setter)
}
Class diagram for updated ImageReader with time_increment supportclassDiagram
class ImageReader {
- time_increment: float
+ read_imagej_metadata(image_file)
+ read_ome_metadata(image_file)
}
Class diagram for updated ImageWriter with time_increment in metadataclassDiagram
class ImageWriter {
+ prepare_metadata(image: Image, channels: int): dict
+ save(image: Image, save_path, compression, **kwargs)
}
Flow diagram for reading and writing time_increment metadataflowchart TD
A["Read TIFF file"] --> B["Extract time_increment from metadata"]
B --> C["Set Image.time_increment"]
C --> D["Prepare metadata for saving"]
D --> E["Write time_increment to TIFF metadata"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdded time_increment support: Image gained a validated time_increment attribute and setter; readers extract and propagate time_increment from ImageJ/OME/TIFF metadata; writers persist TimeIncrement and unit (and IMAGEJ finterval/fps); tests added for setter validation and TIFF round-trip. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Reader as Image Reader
participant Meta as Metadata Parser
participant Img as Image Model
participant Writer as Image Writer
Reader->>Meta: open file (TIFF / ImageJ / OME)
Meta-->>Meta: extract spacing, channels, TimeIncrement / finterval
Meta->>Reader: set reader.time_increment
Reader->>Img: Instantiate image_class(..., spacing=..., time_increment=val, ...)
Img->>Img: store _time_increment (validate > 0)
Note right of Img #D5F5E3: exposes time_increment property
Writer->>Img: query image.time_increment
Writer->>Writer: prepare_metadata(...): include TimeIncrement, TimeIncrementUnit, finterval, fps
Writer->>Meta: write TIFF/IMAGEJ metadata with TimeIncrement fields
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Hey there - I've reviewed your changes - here's some feedback:
- Add a fallback to parse an ‘fps’ field from ImageJ metadata when ‘finterval’ is missing so that time_increment can still be recovered for older or alternative exports.
- Instead of mixing all temporal units into the general name_to_scalar dict, consider extracting a dedicated time_unit_to_scalar mapping to keep spatial and temporal unit conversions clearly separated.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Add a fallback to parse an ‘fps’ field from ImageJ metadata when ‘finterval’ is missing so that time_increment can still be recovered for older or alternative exports.
- Instead of mixing all temporal units into the general name_to_scalar dict, consider extracting a dedicated time_unit_to_scalar mapping to keep spatial and temporal unit conversions clearly separated.
## Individual Comments
### Comment 1
<location> `package/PartSegImage/image_reader.py:634-635` </location>
<code_context>
if "Ranges" in image_file.imagej_metadata:
ranges = image_file.imagej_metadata["Ranges"]
self.ranges = list(zip(ranges[::2], ranges[1::2]))
+ if "finterval" in image_file.imagej_metadata:
+ self.time_increment = image_file.imagej_metadata["finterval"]
self.metadata = image_file.imagej_metadata
</code_context>
<issue_to_address>
**suggestion:** Type conversion for 'finterval' may be needed for robustness.
If 'finterval' can be a string or another type, add explicit type conversion and error handling to ensure self.time_increment is always a float.
```suggestion
if "finterval" in image_file.imagej_metadata:
try:
self.time_increment = float(image_file.imagej_metadata["finterval"])
except (ValueError, TypeError):
# Optionally, log a warning here if you have a logger
self.time_increment = None
```
</issue_to_address>
### Comment 2
<location> `package/tests/test_PartSegImage/test_image_writer.py:163-170` </location>
<code_context>
assert np.all(np.isclose(read_mask.spacing, image.spacing))
+
+
[email protected]("save_method", [IMAGEJImageWriter.save, ImageWriter.save], ids=["ImageJ TIFF", "OME TIFF"])
+def test_save_time_meta(tmp_path, save_method):
+ data = np.zeros((5, 10, 20, 20), dtype=np.uint8)
+ image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=2.0)
+ save_method(image, tmp_path / "time_image.tif")
+ read_image = TiffImageReader.read_image(tmp_path / "time_image.tif")
+ assert np.all(np.isclose(image.spacing, read_image.spacing))
+ assert read_image.time_increment == 2.0
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding tests for edge cases of time_increment, such as zero, negative, and very large/small values.
Please add tests for zero, negative, and extreme time_increment values, and confirm that ValueError is raised for invalid inputs.
Suggested implementation:
```python
@pytest.mark.parametrize("save_method", [IMAGEJImageWriter.save, ImageWriter.save], ids=["ImageJ TIFF", "OME TIFF"])
def test_save_time_meta(tmp_path, save_method):
data = np.zeros((5, 10, 20, 20), dtype=np.uint8)
image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=2.0)
save_method(image, tmp_path / "time_image.tif")
read_image = TiffImageReader.read_image(tmp_path / "time_image.tif")
assert np.all(np.isclose(image.spacing, read_image.spacing))
assert read_image.time_increment == 2.0
@pytest.mark.parametrize(
"time_increment, should_raise",
[
(0.0, True), # zero value, should raise
(-1.0, True), # negative value, should raise
(1e-12, False), # very small positive value, should not raise
(1e12, False), # very large positive value, should not raise
],
ids=["zero", "negative", "very_small", "very_large"]
)
@pytest.mark.parametrize("save_method", [IMAGEJImageWriter.save, ImageWriter.save], ids=["ImageJ TIFF", "OME TIFF"])
def test_save_time_meta_edge_cases(tmp_path, save_method, time_increment, should_raise):
data = np.zeros((5, 10, 20, 20), dtype=np.uint8)
if should_raise:
import pytest
with pytest.raises(ValueError):
image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=time_increment)
save_method(image, tmp_path / f"time_image_{time_increment}.tif")
else:
image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=time_increment)
save_method(image, tmp_path / f"time_image_{time_increment}.tif")
read_image = TiffImageReader.read_image(tmp_path / f"time_image_{time_increment}.tif")
assert np.all(np.isclose(image.spacing, read_image.spacing))
assert read_image.time_increment == time_increment
```
- Ensure that the `Image` class and/or the `save_method` implementation actually raises a `ValueError` for zero or negative `time_increment`. If not, you will need to add validation logic in those classes.
- If the `Image` constructor does not currently validate `time_increment`, add a check like:
```
if time_increment is not None and time_increment <= 0:
raise ValueError("time_increment must be positive")
```
in the `Image` class.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @pytest.mark.parametrize("save_method", [IMAGEJImageWriter.save, ImageWriter.save], ids=["ImageJ TIFF", "OME TIFF"]) | ||
| def test_save_time_meta(tmp_path, save_method): | ||
| data = np.zeros((5, 10, 20, 20), dtype=np.uint8) | ||
| image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=2.0) | ||
| save_method(image, tmp_path / "time_image.tif") | ||
| read_image = TiffImageReader.read_image(tmp_path / "time_image.tif") | ||
| assert np.all(np.isclose(image.spacing, read_image.spacing)) | ||
| assert read_image.time_increment == 2.0 |
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.
suggestion (testing): Consider adding tests for edge cases of time_increment, such as zero, negative, and very large/small values.
Please add tests for zero, negative, and extreme time_increment values, and confirm that ValueError is raised for invalid inputs.
Suggested implementation:
@pytest.mark.parametrize("save_method", [IMAGEJImageWriter.save, ImageWriter.save], ids=["ImageJ TIFF", "OME TIFF"])
def test_save_time_meta(tmp_path, save_method):
data = np.zeros((5, 10, 20, 20), dtype=np.uint8)
image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=2.0)
save_method(image, tmp_path / "time_image.tif")
read_image = TiffImageReader.read_image(tmp_path / "time_image.tif")
assert np.all(np.isclose(image.spacing, read_image.spacing))
assert read_image.time_increment == 2.0
@pytest.mark.parametrize(
"time_increment, should_raise",
[
(0.0, True), # zero value, should raise
(-1.0, True), # negative value, should raise
(1e-12, False), # very small positive value, should not raise
(1e12, False), # very large positive value, should not raise
],
ids=["zero", "negative", "very_small", "very_large"]
)
@pytest.mark.parametrize("save_method", [IMAGEJImageWriter.save, ImageWriter.save], ids=["ImageJ TIFF", "OME TIFF"])
def test_save_time_meta_edge_cases(tmp_path, save_method, time_increment, should_raise):
data = np.zeros((5, 10, 20, 20), dtype=np.uint8)
if should_raise:
import pytest
with pytest.raises(ValueError):
image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=time_increment)
save_method(image, tmp_path / f"time_image_{time_increment}.tif")
else:
image = Image(data, spacing=(0.4, 0.1, 0.1), axes_order="TZYX", time_increment=time_increment)
save_method(image, tmp_path / f"time_image_{time_increment}.tif")
read_image = TiffImageReader.read_image(tmp_path / f"time_image_{time_increment}.tif")
assert np.all(np.isclose(image.spacing, read_image.spacing))
assert read_image.time_increment == time_increment- Ensure that the
Imageclass and/or thesave_methodimplementation actually raises aValueErrorfor zero or negativetime_increment. If not, you will need to add validation logic in those classes. - If the
Imageconstructor does not currently validatetime_increment, add a check like:in theif time_increment is not None and time_increment <= 0: raise ValueError("time_increment must be positive")Imageclass.
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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
package/PartSegImage/image.py (1)
788-788: Consider extracting the error message to a constant.While inline messages are common, extracting to a module-level constant improves consistency and testability. However, this is a stylistic choice and the current implementation is acceptable.
Example refactor:
+_TIME_INCREMENT_ERROR = "Time spacing need to be positive" + @time_increment.setter def time_increment(self, value: float): """set time spacing in seconds""" if value <= 0: - raise ValueError("Time spacing need to be positive") + raise ValueError(_TIME_INCREMENT_ERROR) self._time_increment = valueBased on learnings
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
package/PartSegImage/image.py(3 hunks)package/PartSegImage/image_reader.py(5 hunks)package/PartSegImage/image_writer.py(3 hunks)package/tests/test_PartSegImage/test_image_writer.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
package/tests/test_PartSegImage/test_image_writer.py (2)
package/PartSegImage/image_writer.py (5)
IMAGEJImageWriter(118-181)save(16-17)save(67-83)save(122-146)ImageWriter(25-115)package/PartSegImage/image_reader.py (2)
read_image(168-189)read_image(268-289)
package/PartSegImage/image_writer.py (2)
package/tests/conftest.py (1)
image(49-56)package/PartSegImage/image.py (5)
Image(198-1031)channels(636-638)time_increment(780-782)time_increment(785-789)is_time(626-628)
package/PartSegImage/image_reader.py (1)
package/PartSegImage/image.py (2)
time_increment(780-782)time_increment(785-789)
🪛 Ruff (0.14.1)
package/PartSegImage/image.py
788-788: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
- GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
- GitHub Check: 4DNucleome.PartSeg (Builds sdist)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
- GitHub Check: Base py3.11 / macos-15-intel py 3.11 latest PyQt5
- GitHub Check: test_coverage / ubuntu-24.04 py 3.12 latest PyQt5
- GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
- GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
- GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Sourcery review
🔇 Additional comments (10)
package/PartSegImage/image.py (1)
237-237: LGTM! Time increment parameter added correctly.The parameter has a sensible default value of 1.0 second and is properly stored as an internal attribute.
Also applies to: 260-260
package/tests/test_PartSegImage/test_image_writer.py (1)
163-170: LGTM! Comprehensive test for time metadata preservation.The test correctly verifies that
time_incrementis preserved through both ImageJ and OME TIFF write/read cycles. The parametrization ensures both writers handle time metadata consistently.package/PartSegImage/image_writer.py (3)
29-29: LGTM! Good addition of return type annotation.
52-52: LGTM! Time metadata correctly added to OME TIFF.The
TimeIncrementandTimeIncrementUnitfields follow OME-TIFF specifications and align with the spatial metadata already present.Also applies to: 56-56
133-136: LGTM! ImageJ time metadata added correctly.The implementation adds
fintervalandfpsto ImageJ metadata when time data is present. The zero check on line 135 is defensive programming—while the setter enforces positive values, this guards against any edge cases.package/PartSegImage/image_reader.py (5)
129-129: LGTM! Default time increment initialized consistently.The default value of 1.0 second matches the default in the
Imageclass constructor.
539-539: LGTM! Time increment passed to image constructor.The
time_incrementis correctly propagated from the reader to theImageclass constructor, completing the metadata flow.
634-635: LGTM! ImageJ time metadata extraction implemented correctly.The
fintervalfield is the standard ImageJ TIFF metadata key for frame intervals, and it's correctly extracted and assigned.
659-662: LGTM! OME time metadata extraction with unit conversion.The implementation correctly extracts
TimeIncrementandTimeIncrementUnitfrom OME metadata and applies the appropriate unit conversion, consistent with the spatial metadata handling pattern.
697-721: LGTM! Comprehensive time unit mappings added.The additions cover SI prefixes from yotta- to yocto-seconds, plus common time units (minutes, hours, days). The conversions are mathematically correct and follow the same pattern as existing spatial unit mappings.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1323 +/- ##
===========================================
+ Coverage 93.18% 93.21% +0.02%
===========================================
Files 210 210
Lines 33365 33406 +41
===========================================
+ Hits 31092 31138 +46
+ Misses 2273 2268 -5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
package/PartSegImage/image.py (1)
782-793: Consider extracting the error message to satisfy the linter.The property implementation with validation is correct. However, Ruff flags the inline error message at line 791 (TRY003). While functional, you could optionally define a custom exception class or extract the message to a constant for better maintainability.
If you'd like to address the linter warning, apply this diff:
+TIME_INCREMENT_ERROR_MSG = "Time spacing need to be positive" + @property def time_increment(self) -> float: """time spacing in seconds""" return self._time_increment @time_increment.setter def time_increment(self, value: float): """set time spacing in seconds""" if value <= 0: - raise ValueError("Time spacing need to be positive") + raise ValueError(TIME_INCREMENT_ERROR_MSG) self._time_increment = value
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/project_dict.pws(1 hunks)package/PartSegImage/image.py(6 hunks)package/PartSegImage/image_reader.py(5 hunks)package/tests/test_PartSegImage/test_image_writer.py(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- .github/project_dict.pws
🚧 Files skipped from review as they are similar to previous changes (1)
- package/tests/test_PartSegImage/test_image_writer.py
🧰 Additional context used
🧬 Code graph analysis (1)
package/PartSegImage/image_reader.py (1)
package/PartSegImage/image.py (2)
time_increment(783-785)time_increment(788-792)
🪛 Ruff (0.14.1)
package/PartSegImage/image.py
791-791: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
- GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
- GitHub Check: 4DNucleome.PartSeg (Builds sdist)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
- GitHub Check: Base py3.10 / ubuntu-24.04 py 3.10 latest PySide6
- GitHub Check: Base py3.11 / macos-15-intel py 3.11 latest PyQt5
- GitHub Check: Base py3.9 / ubuntu-24.04 py 3.9 latest PyQt5
- GitHub Check: test_coverage / ubuntu-24.04 py 3.12 latest PyQt5
- GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
- GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
- GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Sourcery review
🔇 Additional comments (6)
package/PartSegImage/image.py (1)
237-237: LGTM! Thetime_incrementparameter is now properly integrated and propagated.The addition of the
time_incrementparameter to__init__(line 237), its storage (line 260), and its propagation through thesubstitutemethod (lines 505, 515, 530) fully addresses the past review comment about missing time_increment preservation. The default value of 1.0 is reasonable.Based on past review comments.
Also applies to: 260-260, 505-505, 515-515, 530-530
package/PartSegImage/image_reader.py (5)
129-129: LGTM! Consistent initialization oftime_increment.The default value of 1.0 matches the default in the Image class and provides a sensible fallback when time metadata is unavailable.
634-636: Good error handling for ImageJ metadata parsing.The use of
float()conversion withsuppress(ValueError)properly handles cases wherefintervalmight be an invalid or non-numeric value. If conversion fails,time_incrementretains its default value of 1.0.Based on past review comments.
660-663: OME metadata parsing with unit conversion looks correct.The code properly extracts
TimeIncrementand converts it using the unit scalar fromTimeIncrementUnit. Thesuppress(KeyError)ensures graceful fallback if either field is missing or if the unit is not recognized inname_to_scalar.
698-721: Comprehensive time unit support added.The extended
name_to_scalardictionary includes a complete set of SI time prefixes and common time units. Spot-checked conversions are correct:
- SI prefixes follow powers of 10 (ms=10⁻³, µs=10⁻⁶, ks=10³, etc.)
- Common units are accurate (min=60, h=3600, d=86400)
539-539: LGTM! Time increment properly passed to Image constructor.The
time_incrementis correctly forwarded to theimage_classconstructor, completing the metadata propagation from TIFF reading to the final Image instance.
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.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
package/PartSegImage/image.py (1)
922-929: Thecut_imagemethod doesn't preservetime_increment.When creating a new image from a cut region, the method passes
spacingbut nottime_increment, causing the cut image to lose time metadata and revert to the default value of 1.0.Apply this diff to preserve
time_increment:return self.__class__( data=self._image_data_normalize(new_image), spacing=self._image_spacing, + time_increment=self._time_increment, file_path=None, mask=new_mask, channel_info=self._channel_info, axes_order=self.axis_order, )
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
package/PartSegImage/image.py(6 hunks)
🧰 Additional context used
🪛 Ruff (0.14.1)
package/PartSegImage/image.py
791-791: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
- GitHub Check: 4DNucleome.PartSeg (Builds sdist)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
- GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
- GitHub Check: Base py3.11 / macos-15-intel py 3.11 latest PyQt5
- GitHub Check: Base py3.11 / windows-latest py 3.11 latest PyQt5
- GitHub Check: Base py3.12 / ubuntu-22.04 py 3.12 latest PyQt6
- GitHub Check: Base py3.10 / ubuntu-22.04 py 3.10 latest PySide2
- GitHub Check: Test PartSeg minimal / ubuntu-24.04 py 3.9 latest PyQt5
- GitHub Check: test_coverage / ubuntu-24.04 py 3.12 latest PyQt5
- GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
- GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
- GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Sourcery review
🔇 Additional comments (1)
package/PartSegImage/image.py (1)
505-505: LGTM! Time increment is now preserved in substitution.The
substitutemethod correctly propagatestime_incrementthrough the copy operation, addressing the concern raised in previous reviews.Also applies to: 515-515, 530-530
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.
Actionable comments posted: 0
🧹 Nitpick comments (2)
package/PartSegImage/image.py (1)
260-261: Consider removing redundant initialization.Line 260 initializes
_time_incrementto 1.0, but line 261 immediately calls the setter which will overwrite it. The intermediate assignment adds no value since the setter will either succeed with the parameter value or raise aValueError.Apply this diff to simplify:
- self._time_increment = 1.0 self.time_increment = time_incrementpackage/PartSegImage/image_reader.py (1)
634-636: Consider logging when finterval parsing fails.The current implementation silently falls back to the default when
fintervalcannot be parsed as a float. While defensive, this could mask malformed metadata that users might want to know about.Apply this diff to add optional logging:
if "finterval" in image_file.imagej_metadata: - with suppress(ValueError): - self.time_increment = float(image_file.imagej_metadata["finterval"]) + try: + self.time_increment = float(image_file.imagej_metadata["finterval"]) + except (ValueError, TypeError) as e: + logging.debug(f"Could not parse finterval from ImageJ metadata: {e}")
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
package/PartSegImage/image.py(6 hunks)package/PartSegImage/image_reader.py(5 hunks)package/tests/test_PartSegImage/test_image.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
package/tests/test_PartSegImage/test_image.py (1)
package/PartSegImage/image.py (3)
spacing(779-781)time_increment(784-786)time_increment(789-793)
package/PartSegImage/image_reader.py (1)
package/PartSegImage/image.py (2)
time_increment(784-786)time_increment(789-793)
🪛 Ruff (0.14.1)
package/PartSegImage/image.py
792-792: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
- GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
- GitHub Check: 4DNucleome.PartSeg (Builds sdist)
- GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
- GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
- GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
- GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
- GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
- GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: Sourcery review
🔇 Additional comments (8)
package/tests/test_PartSegImage/test_image.py (1)
468-479: LGTM!The test comprehensively covers the time_increment property: default initialization, valid updates, and validation behavior. The guard for missing time axis and the verification that state is preserved after validation failure are well-designed.
package/PartSegImage/image.py (3)
237-237: LGTM!The default value of 1.0 for time_increment is appropriate for representing one-second intervals.
502-537: LGTM!The time_increment parameter is correctly integrated into the substitute method, following the same pattern as other optional parameters (defaulting to current value if None, then passing to constructor).
783-794: LGTM!The property implementation correctly enforces positive time_increment values with clear validation and documentation. The Ruff TRY003 hint about extracting the error message to a constant is a minor style preference that can be safely deferred.
package/PartSegImage/image_reader.py (4)
129-129: LGTM!Initializing time_increment to 1.0 provides a sensible default that matches the Image class default.
539-539: LGTM!The time_increment is correctly passed through to the image constructor, completing the data flow from metadata parsing to the final Image instance.
660-665: LGTM!The OME metadata parsing correctly applies unit conversion and validates that time_increment is positive before assignment. The validation is consistent with the Image class property setter.
700-724: LGTM!The time unit conversions are comprehensive and accurate, covering SI prefixes from yottasecond (10²⁴) to yoctosecond (10⁻²⁴), plus common units (minute, hour, day). All scalar values are correct.
|
Summary by Sourcery
Add full support for reading and writing time scale (time_increment) metadata in ImageJ and OME TIFF images, enrich Image model with a validated time_increment property, and extend unit conversion to cover time units.
New Features:
Enhancements:
Tests:
Summary by CodeRabbit
New Features
Tests