Metadata-Version: 2.4
Name: fw-gear-nifti-to-dicom
Version: 0.5.0
Summary: Convert NIfTI1 to DICOM
Project-URL: Repository, https://gitlab.com/flywheel-io/flywheel-apps/nifti-to-dicom.git
Author-email: Flywheel <support@flywheel.io>
License-Expression: MIT
License-File: LICENSE
Keywords: Flywheel,Gears
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Requires-Python: <4,>=3.12
Requires-Dist: flywheel-sdk>=21.6.1
Requires-Dist: fw-file>=4.2.3
Requires-Dist: fw-gear>=0.3.7
Requires-Dist: nibabel>=5.4.2
Requires-Dist: numpy>=2.4.4
Requires-Dist: simpleitk>=2.5.3
Description-Content-Type: text/markdown

# NIfTI to DICOM (nifti-to-dicom)

## Overview

*[Usage](#usage)*

### Summary

The NIfTI to DICOM gear is a Flywheel gear for the conversion of NIfTI images into
DICOM format. This gear takes as required input a NIfTI file and includes an optional
input for a template DICOM. If a DICOM is not input and `find_dicom_in_container` is
enabled, the gear will search for a template DICOM at runtime. If a template DICOM is
not provided, the gear will look for a JSON sidecar corresponding with the input NIfTI.

### Cite

None

### License

License: [MIT](LICENSE)

### Classification

*Category:* *Converter*

*Gear Level:*

- [ ] Project
- [ ] Subject
- [ ] Session
- [X] Acquisition
- [ ] Analysis

----

[[*TOC*]]

----

### Inputs

- nifti-file
  - __Name__: nifti-file
  - __Type__: NIfTI File
  - __Optional__: False
  - __Description__: A NIfTI file to be converted to DICOM
- dicom
  - __Name__: dicom
  - __Type__: DICOM File
  - __Optional__: True
  - __Description__: A template DICOM to be used to populate DICOM tags.
  - __Notes__: All public DICOM tags from the file are used to populate the output
  DICOM tags, except `SeriesInstanceUID` is replaced - by default with a
  deterministic UID generated from subject/session/acquisition labels and the
  NIfTI filename (not the original), or with the reference DICOM's own
  `SeriesInstanceUID` if `match_series_instance_uid` is enabled - Value1 of
  `ImageType` is set to `DERIVED`, and Value2 of `ImageType` is set to
  `SECONDARY`

### Config

- allow_no_sidecar
  - __Name__: allow_no_sidecar
  - __Type__: bool
  - __Description__: Allow for conversion to continue even if neither a DICOM nor a
  JSON sidecar is found, or if the found JSON sidecar does not have
  `ImageOrientationPatientDICOM`. __WARNING__: Unexpected orientation output may occur.
  - __Default__: False
- copy_metadata_from_dicom
  - __Name__: copy_metadata_from_dicom
  - __Type__: bool
  - __Description__: Copy Flywheel File metadata (modality, classification, info, tags) from template DICOM to output DICOM
  - __Default__: False
- debug
  - __Name__: debug
  - __Type__: bool
  - __Description__: Log debug statements
  - __Default__: False
- find_dicom_in_container
  - __Name__: find_dicom_in_container
  - __Type__: bool
  - __Description__: Search NIfTI parent acquisition container for a DICOM file. If a
  DICOM is already provided as input, this option is ignored. Otherwise, if a DICOM is
  not found, gear fails. If more than one DICOM is found, gear will use the DICOM with
  shortest filename.
  - __Default__: False
- force_16_bit
  - __Name__: force_16_bit
  - __Type__: bool
  - __Description__: Force output to be 16-bit (either UInt16 or Int16, depending on
  input).
  - __Default__: False
- overwrite_dicom
  - __Name__: overwrite_dicom
  - __Type__: bool
  - __Description__: Overwrite input template DICOM file. If template DICOM is not
  present in the same acquisition container as the NIfTI input, the output DICOM will
  be named to match the template DICOM file but will be saved in the NIfTI acquisition
  container. If no DICOM file is input/found, this option is ignored.
  - __Default__: False
- tag
  - __Name__: tag
  - __Type__: str
  - __Description__: Tag to be added to output DICOM file.
  - __Default__: "nifti-to-dicom"

### Outputs

#### Files

- Converted DICOM
  - __Name__: Converted DICOM
  - __Type__: DICOM File
  - __Optional__: False
  - __Description__: The result of converting the input NIfTI to DICOM

#### Metadata

If `copy_metadata_from_dicom` is enabled and a DICOM file is provided to the gear,
either as as input or by enabling `find_dicom_in_container`, the Flywheel file metadata
from the template DICOM will be copied to the output file. This includes modality,
classification, info, and tags.

### Pre-requisites

#### Prerequisite Gear Runs

No prerequisite gear runs are required for running nifti-to-dicom.

#### Prerequisite Files

To run this gear with a template DICOM, the DICOM must either exist within the same
acquisition container as the input NIfTI or be provided to the gear as input.

To run this gear with a JSON sidecar, either a JSON sidecar must exist within the same
acquisition container as the input NIfTI and have a matching filename (i.e
`example.nii.gz` matches `example.json`), or a JSON file named
`nifti_to_dicom_config.json` must exist at the project level.

#### Prerequisite Metadata

If using the sidecar method and no "modality" exists in the JSON, the gear looks at
first the input NIfTI file modality and then the sidecar's file modality. To ensure
that the approriate `SOPClassUID` is set on the output file, check the JSON sidecar
for "modality" and, if it does not exist, update the NIfTI file's modality to the
correct modality.
As a fallback, output DICOM files are set to `"MR"` (`SOPClassUID`:
`"1.2.840.10008.5.1.4.1.1.4"`).

The `copy_metadata_from_dicom` configuration option allows for copying Flywheel File
metadata from the template DICOM to the output DICOM. If this option is selected, the
template DICOM file metadata should exist in the state in which the user wants it
copied. The modality, classification, info, and tags are copied from the template DICOM.

## Usage

The NIfTI to DICOM gear converts NIfTI images to DICOM format using either an
associated JSON sidecar or a template DICOM.

The optional template DICOM can be input explicitly as the `dicom` gear input, or
the config option `find_dicom_in_container` can be enabled to tell the gear to
search the parent acquisition container of the NIfTI image for a DICOM file.
When searching for the DICOM file, the gear currently selects the DICOM with
the shortest filename in the acquisition container.

If a template DICOM is used, all public DICOM tags from the DICOM are used to create
the converted DICOM, except a new
`SeriesInstanceUID` is generated, Value1 of `ImageType` is set to `DERIVED` and Value 2
is set to `SECONDARY`, see [DICOM standard](
<https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.html#sect_C.7.6.1.1.2>
).

If a DICOM is not input or found, the gear will look for a JSON sidecar to populate
metadata. The gear first searches the acquisition container of the `nifti` file input
for a JSON file with a filename that matches the NIfTI file (`example.nii.gz` matches
`example.json`). If a match is not found, the gear then searches the parent project
for a file named `nifti_to_dicom_config.json`.

If neither a DICOM nor a JSON file is available to the gear at runtime, or if the
found JSON file does not include `ImageOrientationPatientDICOM`, by default the gear
fails. To override this safety measure, `allow_no_sidecar` can be enabled. When
`allow_no_sidecar` is `True` and `ImageOrientationPatientDICOM` is not available
to the gear, the conversion will continue and the output orientation will be set to
the input NIfTI orientation. This may result in unexpected orientation outcomes.

### Limitations

- Pixel Data type: Currently this gear does not handle NIfTI pixel data with complex
  values, or RGB values.
- Dimensions: Supports both 3D and 4D NIfTI input.
  - 4D conversion with a reference DICOM requires a genuine round-trip: the
      reference DICOM series must be the same acquisition the NIfTI was itself
      derived from (matching instance count and volume/slice structure), not an
      arbitrary or unrelated DICOM series - the gear pixel-matches NIfTI volumes
      back to reference DICOM instances and will fail validation otherwise.
  - 4D conversion without a reference DICOM requires a JSON sidecar with enough
      information to determine the 4th dimension's meaning (`RepetitionTime` for
      time-series, PET frame-timing fields, or an accompanying `.bval`/`.bvec`
      pair for diffusion) - see the `nifti_4d_dimension` config option. If
      neither a reference DICOM nor a sufficient JSON sidecar is available, the
      gear fails with a clear error rather than guessing.
  - Some 4D MR acquisition types aren't yet validated for sidecar-only
      conversion: ASL/perfusion is rejected outright with a clear error, and
      cardiac-gated cine isn't distinguished from an ordinary time-series and
      may be misclassified.
