π§ More dotnetcore2 updgrades
This commit is contained in:
Π ΠΎΠ΄ΠΈΡΠ΅Π»Ρ
9178c57610
ΠΠΎΠΌΠΌΠΈΡ
b63c799791
362
README.md
362
README.md
|
@ -1,181 +1,181 @@
|
|||
# Introduction
|
||||
|
||||
InnerEye-DICOM-RT contains tools to convert medical datasets in NIFTI format to DICOM-RT. Datasets converted using
|
||||
this tool can be consumed directly by [InnerEye-DeepLearning](https://github.com/microsoft/InnerEye-DeepLearning).
|
||||
|
||||
Most of the work is done by a .NET Core 2.1 project in RTConvert, written in C#. There is a very lightweight wrapper
|
||||
around this so that it can be consumed from Python. The wrapper relies on the PyPI package https://pypi.org/project/dotnetcore2/ which wraps up .NET Core 2.1.
|
||||
|
||||
# Installing
|
||||
|
||||
## Git for Windows
|
||||
|
||||
Get the installer from [Git for Windows](https://git-scm.com/download/win)
|
||||
|
||||
The installer will prompt you to "Select Components". Make sure that you tick
|
||||
* Git LFS (Large File Support)
|
||||
* Git Credential Manager for Windows
|
||||
|
||||
After the installation, open a command prompt or the Git Bash:
|
||||
- Run `git lfs install` to set up the hooks in git
|
||||
- Run `git config --global core.autocrlf true` to ensure that line endings are working as expected
|
||||
|
||||
Clone the InnerEye-DICOM-RT repository on your machine: Run `git lfs clone --recursive https://github.com/microsoft/InnerEye-DICOM-RT`
|
||||
|
||||
## Visual Studio / .NET Core
|
||||
|
||||
The C# components can be built with the .NET Core SDK. We use version 2.1 for compatibility with the PyPI package `dotnetcore2`.
|
||||
Installation instructions are here: https://docs.microsoft.com/en-us/dotnet/core/install/.
|
||||
Visual Studio is not required to build, but if you wish to use it then for .Net Core 2.1 you need at least:
|
||||
[Visual Studio 2017 version 15.7](https://visualstudio.microsoft.com/vs/?utm_medium=microsoft&utm_source=docs.microsoft.com&utm_campaign=inline+link).
|
||||
|
||||
### RTConvert
|
||||
|
||||
RTConvert can be built from a .NET Core command line:
|
||||
|
||||
```bash
|
||||
dotnet build RTConvert
|
||||
```
|
||||
|
||||
There are unit tests:
|
||||
|
||||
```bash
|
||||
dotnet test RTConvert
|
||||
```
|
||||
|
||||
Note that the unit tests have a dependency on `System.Drawing` and that on Linux `System.Drawing` requires a native package:
|
||||
|
||||
```bash
|
||||
apt-get -s install libgdiplus
|
||||
```
|
||||
|
||||
Finally, for consumption by the Python wrapper, this solution must be published:
|
||||
|
||||
```bash
|
||||
dotnet publish RTConvert --configuration Release -p:Platform=x64
|
||||
```
|
||||
|
||||
This should create a folder with all the requirements for RTConvert at:
|
||||
`RTConvert/Microsoft.RTConvert.Console/bin/x64/Release/netcoreapp2.1/publish/*`
|
||||
|
||||
### Echo
|
||||
|
||||
Echo is a very simple application that takes 1 or 2 arguments. The first is echoed to `stdout`, and if a
|
||||
second argument is supplied then it is echoed to `stderr`. This is only required for units tests to establish
|
||||
that a .NET Core application can be called.
|
||||
|
||||
Echo can be built from a .NET Core command line:
|
||||
|
||||
```bash
|
||||
dotnet build Echo
|
||||
```
|
||||
|
||||
There are no unit tests.
|
||||
|
||||
Finally, for consumption by the Python wrapper, this solution must be published:
|
||||
|
||||
```bash
|
||||
dotnet publish Echo --configuration Release -p:Platform=x64
|
||||
```
|
||||
|
||||
This should create a folder with all the requirements for Echo at: `Echo/Echo/bin/x64/Release/netcoreapp2.1/publish/*`
|
||||
|
||||
## Python
|
||||
|
||||
The Python wrapper is in `src/InnerEye_DICOM_RT/nifti_to_dicom_rt_converter.py`. It simply uses `subprocess.Popen` to invoke
|
||||
the .NET Core application passing in the relevant dll and command line arguments.
|
||||
|
||||
It does require that the RTConvert and Echo published packages are copied to the folder: `src/InnerEye_DICOM_RT/bin/netcoreapp2.1`.
|
||||
|
||||
Note that the github build action does this automatically, but if testing then this needs to be done
|
||||
manually.
|
||||
|
||||
The Python package is created with:
|
||||
|
||||
```bash
|
||||
python setup.py sdist bdist_wheel
|
||||
```
|
||||
which builds a source distribution and wheel to the `dist` folder.
|
||||
|
||||
To run the Python tests:
|
||||
|
||||
```bash
|
||||
pip install pytest dotnetcore2
|
||||
pytest tests
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To consume this package:
|
||||
|
||||
```bash
|
||||
pip install InnerEye-DICOM-RT
|
||||
```
|
||||
|
||||
To call RTConvert:
|
||||
|
||||
```python
|
||||
from InnerEye_DICOM_RT.nifti_to_dicom_rt_converter import rtconvert
|
||||
|
||||
(stdout, stderr) = rtconvert(
|
||||
in_file=NiftiSegmentationLocation,
|
||||
reference_series=DicomVolumeLocation,
|
||||
out_file=OutputFile,
|
||||
struct_names=StructureNames,
|
||||
struct_colors=StructureColors,
|
||||
fill_holes=FillHoles,
|
||||
roi_interpreted_types=ROIInterpretedTypes,
|
||||
manufacturer=Manufacturer,
|
||||
interpreter=Interpreter,
|
||||
modelId=ModelId
|
||||
)
|
||||
```
|
||||
|
||||
where:
|
||||
* `in_file` is the path to the input Nifti file. This file is a 3D volume in [Nifti format](https://nifti.nimh.nih.gov/).
|
||||
* `reference_series` is the path to the input folder containing the reference DICOM series;
|
||||
* `out_file` is the path to the output DICOM-RT file;
|
||||
* `struct_names` is a list of structure names like: ["External", "parotid_l", "parotid_r", "smg_l"].
|
||||
Each structure name corresponds to a non-zero voxel value in the input volume. In the example External corresponds to voxel
|
||||
value 1, parotid_l to 2, etc. Voxels with value 0 are dropped.
|
||||
If there are voxels without a corresponding structure name, they will also be dropped.
|
||||
The structure name will become its ROI Name in the Structure Set ROI Sequence in the Structure Set in the DICOM-RT file.
|
||||
* `struct_colors` is a list of structure colors in hexadecimal notation like: ["000000", "FF0080", "00FF00", "0000FF"].
|
||||
Each color in this list corresponds to a structure in struct_names and will become its ROI Display Color
|
||||
in the ROI Contour Sequence in the ROI Contour in the DICOM-RT file.
|
||||
If there are less colors than struct_names, or if an entry is empty, the default is red (FF0000);
|
||||
* `fill_holes` is a list of bools like: [True, False, True].
|
||||
If there are less bools than struct_names, or if an entry is empty, the default is false.
|
||||
If True then any contours found per slice will have their holes filled, otherwise contours will be returned
|
||||
as found.
|
||||
* `modelId` Model name and version from AzureML. E.g. Prostate:123
|
||||
* `manufacturer` Manufacturer for the DICOM-RT (check DICOM-RT documentation)
|
||||
* `interpreter` Interpreter for the DICOM-RT (check DICOM-RT documentation)
|
||||
* `roi_interpreted_types` is a list of ROIInterpretedType. Possible values (None, CTV, ORGAN, EXTERNAL).
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
||||
**You are responsible for the performance, the necessary testing, and if needed any regulatory clearance for
|
||||
any of the models produced by this toolbox.**
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Trademarks
|
||||
|
||||
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
|
||||
trademarks or logos is subject to and must follow
|
||||
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
||||
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
||||
Any use of third-party trademarks or logos are subject to those third-party's policies.
|
||||
# Introduction
|
||||
|
||||
InnerEye-DICOM-RT contains tools to convert medical datasets in NIFTI format to DICOM-RT. Datasets converted using
|
||||
this tool can be consumed directly by [InnerEye-DeepLearning](https://github.com/microsoft/InnerEye-DeepLearning).
|
||||
|
||||
Most of the work is done by a .NET Core 2.1 project in RTConvert, written in C#. There is a very lightweight wrapper
|
||||
around this so that it can be consumed from Python. The wrapper relies on the PyPI package https://pypi.org/project/dotnetcore2/ which wraps up .NET Core 2.1.
|
||||
|
||||
# Installing
|
||||
|
||||
## Git for Windows
|
||||
|
||||
Get the installer from [Git for Windows](https://git-scm.com/download/win)
|
||||
|
||||
The installer will prompt you to "Select Components". Make sure that you tick
|
||||
* Git LFS (Large File Support)
|
||||
* Git Credential Manager for Windows
|
||||
|
||||
After the installation, open a command prompt or the Git Bash:
|
||||
- Run `git lfs install` to set up the hooks in git
|
||||
- Run `git config --global core.autocrlf true` to ensure that line endings are working as expected
|
||||
|
||||
Clone the InnerEye-DICOM-RT repository on your machine: Run `git lfs clone --recursive https://github.com/microsoft/InnerEye-DICOM-RT`
|
||||
|
||||
## Visual Studio / .NET Core
|
||||
|
||||
The C# components can be built with the .NET Core SDK. We use version 2.1 for compatibility with the PyPI package `dotnetcore2`.
|
||||
Installation instructions are here: https://docs.microsoft.com/en-us/dotnet/core/install/.
|
||||
Visual Studio is not required to build, but if you wish to use it then for .Net Core 2.1 you need at least:
|
||||
[Visual Studio 2017 version 15.7](https://visualstudio.microsoft.com/vs/?utm_medium=microsoft&utm_source=docs.microsoft.com&utm_campaign=inline+link).
|
||||
|
||||
### RTConvert
|
||||
|
||||
RTConvert can be built from a .NET Core command line:
|
||||
|
||||
```bash
|
||||
dotnet build RTConvert
|
||||
```
|
||||
|
||||
There are unit tests:
|
||||
|
||||
```bash
|
||||
dotnet test RTConvert
|
||||
```
|
||||
|
||||
Note that the unit tests have a dependency on `System.Drawing` and that on Linux `System.Drawing` requires a native package:
|
||||
|
||||
```bash
|
||||
apt-get -s install libgdiplus
|
||||
```
|
||||
|
||||
Finally, for consumption by the Python wrapper, this solution must be published:
|
||||
|
||||
```bash
|
||||
dotnet publish RTConvert --configuration Release -p:Platform=x64
|
||||
```
|
||||
|
||||
This should create a folder with all the requirements for RTConvert at:
|
||||
`RTConvert/Microsoft.RTConvert.Console/bin/x64/Release/netcoreapp3.1/publish/*`
|
||||
|
||||
### Echo
|
||||
|
||||
Echo is a very simple application that takes 1 or 2 arguments. The first is echoed to `stdout`, and if a
|
||||
second argument is supplied then it is echoed to `stderr`. This is only required for units tests to establish
|
||||
that a .NET Core application can be called.
|
||||
|
||||
Echo can be built from a .NET Core command line:
|
||||
|
||||
```bash
|
||||
dotnet build Echo
|
||||
```
|
||||
|
||||
There are no unit tests.
|
||||
|
||||
Finally, for consumption by the Python wrapper, this solution must be published:
|
||||
|
||||
```bash
|
||||
dotnet publish Echo --configuration Release -p:Platform=x64
|
||||
```
|
||||
|
||||
This should create a folder with all the requirements for Echo at: `Echo/Echo/bin/x64/Release/netcoreapp3.1/publish/*`
|
||||
|
||||
## Python
|
||||
|
||||
The Python wrapper is in `src/InnerEye_DICOM_RT/nifti_to_dicom_rt_converter.py`. It simply uses `subprocess.Popen` to invoke
|
||||
the .NET Core application passing in the relevant dll and command line arguments.
|
||||
|
||||
It does require that the RTConvert and Echo published packages are copied to the folder: `src/InnerEye_DICOM_RT/bin/netcoreapp3.1`.
|
||||
|
||||
Note that the github build action does this automatically, but if testing then this needs to be done
|
||||
manually.
|
||||
|
||||
The Python package is created with:
|
||||
|
||||
```bash
|
||||
python setup.py sdist bdist_wheel
|
||||
```
|
||||
which builds a source distribution and wheel to the `dist` folder.
|
||||
|
||||
To run the Python tests:
|
||||
|
||||
```bash
|
||||
pip install pytest dotnetcore2
|
||||
pytest tests
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To consume this package:
|
||||
|
||||
```bash
|
||||
pip install InnerEye-DICOM-RT
|
||||
```
|
||||
|
||||
To call RTConvert:
|
||||
|
||||
```python
|
||||
from InnerEye_DICOM_RT.nifti_to_dicom_rt_converter import rtconvert
|
||||
|
||||
(stdout, stderr) = rtconvert(
|
||||
in_file=NiftiSegmentationLocation,
|
||||
reference_series=DicomVolumeLocation,
|
||||
out_file=OutputFile,
|
||||
struct_names=StructureNames,
|
||||
struct_colors=StructureColors,
|
||||
fill_holes=FillHoles,
|
||||
roi_interpreted_types=ROIInterpretedTypes,
|
||||
manufacturer=Manufacturer,
|
||||
interpreter=Interpreter,
|
||||
modelId=ModelId
|
||||
)
|
||||
```
|
||||
|
||||
where:
|
||||
* `in_file` is the path to the input Nifti file. This file is a 3D volume in [Nifti format](https://nifti.nimh.nih.gov/).
|
||||
* `reference_series` is the path to the input folder containing the reference DICOM series;
|
||||
* `out_file` is the path to the output DICOM-RT file;
|
||||
* `struct_names` is a list of structure names like: ["External", "parotid_l", "parotid_r", "smg_l"].
|
||||
Each structure name corresponds to a non-zero voxel value in the input volume. In the example External corresponds to voxel
|
||||
value 1, parotid_l to 2, etc. Voxels with value 0 are dropped.
|
||||
If there are voxels without a corresponding structure name, they will also be dropped.
|
||||
The structure name will become its ROI Name in the Structure Set ROI Sequence in the Structure Set in the DICOM-RT file.
|
||||
* `struct_colors` is a list of structure colors in hexadecimal notation like: ["000000", "FF0080", "00FF00", "0000FF"].
|
||||
Each color in this list corresponds to a structure in struct_names and will become its ROI Display Color
|
||||
in the ROI Contour Sequence in the ROI Contour in the DICOM-RT file.
|
||||
If there are less colors than struct_names, or if an entry is empty, the default is red (FF0000);
|
||||
* `fill_holes` is a list of bools like: [True, False, True].
|
||||
If there are less bools than struct_names, or if an entry is empty, the default is false.
|
||||
If True then any contours found per slice will have their holes filled, otherwise contours will be returned
|
||||
as found.
|
||||
* `modelId` Model name and version from AzureML. E.g. Prostate:123
|
||||
* `manufacturer` Manufacturer for the DICOM-RT (check DICOM-RT documentation)
|
||||
* `interpreter` Interpreter for the DICOM-RT (check DICOM-RT documentation)
|
||||
* `roi_interpreted_types` is a list of ROIInterpretedType. Possible values (None, CTV, ORGAN, EXTERNAL).
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
||||
**You are responsible for the performance, the necessary testing, and if needed any regulatory clearance for
|
||||
any of the models produced by this toolbox.**
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Trademarks
|
||||
|
||||
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
|
||||
trademarks or logos is subject to and must follow
|
||||
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
||||
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
||||
Any use of third-party trademarks or logos are subject to those third-party's policies.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
dotnetcore2==2.1.23
|
||||
dotnetcore2==3.1.23
|
||||
flake8==5.0.4
|
||||
mypy==0.800
|
||||
pydicom==2.1.2
|
||||
|
|
2
setup.py
2
setup.py
|
@ -65,6 +65,6 @@ setup(
|
|||
package_dir={"": "src"},
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'dotnetcore2==2.1.23'
|
||||
'dotnetcore2==3.1.23'
|
||||
],
|
||||
)
|
||||
|
|
|
@ -87,7 +87,7 @@ def _make_dll_path(dll_name: str) -> Path:
|
|||
:return: Expected path to dll.
|
||||
"""
|
||||
current_folder = Path(__file__).parent.resolve()
|
||||
return current_folder / "bin" / "netcoreapp2.1" / dll_name
|
||||
return current_folder / "bin" / "netcoreapp3.1" / dll_name
|
||||
|
||||
|
||||
def _format_bool(b: bool) -> str:
|
||||
|
|
|
@ -25,7 +25,7 @@ logger.setLevel(logging.DEBUG)
|
|||
def test_get_version() -> None:
|
||||
"""
|
||||
Test that .dotnet core can be called --info and that it is
|
||||
running version 2.1.
|
||||
running version 3.1.
|
||||
"""
|
||||
(stdout, stderr) = get_version()
|
||||
|
||||
|
@ -33,7 +33,7 @@ def test_get_version() -> None:
|
|||
logger.debug("stderr: %s", stderr)
|
||||
|
||||
assert stderr == ''
|
||||
assert 'Microsoft.NETCore.App 2.1.' in stdout
|
||||
assert 'Microsoft.NETCore.App 3.1.' in stdout
|
||||
|
||||
|
||||
def test_echo() -> None:
|
||||
|
@ -78,42 +78,42 @@ TestOutputFile: Path = THIS_DIR / "test.dcm"
|
|||
|
||||
# Test fill holes.
|
||||
FillHoles: List[bool] = [
|
||||
True, True, True, True,
|
||||
False, False, True, True,
|
||||
True, True, False, True,
|
||||
True, True, True, False,
|
||||
True, False, True, True,
|
||||
False, True
|
||||
True, True, True, True,
|
||||
False, False, True, True,
|
||||
True, True, False, True,
|
||||
True, True, True, False,
|
||||
True, False, True, True,
|
||||
False, True
|
||||
]
|
||||
|
||||
# Test ROIInterpretedType.
|
||||
ROIInterpretedTypes: List[str] = [
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None"
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None", "CTV", "EXTERNAL",
|
||||
"ORGAN", "None"
|
||||
]
|
||||
|
||||
# Test structure colors.
|
||||
StructureColors: List[str] = [
|
||||
"FF0001", "FF0002", "FF0003", "FF0004",
|
||||
"FF0101", "FF0102", "FF0103", "FF0103",
|
||||
"FF0201", "FF02FF", "FF0203", "FF0204",
|
||||
"FF0301", "FF0302", "01FF03", "FF0304",
|
||||
"FF0401", "00FFFF", "FF0403", "FF0404",
|
||||
"FF0501", "FF0502"
|
||||
"FF0001", "FF0002", "FF0003", "FF0004",
|
||||
"FF0101", "FF0102", "FF0103", "FF0103",
|
||||
"FF0201", "FF02FF", "FF0203", "FF0204",
|
||||
"FF0301", "FF0302", "01FF03", "FF0304",
|
||||
"FF0401", "00FFFF", "FF0403", "FF0404",
|
||||
"FF0501", "FF0502"
|
||||
]
|
||||
|
||||
# Test structure names.
|
||||
StructureNames: List[str] = [
|
||||
"External", "parotid_l", "parotid_r", "smg_l",
|
||||
"smg_r", "spinal_cord", "brainstem", "globe_l",
|
||||
"Globe_r", "mandible", "spc_muscle", "mpc_muscle",
|
||||
"Cochlea_l", "cochlea_r", "lens_l", "lens_r",
|
||||
"optic_chiasm", "optic_nerve_l", "optic_nerve_r", "pituitary_gland",
|
||||
"lacrimal_gland_l", "lacrimal_gland_r"
|
||||
"External", "parotid_l", "parotid_r", "smg_l",
|
||||
"smg_r", "spinal_cord", "brainstem", "globe_l",
|
||||
"Globe_r", "mandible", "spc_muscle", "mpc_muscle",
|
||||
"Cochlea_l", "cochlea_r", "lens_l", "lens_r",
|
||||
"optic_chiasm", "optic_nerve_l", "optic_nerve_r", "pituitary_gland",
|
||||
"lacrimal_gland_l", "lacrimal_gland_r"
|
||||
]
|
||||
|
||||
Manufacturer = "Contosos"
|
||||
|
|
ΠΠ°Π³ΡΡΠ·ΠΊΠ°β¦
Π‘ΡΡΠ»ΠΊΠ° Π² Π½ΠΎΠ²ΠΎΠΉ Π·Π°Π΄Π°ΡΠ΅