Skip to content
This repository was archived by the owner on Mar 21, 2024. It is now read-only.

Commit ad9aac5

Browse files
authored
DOC: Improve documentation for dataset creation (#723)
1 parent 796594f commit ad9aac5

1 file changed

Lines changed: 54 additions & 40 deletions

File tree

docs/creating_dataset.md

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Dataset Creation
2+
23
This document describes the dataset formats used by InnerEye for segmentation and classification tasks. After creating
34
the dataset, upload it to AzureML blob storage (as described in the
45
[AzureML documentation](setting_up_aml.md#step-4-create-a-storage-account-for-your-datasets))
56

67
## Segmentation Datasets
8+
79
This section walks through the process of creating a dataset in the format expected by the InnerEye package.
810
However, if your dataset is in DICOM-RT format, you should instead use the
911
[InnerEye-CreateDataset](https://github.com/microsoft/InnerEye-CreateDataset) tool.
@@ -12,30 +14,33 @@ After creating the dataset, you can also [analyze](#analysing-segmentation-datas
1214
Segmentation datasets should have the input scans and ground truth segmentations in Nifti format.
1315

1416
InnerEye expects segmentation datasets to have the following structure:
15-
* Each subject has one or more scans, and one or more segmentation masks. There should be one segmentation mask for
17+
18+
* Each subject has one or more scans, and one or more segmentation masks. There should be one segmentation mask for
1619
each ground truth structure (anatomical structure that the model should segment)
17-
* For convenience, scans and ground truth masks for different subjects can live in separate folders, but that's not a must.
18-
* Inside the root folder for the dataset, there should be a file `dataset.csv`, containing the following fields
20+
* For convenience, scans and ground truth masks for different subjects can live in separate folders, but that's not a must.
21+
* Inside the root folder for the dataset, there should be a file `dataset.csv`, containing the following fields
1922
at minimum:
20-
* `subject`: A unique positive integer assigned to every patient
21-
* `channel`: The imaging channel or ground truth structure described by this row.
22-
* `filePath`: Path to the file for this scan or structure. We support nifti (nii, nii.gz), numpy (npy, npz) and hdf5(h5).
23-
* For HDF5 path suffix with | separator
24-
* For images <path>|<dataset_name>|<channel index>
25-
* For segmentation binary <path>|<dataset_name>|<channel index>
26-
* For segmentation multimap <path>|<dataset_name>|<channel index>|<multimap value>
27-
* Multimaps are encoded as 0=background and integers for each class.
28-
* The expected dimensions: (channel, Z, Y, X)
29-
* For numpy or nifti just the expected format is just the path to the files.
30-
* For images can be encoded as float32 with dimensions (X, Y, Z)
31-
* For segmentations should be encoded as binary masks with dimensions (X, Y, Z)
23+
* `subject`: A unique positive integer assigned to every patient
24+
* `channel`: The imaging channel or ground truth structure described by this row.
25+
* `filePath`: Path to the file for this scan or structure. We support nifti (`.nii`, `.nii.gz` extensions), numpy (`.npy`, `.npz`) and hdf5(`.h5`).
26+
* For HDF5 files, you need set the the actual file path, and specify the HDF5 dataset name and channel as follows with `|` as a separator:
27+
* For images: `<path>|<dataset_name>|<channel index>`
28+
* For segmentations that are provided as binary maps: `<path>|<dataset_name>|<channel index>`
29+
* For segmentations that are given as multimaps: `<path>|<dataset_name>|<channel index>|<multimap value>`
30+
* Multimaps are encoded as 0=background and integers for each class.
31+
* The expected dimensions: (channel, Z, Y, X)
32+
* For numpy or nifti just the expected format is just the path to the files.
33+
* Images must be encoded as float32 with dimensions (X, Y, Z)
34+
* Segmentations need to be encoded as binary masks in `uint8` format with dimensions (X, Y, Z). There must be one binary mask per
35+
ground truth structure. The arrays need to contain 1 for all voxels that belong to the structure, and 0 for all other voxels.
36+
You can save those to nifti by working with numpy `uint8` arrays.
3237

3338
Additional supported fields include `acquisition_date`, `institutionId`, `seriesID` and `tags` (meant for miscellaneous labels).
3439

3540
For example, for a CT dataset with two structures `heart` and `lung` to be segmented, the dataset folder
3641
could look like:
3742

38-
```
43+
```text
3944
dataset_folder_name
4045
├──dataset.csv
4146
├──subjectID1/
@@ -50,7 +55,8 @@ dataset_folder_name
5055
```
5156

5257
The `dataset.csv` for this dataset would look like:
53-
```
58+
59+
```text
5460
subject,filePath,channel
5561
1,subjectID1/ct.nii.gz,ct
5662
1,subjectID1/heart.nii.gz,structure1
@@ -59,11 +65,14 @@ subject,filePath,channel
5965
2,subjectID2/heart.nii.gz,structure1
6066
2,subjectID2/lung.nii.gz,structure2
6167
```
68+
6269
Note: The paths in the `dataset.csv` file should **not** be absolute paths, but relative to the folder that contains
6370
`dataset.csv'.
6471

6572
### Image size requirements
73+
6674
The images in a dataset must adhere to these constraints:
75+
6776
* All images, across all subjects, must have already undergone geometric normalization, i.e., all images must have
6877
approximately the same voxel size. For example, if all images for subject 1 have voxel size 1.5mm x 1.01mm x 1.01mm,
6978
and all images for subject 2 have voxel size 1.51mm x 0.99mm x 0.99mm, this should be fine. In particular, this
@@ -77,7 +86,6 @@ All these constraints are automatically checked and guaranteed if the raw data i
7786
the [InnerEye-CreateDataset](https://github.com/microsoft/InnerEye-CreateDataset) tool to convert them to Nifti
7887
format. Geometric normalization can also be turned on as a pre-processing step.
7988

80-
8189
### Uploading to Azure
8290

8391
When running in Azure, you need to upload the folder containing the dataset (i.e., the file `dataset.csv` and the
@@ -87,20 +95,21 @@ image referenced therein) to the storage account for datasets. This is the stora
8795
The best way of uploading the data is via
8896
[Azure Storage Explorer](https://azure.microsoft.com/en-gb/features/storage-explorer/). Please follow the installation
8997
instructions first.
90-
- Find your Azure subscription in the "Explorer" bar, and inside of that, the "Storage Accounts" field, and the
98+
99+
* Find your Azure subscription in the "Explorer" bar, and inside of that, the "Storage Accounts" field, and the
91100
storage account you created for datasets.
92-
- That storage account should have a section "Blob Containers". Check if there is a container called "datasets" already.
101+
* That storage account should have a section "Blob Containers". Check if there is a container called "datasets" already.
93102
If not, create one using the context menu.
94-
- Navigate into the "datasets" container.
95-
- Then use "Upload/Upload Folder" and choose the folder that contains your dataset (`dataset_folder_name` in the
103+
* Navigate into the "datasets" container.
104+
* Then use "Upload/Upload Folder" and choose the folder that contains your dataset (`dataset_folder_name` in the
96105
above example). Leave all other settings in the upload dialog at their default.
97-
- This will start the upload. Depending on the number of files, that can of course take some time.
98-
106+
* This will start the upload. Depending on the number of files, that can of course take some time.
99107

100108
### Creating a model configuration
101109

102110
For the above dataset structure for heart and lung segmentation, you would then create a model configuration that
103111
contains at least the following fields:
112+
104113
```python
105114
class HeartLungModel(SegmentationModelBase):
106115
def __init__(self) -> None:
@@ -131,12 +140,12 @@ class HeartLungModel(SegmentationModelBase):
131140
num_epochs=120,
132141
)
133142
```
143+
134144
The `local_dataset` field is required if you want to run the InnerEye toolbox on your own VM, and you want to consume
135145
the dataset from local storage. If you want to run the InnerEye toolbox inside of AzureML, you need to supply the
136146
`azure_dataset_id`, pointing to a folder in Azure blob storage. This folder should reside in the `datasets` container
137147
in the storage account that you designated for storing your datasets, see [the setup instructions](setting_up_aml.md).
138148

139-
140149
#### Analyzing segmentation datasets
141150

142151
Once you have created your Azure dataset, either by the process described here or with the CreateDataset tool,
@@ -145,29 +154,29 @@ with respect to a number of statistics, and which therefore may be erroneous or
145154
This can be done using the analyze command provided by
146155
[InnerEye-CreateDataset](https://github.com/microsoft/InnerEye-CreateDataset).
147156

148-
149157
## Classification Datasets
150158

151159
Classification datasets should have a `dataset.csv` and a folder containing the image files. The `dataset.csv` should
152160
have at least the following fields:
153-
* subject: The subject ID, a unique positive integer assigned to every image
154-
* path: Path to the image file for this subject
155-
* value:
156-
* For binary classification, a (binary) ground truth label. This can be "true" and "false" or "0" and "1".
157-
* For multi-label classification, the set of all positive labels for the image, separated by a `|` character.
161+
162+
* subject: The subject ID, a unique positive integer assigned to every image
163+
* path: Path to the image file for this subject
164+
* value:
165+
* For binary classification, a (binary) ground truth label. This can be "true" and "false" or "0" and "1".
166+
* For multi-label classification, the set of all positive labels for the image, separated by a `|` character.
158167
Ex: "0|2|4" for a sample with true labels 0, 2 and 4 and "" for a sample in which all labels are false.
159-
* For regression, a scalar value.
168+
* For regression, a scalar value.
160169

161170
These, and other fields which can be added to dataset.csv are described in the examples below.
162171

163172
For each entry (subject ID, label value, etc) needed to construct a single input sample, the entry value is read
164173
from the channels and columns specified for that entry.
165174

166-
#### A simple example
175+
### A simple example
167176

168177
Let's look at how to construct a `dataset.csv` (and changes we will need to make to the model config file in parallel):
169178

170-
```
179+
```text
171180
SubjectID, FilePath, Label
172181
1, images/image1.npy, True
173182
2, images/image2.npy, False
@@ -195,13 +204,14 @@ what columns in the csv contain the subject identifiers, channel names, image fi
195204
NOTE: If any of the `*_column` parameters are not specified, InnerEye will look for these entries under the default column names
196205
if default names exist. See the CSV headers in [csv_util.py](/InnerEye/ML/utils/csv_util.py) for all the defaults.
197206

198-
#### Using channels in dataset.csv
207+
### Using channels in dataset.csv
208+
199209
Channels are fields in `dataset.csv` which can be used to filter rows. They are typically used when there are multiple
200210
images or labels per subject (for example, if multiple images were taken across a period of time for each subject).
201211

202212
A slightly more complex `dataset.csv` would be the following:
203213

204-
```
214+
```text
205215
SubjectID, Channel, FilePath, Label
206216
1, image_feature_1, images/image_1_feature_1.npy,
207217
1, image_feature_2, images/image_1_feature_2.npy,
@@ -232,14 +242,15 @@ and `image_feature_2`) and the associated label (read from the row with `Channel
232242

233243
NOTE: There are no defaults for the `*_channels` parameters, so these must be set as parameters.
234244

235-
#### Recognized columns in dataset.csv and filtering based on channels
245+
### Recognized columns in dataset.csv and filtering based on channels
246+
236247
Other recognized fields, apart from subject, channel, file path and label are numerical features and categorical features.
237248
These are extra scalar and categorical values to be used as model input.
238249

239250
Any *unrecognized* columns (any column which is both not described in the model config and has no default)
240251
will be converted to a dict of key-value pairs and stored in an object of type `GeneralSampleMetadata` in the sample.
241252

242-
```
253+
```text
243254
SubjectID, Channel, FilePath, Label, Tag, weight, class
244255
1, image_time_1, images/image_1_time_1.npy, True, , ,
245256
1, image_time_2, images/image_1_time_2.npy, False, , ,
@@ -275,7 +286,8 @@ In this example, `weight` is a scalar feature read from the csv, and `class` is
275286
different times with different label values. By using `label_channels=["image_time_2"]`, we can use the label associated with
276287
the second image for all subjects.
277288

278-
#### Multi-label classification datasets
289+
### Multi-label classification datasets
290+
279291
Classification datasets can be multi-label, i.e. they can have more than one label associated with every sample.
280292
In this case, in the label column, separate the (numerical) ground truth labels with a pipe character (`|`) to
281293
provide multiple ground truth labels for the sample.
@@ -285,7 +297,7 @@ are not supported.
285297

286298
For example, the `dataset.csv` for a multi-label task with 4 classes (0, 1, 2, 3) would look like the following:
287299

288-
```
300+
```text
289301
SubjectID, Channel, FilePath, Label
290302
1, image_feature_1, images/image_1_feature_1.npy,
291303
1, image_feature_2, images/image_1_feature_2.npy,
@@ -300,11 +312,13 @@ SubjectID, Channel, FilePath, Label
300312
4, image_feature_2, images/image_4_feature_2.npy
301313
4, label, ,
302314
```
315+
303316
Note that the label field for sample 4 is left empty, this indicates that all labels are negative in Sample 4.
304317
In multi-label tasks, the negative class (all ground truth classes being false for a sample) should not be
305318
considered a separate class, and should be encoded by an empty label field.
306319

307320
The labels which are true for each sample in the `dataset.csv` shown above are:
321+
308322
* Sample 1: 0, 2, 3
309323
* Sample 2: 1, 2
310324
* Sample 3: 1

0 commit comments

Comments
 (0)