- Orientation:
  - When only a sidecar is provided, the gear uses `ImageOrientationPatientDICOM` in
      the JSON to re-orient the NIfTI. This field must be present for correct
      orientation.
  - Additionally, the `ImageOrientationPatientDICOM` field only carries in-plane
      information, not plane to plane. So the z-axis is calculated using a
      cross-product of the first two columns of this matrix. There may be some cases
      where the z-direction needs to be further flipped, but it is not clear when.
- Multiframe reference DICOM: When a DICOM is input, all of the tags that are
  consistent throughout the DICOM header are copied into the output DICOM. This method
  was developed for use with DICOM archives (zip files of multiple individual DICOM
  slices) and using a multiframe DICOM may result in unintended tags being copied over
  to the output DICOM.
- Modality: Currently, the gear supports MR, CT, OPT, and PET modalities when using the
sidecar conversion method. To use a modality outside of this list with the sidecar
conversion method, manually add `"SOPClassUID"` and `"MediaStorageSOPClassUID"`
to the JSON Sidecar according to the DICOM standard value.

### Workflow

```mermaid
graph LR;
    A[NIfTI]:::input --> D;
    B[DICOM] -->|optional| D;
    C[JSON sidecar] -->|optional| D
    A -->|Input as `nifti-input`| E

    D[Acquisition Container]:::container --> E((Gear));
    E:::gear -->|Output saved back to parent acquisition| D;

    classDef container fill:#57d,color:#fff
    classDef input fill:#7a9,color:#fff
    classDef gear fill:#659,color:#fff
```

1. Upload NIfTI file to Acquisition container
1. Optionally, upload corresponding JSON sidecar to Acquisition container
1. Optionally, upload template DICOM to Acquisition container
1. Select NIfTI file within Acquisition container as input to gear
1. Confirm configuration options
1. Gear places converted DICOM file within same Acquisition container

### Logging

The nifti-to-dicom gear logs various messages throughout the gear run to help the user
know what the gear is doing and if any errors are encountered.

### Testing

The nifti-to-dicom gear is tested both with pytest unit tests as well as an end-to-end
Jupyter Notebook testing suite within the [Scientific Solutions gear-testing repository](https://gitlab.com/flywheel-io/scientific-solutions/gears/gear-testing)
to ensure consistent, reliable outcomes.

## Contributing

For more information about how to get started contributing to this gear,
check out [CONTRIBUTING.md](CONTRIBUTING.md).
