Custom Dataset class for medical image patches

AmayaGS: <p>Hi,</p>
<p>I’m trying to define a custom Dataset for my medical images. For each patient, I have a histopathology Whole Slide Images (WSI). I preprocess each image, obtaining a variable number of 224 x 224 patches and keep them in a folder with the patient ID. The label for each patch in a given patient folder is the label assigned to the WSI. The data directory structure is as follows:</p>
<p><img src="https://discuss.pytorch.org/uploads/default/original/3X/b/b/bbf9cd420a150e8f3a4eb3b459c93a29307b32b0.png" alt="image" data-base62-sha1="qOUu3IzRGarRCS6KU7V2S3YabAY" width="446" height="279"></p>
<p>I want to write a custom Dataset that will load each patch, with the correct label and feed it into DataLoader, in this case treating each patch as independent. My current implementation return a list of images and list of labels, as so:</p>
<blockquote>
<p>class histoDataset(Dataset):</p>
<pre><code>def __init__(self, main_dir, csv_file, transform):

    self.main_dir = main_dir 
    self.transform = transform 
    self.CSV = pd.read_csv(csv_file) 
    self.folder_id_code = self.CSV['Patient image ID'].tolist() 
    self.image_diagnosis = self.CSV['Classification.based.on.CD20'].tolist()
    self.classes = list(dict.fromkeys(self.image_diagnosis))
    self.label = self.CSV['Classification.based.on.CD20'].apply(self.classes.index)
    self.label_dict = dict(zip(self.label, self.classes))
    self.folders = [f.path for f in os.scandir(self.main_dir)]

def __len__(self):
     return len(self.folders)

def __getitem__(self, idx): 
    image_list = []
    image_labels = []
    self.folder_loc = os.path.join(self.main_dir, self.folder_id_code[idx])
    self.folder_files = [f.path for f in os.scandir(self.folder_loc)]
    for image in self.folder_files:
        image = Image.open(image) 
        image_tensor = self.transform(image) 
        image_label = self.label[idx] 
        image_list.append(image_tensor)
        image_labels.append(image_label)
        
    return image_list, image_labels
</code></pre>
</blockquote>
<p>After inputting the resulting histoDataset into DataLoder and running this:</p>
<blockquote>
<p>sample_batch = next(iter(df_loader))<br>
sample_images, sample_labels = sample_batch<br>
print_images = sample_images[0:10]<br>
print_labels = sample_labels[0:10]</p>
</blockquote>
<p>to check the images and labels, I get the following error:</p>
<pre><code class="lang-auto">Traceback (most recent call last):

  File "C:\Users\AmayaGS\Documents\PhD\Data\Dataset_loader.py", line 248, in &lt;module&gt;
    sample_batch = next(iter(df_loader))

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 521, in __next__
    data = self._next_data()

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 561, in _next_data
    data = self._dataset_fetcher.fetch(index)  # may raise StopIteration

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\_utils\fetch.py", line 47, in fetch
    return self.collate_fn(data)

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\_utils\collate.py", line 84, in default_collate
    return [default_collate(samples) for samples in transposed]

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\_utils\collate.py", line 84, in &lt;listcomp&gt;
    return [default_collate(samples) for samples in transposed]

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\_utils\collate.py", line 82, in default_collate
    raise RuntimeError('each element in list of batch should be of equal size')

RuntimeError: each element in list of batch should be of equal size
</code></pre>
<p>Could someone please point out how to correctly implement the histoDataset and why I am getting this error? Thanks!</p>

Dazitu616: <p>Maybe something is wrong in the <code>transform</code> of your dataset. Can you post it here?</p>

AmayaGS: <p>This is how I use my <code>transform</code></p>
<blockquote>
<p>import torchvision<br>
import torchvision.transforms as transforms</p>
</blockquote>
<blockquote>
<p>transforms = transforms.Compose([<br>
transforms.Resize((224, 224)),<br>
transforms.ToTensor(),<br>
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))<br>
])</p>
</blockquote>
<blockquote>
<p>df = histoDataset(main_dir, csv_file, transforms)</p>
</blockquote>
<blockquote>
<p>df_loader = torch.utils.data.DataLoader(df, batch_size=10, shuffle=True, num_workers=0, drop_last=False)</p>
</blockquote>

Dazitu616: <p>The transform looks good to me. The error message basically says that some of the loaded data are of different shapes. I think it should be <code>image_list</code> not <code>image_labels</code>.<br>
Is it possible that <code>self.folder_files</code> can have different number of items when loading with different <code>idx</code>? For example, sometimes a folder contains 2 images, while sometimes it contains 4. Then the loaded data shape will be [2, 3, 224, 224] and [4, 2, 224, 224], which causes the problem.</p>

aiGeekProgrammer: <p>I’m not sure if you structure this data loading correctly.<br>
If I understand this right, the number of patches may be different from patient to patient.<br>
Consequently, your <em>image_list, image_labels</em> variables that you return from <em><strong>getitem</strong></em> are of different length.<br>
That might be the reason why you get: <em>RuntimeError: each element in list of batch should be of equal size</em><br>
As I don’t have this data and the code running in my notebook, this is just a vague hypothesis.</p>
<p><strong>Did you consider <strong>getitem</strong> returning 1 patch only?</strong></p>