|
| 1 | +# An example of how to use the bundle config |
| 2 | + |
| 3 | +```bash |
| 4 | +python -m monai.bundle run display --config_file example.yaml |
| 5 | +``` |
| 6 | + |
| 7 | +By running the command within the same directory of this readme file, an example MedNIST dataset (~60MB) will be downloaded |
| 8 | +and a PyTorch-style `Dataset` will be created. The yaml config file also specifies the necessary commands to display the first pair of images in the dataset (shown in the figure). |
| 9 | + |
| 10 | +<div> <img src="../../figures/mednist_config_intro.png"/> </div> |
| 11 | + |
| 12 | +More specifically: |
| 13 | +- `python -m monai.bundle run` is the command [provided by the `monai.bundle` module](https://docs.monai.io/en/stable/bundle.html#monai.bundle.run). Apart from `run`, there are other actions available, such as `download` and `init_bundle`. |
| 14 | +- `display` is a user-defined component name in the `example.yaml` config file. |
| 15 | +- `--config_file example.yaml` specifies the config file describing the workflow, `monai.bundle` module supports both yaml and json formats ([syntax quick ref.](https://docs.monai.io/en/stable/config_syntax.html)). |
| 16 | + |
| 17 | +The rest of the readme file will explain how the yaml config file is parsed in detail. |
| 18 | + |
| 19 | +_Pre-requisites_ |
| 20 | + |
| 21 | +Installing MONAI (<https://docs.monai.io/en/stable/installation.html>) and basic knowledge of Python and Pytorch. |
| 22 | + |
| 23 | +## Parsing the `example.yaml` |
| 24 | + |
| 25 | +### Import modules |
| 26 | + |
| 27 | +The bundle parser will first evaluate all the import statements such as `import package` and `from package import mod`. |
| 28 | + |
| 29 | +```yaml |
| 30 | +imports: |
| 31 | + - $import glob |
| 32 | + - $import matplotlib.pyplot as plt |
| 33 | +``` |
| 34 | +
|
| 35 | +These two statements will be evaluated and the imported modules will be available throughout the config file. |
| 36 | +By default, the parser imports `monai`'s modules and `import numpy as np` for you, so you don't need to add them in the config file. |
| 37 | + |
| 38 | +### Running the `display` component |
| 39 | + |
| 40 | +After all the import statements are evaluated, the `display` component defined as follows will be evaluated: |
| 41 | + |
| 42 | +```yaml |
| 43 | +display: |
| 44 | + - _requires_: "@downloading" |
| 45 | + - $print("displaying images:") |
| 46 | + - $plt.subplot(1,2,1) |
| 47 | + - $plt.imshow(@first_pair['f_img'][0], cmap="gray") |
| 48 | + - $plt.subplot(1,2,2) |
| 49 | + - $plt.imshow(@first_pair['m_img'][0], cmap="gray") |
| 50 | + - $plt.show() |
| 51 | +``` |
| 52 | + |
| 53 | +`_requires_` is a special key for the bundle parsing, it specifies the dependencies of the component. In this case, the `display` component requires the `downloading` component to be executed first. |
| 54 | + |
| 55 | +The rest of the keys are the list of commands to be executed. The bundle parser will evaluate the commands in the order they are defined in the config file. |
| 56 | + |
| 57 | +`$` is the prefix for the commands, it tells the parser to evaluate the following string as a Python expression. |
| 58 | + |
| 59 | +The `@` prefix is used to refer to another component. In this case, the `display` component refers to the output of the `first_pair` component, which is a dictionary containing the first pair of images in the dataset. |
| 60 | +At the runtime, the `@first_pair` will be replaced by the output of the `first_pair` component. |
| 61 | + |
| 62 | +### Running the `first_pair` component |
| 63 | + |
| 64 | +```yaml |
| 65 | +first_pair: $@preprocessing(@paired_dataset[0]) |
| 66 | +``` |
| 67 | + |
| 68 | +The `first_pair` component is a simple Python expression, it refers to calling the `preprocessing` python instance with the first element of `paired_dataset`, which is a dictionary containing the first pair of images in the dataset. |
| 69 | + |
| 70 | +### Defining the `preprocessing` component |
| 71 | + |
| 72 | +```yaml |
| 73 | +preprocessing: |
| 74 | + _target_: Compose |
| 75 | + transforms: |
| 76 | +
|
| 77 | + - _target_: LoadImaged |
| 78 | + keys: [f_img, m_img] |
| 79 | + image_only: True |
| 80 | +
|
| 81 | + - _target_: EnsureChannelFirstd |
| 82 | + keys: [f_img, m_img] |
| 83 | +
|
| 84 | + - _target_: ScaleIntensityRanged |
| 85 | + keys: [f_img, m_img] |
| 86 | + a_min: 0. |
| 87 | + a_max: 255. |
| 88 | + b_min: 0.0 |
| 89 | + b_max: 1.0 |
| 90 | +
|
| 91 | + - _target_: RandRotated |
| 92 | + keys: [m_img] |
| 93 | + range_x: $np.pi/4 |
| 94 | + prob: 1.0 |
| 95 | + mode: "bicubic" |
| 96 | + keep_size: True |
| 97 | +
|
| 98 | + - _target_: RandZoomd |
| 99 | + keys: [m_img] |
| 100 | + min_zoom: 0.9 |
| 101 | + max_zoom: 1.1 |
| 102 | + prob: 1.0 |
| 103 | + mode: "bicubic" |
| 104 | +``` |
| 105 | + |
| 106 | +`_target_` is a special key for the bundle parsing, it specifies the class to be instantiated. In this case, the `preprocessing` component is a `Compose` object. `Compose` is resolved to `monai.transforms.Compose` by default, so you don't need to specify the full path. |
| 107 | + |
| 108 | +`transforms` is the first argument to `monai.transforms.Compose`, it specifies the list of transforms to be added to the `Compose` object. |
| 109 | + |
| 110 | +The rest of the keys are the arguments to the transforms. In this case, the `LoadImaged` transform is used to load the images from the file paths, `EnsureChannelFirstd` transform is used to ensure the image data is in channel-first format, `ScaleIntensityRanged` transform is used to scale the image intensity to `[0, 1]`. `RandRotated` transform is used to randomly rotate the moving image, and `RandZoomd` transform is used to randomly zoom the moving image. |
| 111 | + |
| 112 | + |
| 113 | +_The equivalent Python code of the yaml config file as a comparison_ |
| 114 | + |
| 115 | +```py |
| 116 | +import glob |
| 117 | +import matplotlib.pyplot as plt |
| 118 | +import monai |
| 119 | +import monai.transforms as mt |
| 120 | +import numpy as np |
| 121 | +
|
| 122 | +url = "https://github.com/Project-MONAI/MONAI-extra-test-data/releases/download/0.8.1/MedNIST.tar.gz" |
| 123 | +monai.apps.utils.download_and_extract(url, './mednist.tar.gz') |
| 124 | +
|
| 125 | +dataset_dir = "MedNIST/Hand" |
| 126 | +datalist = list(sorted(glob.glob(f"{dataset_dir}/*.jpeg"))) |
| 127 | +paired_dataset = [{"f_img": item, "m_img": item} for item in datalist] |
| 128 | +
|
| 129 | +preprocessing = mt.Compose( |
| 130 | + [ |
| 131 | + mt.LoadImaged(keys=("f_img", "m_img"), image_only=True), |
| 132 | + mt.EnsureChannelFirstd(keys=("f_img", "m_img")), |
| 133 | + mt.ScaleIntensityRanged( |
| 134 | + keys=("f_img", "m_img"), a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0 |
| 135 | + ), |
| 136 | + mt.RandRotated( |
| 137 | + keys=["m_img"], range_x=np.pi / 4, prob=1.0, mode="bicubic", keep_size=True |
| 138 | + ), |
| 139 | + mt.RandZoomd(keys=["m_img"], min_zoom=0.9, max_zoom=1.1, prob=1.0, mode="bicubic"), |
| 140 | + ] |
| 141 | +) |
| 142 | +
|
| 143 | +first_pair = preprocessing(paired_dataset[0]) |
| 144 | +print("displaying images:") |
| 145 | +plt.subplot(1, 2, 1) |
| 146 | +plt.imshow(first_pair["f_img"][0], cmap="gray") |
| 147 | +plt.subplot(1, 2, 2) |
| 148 | +plt.imshow(first_pair["m_img"][0], cmap="gray") |
| 149 | +plt.show() |
| 150 | +
|
| 151 | +``` |
| 152 | + |
| 153 | +## Topics not covered but possible in the config |
| 154 | + |
| 155 | +- Running customized Python components (made available on the `PYTHONPATH`, more examples [in the model_zoo](https://github.com/Project-MONAI/model-zoo)). |
| 156 | +- Overriding the component in `example.yaml` using, for example, `--id=new_value` in the command line. |
| 157 | +- Multiple configuration files and cross-file references. |
| 158 | +- Replacing in terms of plain texts instead of Python objects ([tutorial](https://github.com/Project-MONAI/tutorials/blob/main/bundle/get_started.ipynb)). |
| 159 | +- The debugging mode to investigate the intermediate variables and results. |
0 commit comments