Introduce itk_infer command#71
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new CLI command itk_infer for batch inference on 3D medical images using either MMEngine or ONNX backends. The implementation includes sliding window inference support, multi-GPU processing, confidence scoring, and optional logits saving.
Changes:
- Added new
itk_inferCLI command for batch inference with MMEngine and ONNX backend support - Incremented package version from 4.0.0rc3 to 4.0.0rc4
- Added verbose output to TestPyPI publishing workflow for better debugging
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Version bump to 4.0.0rc4 and added entry point for itk_infer command |
| mkdocs.yml | Added documentation link for itk_infer in navigation menu |
| itkit/process/itk_infer.py | Complete implementation of batch inference tool with multi-processing support |
| docs/itk_infer.md | Comprehensive documentation covering usage, backends, parameters, and examples |
| docs/index.md | Added itk_infer to command-line tools list in main documentation page |
| .github/workflows/publish-to-pypi.yml | Added verbose flag to TestPyPI publishing step |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| confidence = calc_classwise_pred_confidence(pred_seg_logits) # (N,) | ||
| pred_confidences[file.stem] = confidence[0].cpu().item() | ||
|
|
||
| # Save segmentation logits as .npz |
There was a problem hiding this comment.
Incorrect comment. The comment says "Save segmentation logits as .npz" but the code actually saves them as .zarr files (line 177). Update the comment to reflect the actual file format.
| # Save segmentation logits as .npz | |
| # Save segmentation logits as Zarr (.zarr) |
| return tasks | ||
|
|
||
|
|
||
| def set_window(image_array:np.ndarray, wl:int, ww:int) -> np.ndarray: |
There was a problem hiding this comment.
Missing spaces after colons in type annotations. Should be wl: int, ww: int instead of wl:int, ww:int to follow PEP 8 style guidelines and be consistent with the rest of the codebase.
| def set_window(image_array:np.ndarray, wl:int, ww:int) -> np.ndarray: | |
| def set_window(image_array: np.ndarray, wl: int, ww: int) -> np.ndarray: |
| Returns: | ||
| confidence (Tensor): Tensor with shape (N,) representing the mean confidence across all classes. | ||
| """ | ||
| assert (C:=seg_logits.size(1)) >= 2, f"Number of classes must be at least 2, got {C}." |
There was a problem hiding this comment.
Missing spaces around walrus operator. Should be C := seg_logits.size(1) instead of C:=seg_logits.size(1) to follow PEP 8 style guidelines and be consistent with the rest of the codebase (e.g., itkit/process/itk_patch.py:186).
| assert (C:=seg_logits.size(1)) >= 2, f"Number of classes must be at least 2, got {C}." | |
| assert (C := seg_logits.size(1)) >= 2, f"Number of classes must be at least 2, got {C}." |
| p = mp.get_context('spawn').Process( | ||
| target = process_gpu_task, | ||
| args = (process_id, file_list, args, pred_conf_shared_dict), | ||
| daemon = True |
There was a problem hiding this comment.
Setting daemon=True for worker processes is problematic because daemon processes are forcibly terminated when the parent exits and cannot spawn child processes. Since the code already calls p.join() on line 265 to wait for all processes to complete, the daemon flag is unnecessary and could lead to incomplete file writes or data corruption if the parent process is interrupted. Remove the daemon=True parameter.
| daemon = True |
| target = process_gpu_task, | ||
| args = (process_id, file_list, args, pred_conf_shared_dict), | ||
| daemon = True |
There was a problem hiding this comment.
Spaces around = in keyword arguments violate PEP 8 style guidelines. According to PEP 8, keyword arguments should not have spaces around the equals sign. Change target = process_gpu_task to target=process_gpu_task, args = ... to args=..., and daemon = True to daemon=True.
| target = process_gpu_task, | |
| args = (process_id, file_list, args, pred_conf_shared_dict), | |
| daemon = True | |
| target=process_gpu_task, | |
| args=(process_id, file_list, args, pred_conf_shared_dict), | |
| daemon=True |
| itk_combine = "itkit.process.itk_combine:main" | ||
| itkit-app = "itkit.gui.app:main" | ||
| itk_slicer = "SlicerITKIT.server.itkit_server:main" | ||
| itk_infer = "itkit.inference.itk_infer:main" |
There was a problem hiding this comment.
The entry point path is incorrect. The file is located at itkit/process/itk_infer.py but the entry point references itkit.inference.itk_infer:main. This will cause the command to fail at runtime with a ModuleNotFoundError. The entry point should be changed to itkit.process.itk_infer:main to match the actual file location and be consistent with other CLI commands in this project.
| itk_infer = "itkit.inference.itk_infer:main" | |
| itk_infer = "itkit.process.itk_infer:main" |
| return tasks | ||
|
|
||
|
|
||
| def set_window(image_array:np.ndarray, wl:int, ww:int) -> np.ndarray: |
There was a problem hiding this comment.
Missing space after colon in type annotation. Should be image_array: np.ndarray instead of image_array:np.ndarray to follow PEP 8 style guidelines and be consistent with the rest of the codebase (e.g., itkit/process/itk_extract.py:18-24).
| def set_window(image_array:np.ndarray, wl:int, ww:int) -> np.ndarray: | |
| def set_window(image_array: np.ndarray, wl: int, ww: int) -> np.ndarray: |
| idx = 0 | ||
| for i in range(args.num_proc): | ||
| cnt = avg + (1 if i < rem else 0) | ||
| tasks.append(pending[idx:idx+cnt]) |
There was a problem hiding this comment.
Missing space around binary operator. Should be idx:idx + cnt instead of idx:idx+cnt to follow PEP 8 style guidelines.
| tasks.append(pending[idx:idx+cnt]) | |
| tasks.append(pending[idx:idx + cnt]) |
| logits_np = pred_seg_logits[0].cpu().numpy().astype(np.float16) | ||
| if args.save_logits: | ||
| zarr.save_array( | ||
| output_folder / (file.stem+'.zarr'), |
There was a problem hiding this comment.
Missing spaces around binary operator. Should be file.stem + '.zarr' instead of file.stem+'.zarr' to follow PEP 8 style guidelines and be consistent with the rest of the codebase.
| output_folder / (file.stem+'.zarr'), | |
| output_folder / (file.stem + '.zarr'), |
| import multiprocessing as mp | ||
| import os | ||
| import traceback | ||
| from multiprocessing import Manager |
There was a problem hiding this comment.
Module 'multiprocessing' is imported with both 'import' and 'import from'.
| from multiprocessing import Manager |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #71 +/- ##
=======================================
Coverage ? 72.14%
=======================================
Files ? 108
Lines ? 12167
Branches ? 1085
=======================================
Hits ? 8778
Misses ? 3139
Partials ? 250 ☔ View full report in Codecov by Sentry. |
itk_inferis a CLI entry to inference samples using OneDL-MMEngine backend or ONNX backend.Also contains modification on the deployment of pre-release to TestPyPI
📚 Documentation preview 📚: https://ITKIT--71.org.readthedocs.build/en/71/