*Please Help: Data Loader for Image and Mask*

Hi everyone,
I am currently developing a meta learning for semantic segmentation using MAML approach and my dataset comprises of an image and its mask with tif format. My file path is ,/dataset> Train, Test and Validate and each has a sub-folder of image_folder and mask_folder. Am writing a custom dataset to random crop the image with size of 256x256 and number of crops for each image should be 5. I was able to do the random crop class but was trying to check what i did was correct but getting this error: TypeError: Unexpected type <class ā€˜strā€™>

My code:
import os
import numpy as np
import torch
import torchvision.transforms as TF

from torch.utils import data
from skimage import io
from skimage import transform
from skimage import util

Constants.

num_classes = 4

Class that reads a sequence of image paths from a directory and creates a data.Dataset with them.

class ListDataset(data.Dataset):

def __init__(self, dataset, mode, normalization='minmax', hidden_classes=None):

    # Initializing variables.
    self.root = './' + dataset + '/'
    self.mode = mode
    self.normalization = normalization
    self.hidden_classes = hidden_classes
    
    
    if self.hidden_classes is not None:
        self.n_classes = num_classes - len(hidden_classes)
    else:
        self.n_classes = num_classes

    # Creating list of paths.
    self.imgs = self.make_dataset()

    # Check for consistency in list.
    if len(self.imgs) == 0:

        raise (RuntimeError('Found 0 images, please check the data set'))

def make_dataset(self):

    # Making sure the mode is correct.
    assert self.mode in ['Train', 'Test', 'Validate']

    # Setting string for the mode.
    img_dir = os.path.join(self.root, self.mode, 'image_folder')
    msk_dir = os.path.join(self.root, self.mode, 'mask_folder')
    
    if self.mode == 'Validate':
        img_dir = os.path.join(self.root, 'Train', 'image_folder')
        msk_dir = os.path.join(self.root, 'Train', 'mask_folder')
        
        
        
    data_list = sorted([f for f in os.listdir(msk_dir) if os.path.isfile(os.path.join(msk_dir, f))])
        
    #print(data_list)
    # Creating list containing image and ground truth paths.
    items = []
    for it in data_list:
        item = (os.path.join(img_dir, it.replace('Mask.tif', '.tif')), os.path.join(msk_dir, it))
        items.append(item)
        
        #print(item)
       # print(img_dir, it, it.replace('_noBoundary.tif', '.tif'), os.path.join(msk_dir, it))
    # Returning list.
    return items


def transform(self, image, mask):
    

    # Random crop
    i, j, h, w = TF.RandomCrop.get_params(
        image, output_size=(256, 256))
    image = TF.crop(image, i, j, h, w)
    mask = TF.crop(mask, i, j, h, w)

    # Random horizontal flipping
    if random.random() > 0.5:
        image = TF.hflip(image)
        mask = TF.hflip(mask)

    # Random vertical flipping
    if random.random() > 0.5:
        image = TF.vflip(image)
        mask = TF.vflip(mask)

    # Transform to tensor
    image = TF.to_tensor(image)
    mask = TF.to_tensor(mask)
    
    return image, mask

def __getitem__(self, index):
    # Reading items from list.
    img_path, msk_path = self.imgs[index]
    
    # Reading images.
    image = io.imread(img_path)
    mask = io.imread(msk_path)
    
    x, y = self.transform(image, mask)
    return x, y

def __len__(self):

    return len(self.imgs)

Please any Help?

1 Like

Which line of code raises this error?
Could you post the complete stack trace?

Since i was trying to define the transform function and it was not working for me i decided to go with this: num_classes = 4

Class that reads a sequence of image paths from a directory and creates a data.Dataset with them.

class ListDataset(data.Dataset):

def __init__(self, dataset, mode, transforms=None, normalization='minmax', hidden_classes=None):

    # Initializing variables.
    self.root = './' + dataset + '/'
    self.mode = mode
    self.normalization = normalization
    self.hidden_classes = hidden_classes
    
    
    if self.hidden_classes is not None:
        self.n_classes = num_classes - len(hidden_classes)
    else:
        self.n_classes = num_classes

    # Creating list of paths.
    self.imgs = self.make_dataset()

    # Check for consistency in list.
    if len(self.imgs) == 0:

        raise (RuntimeError('Found 0 images, please check the data set'))

def make_dataset(self):

    # Making sure the mode is correct.
    assert self.mode in ['Train', 'Test', 'Validate']

    # Setting string for the mode.
    img_dir = os.path.join(self.root, self.mode, 'image_folder')
    msk_dir = os.path.join(self.root, self.mode, 'mask_folder')
    
    if self.mode == 'Validate':
        img_dir = os.path.join(self.root, 'Train', 'image_folder')
        msk_dir = os.path.join(self.root, 'Train', 'mask_folder')
        
        
        
    data_list = sorted([f for f in os.listdir(msk_dir) if os.path.isfile(os.path.join(msk_dir, f))])
        
    #print(data_list)
    # Creating list containing image and ground truth paths.
    items = []
    for it in data_list:
        item = (os.path.join(img_dir, it.replace('Mask.tif', '.tif')), os.path.join(msk_dir, it))
        items.append(item)
        
        #print(item)
       # print(img_dir, it, it.replace('_noBoundary.tif', '.tif'), os.path.join(msk_dir, it))
    # Returning list.
    return items


def __getitem__(self, index):
    # Reading items from list.
    img_path, msk_path = self.imgs[index]
    
    # Reading images.
    image = io.imread(img_path)
    mask = io.imread(msk_path)
    
    # Turning to tensors.
    image = torch.from_numpy(image)
    mask = torch.from_numpy(mask)
    
    return image, mask

def __len__(self):

    return len(self.imgs)

And my transform for dataloader goes like this:
if name == ā€˜mainā€™:
train_transform = transforms.Compose([
transforms.ToPILImage(),
transforms.FiveCrop(256),
transforms.RandomHorizontalFlip(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
transforms.ToTensor()

])
test_transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.ToTensor()
])

train_dataset = ListDataset('./dataset', 'Train', transforms=train_transform)
train_loader = DataLoader(dataset=train_dataset, batch_size=2, shuffle=True, sampler=None,
           batch_sampler=None, num_workers=0, collate_fn=None,
           pin_memory=False, drop_last=False, timeout=0,
           worker_init_fn=None)


dataiter = iter(train_loader)
data = dataiter.next()
features, labels = data
print(features, labels)

but i get this error below:
RuntimeError: stack expects each tensor to be equal size, but got [11361, 7665, 3] at entry 0 and [13488, 14017, 3] at entry 1

Basically my images are of different dimensions but after the FiveCrops, i dont know if it is that it was not appleid to the train_loader

You are not using any transformations in Dataset.__getitem__ at the moment, so image and mask will be the loaded images transformed to tensors.
It seems they have a different shape, which would yield this error.

To apply the transformations, you would have to assign transforms in __init__ via:

self.transforms = transforms

and apply it in __getitem__:

if self.transforms:
    image = self.transforms(image)

Note that the torchvisiontransformswork onPIL.Imagesby default, so you might need to switch toPIL` to load the images.

Thanks for your reply and i edited like u suggested but got this error : TypeError: pic should be Tensor or ndarray. Got <class ā€˜PIL.TiffImagePlugin.TiffImageFileā€™>.

import os
import numpy as np
import torch
import torchvision.transforms as transforms
from PIL import Image
Image.MAX_IMAGE_PIXELS = None

from torch.utils import data
from skimage import io
from skimage import transform
from skimage import util

Constants.

num_classes = 4

Class that reads a sequence of image paths from a directory and creates a data.Dataset with them.

class ListDataset(data.Dataset):

def __init__(self, dataset, mode, normalization='minmax', hidden_classes=None):

    # Initializing variables.
    self.root = './' + dataset + '/'
    self.mode = mode
    self.normalization = normalization
    self.hidden_classes = hidden_classes
    self.transforms = transforms
    
    
    if mode == 'Train':
        self.transforms = transforms.Compose([
                                            transforms.ToPILImage(),
                                            transforms.FiveCrop(256),
                                            transforms.RandomHorizontalFlip(),
                                            transforms.ToTensor(),
                                            transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
                                            
    
                                            ])
    else:
        self.transforms = transforms.Compose([
                                            transforms.ToPILImage(),
                                            transforms.ToTensor(),
                                            transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
                                            ])
        
    if self.hidden_classes is not None:
        self.n_classes = num_classes - len(hidden_classes)
    else:
        self.n_classes = num_classes

    # Creating list of paths.
    self.imgs = self.make_dataset()

    # Check for consistency in list.
    if len(self.imgs) == 0:

        raise (RuntimeError('Found 0 images, please check the data set'))

def make_dataset(self):

    # Making sure the mode is correct.
    assert self.mode in ['Train', 'Test', 'Validate']

    # Setting string for the mode.
    img_dir = os.path.join(self.root, self.mode, 'image_folder')
    msk_dir = os.path.join(self.root, self.mode, 'mask_folder')
    
    if self.mode == 'Validate':
        img_dir = os.path.join(self.root, 'Train', 'image_folder')
        msk_dir = os.path.join(self.root, 'Train', 'mask_folder')
        
        
        
    data_list = sorted([f for f in os.listdir(msk_dir) if os.path.isfile(os.path.join(msk_dir, f))])
        
    #print(data_list)
    # Creating list containing image and ground truth paths.
    items = []
    for it in data_list:
        item = (os.path.join(img_dir, it.replace('Mask.tif', '.tif')), os.path.join(msk_dir, it))
        items.append(item)
        
        #print(item)
       # print(img_dir, it, it.replace('_noBoundary.tif', '.tif'), os.path.join(msk_dir, it))
    # Returning list.
    return items


def __getitem__(self, index):
    # Reading items from list.
    img_path, msk_path = self.imgs[index]
    
    # Reading images.

    image = Image.open(img_path)
    mask = Image.open(msk_path)
    
    if self.transforms:
        image = self.transforms(image)
        mask  = self.transforms(mask)
    
    # Turning to tensors.
    image = torch.tensor(image)
    mask = torch.tensor(mask)
    
    
    print(image, mask)
    return image, mask

def __len__(self):

    return len(self.imgs)

The transforms.ToPILImage() transformations wouldnā€™t be needed, if you are already passing a PIL.Image to the transformations.
Try to remove it and rerun the script.

This was what i got i think its not so easy dealing with tif images
TypeError: pic should be PIL Image or ndarray. Got <class ā€˜tupleā€™>

I donā€™t think this error is related to the TIF image, but assume that you are trying to pass the image and mask to the transformation together as a tuple.

Ok so what do u think will be best solution because at this point i dnt think i have a clue on what to do next

Try to pass only the image to the transformation and check if it works.
If you would need to transform the image and the mask together (using the same ā€œrandomā€ transformations), I would recommend to use the functional API as described here.

Thanks alot the Custom data worked but i i had to pre-process the image manually using a loop to crop the required images in the range of 50 then i applied the transform and my custom dataset is as follows:

num_classes = 2

Class that reads a sequence of image paths from a directory and creates a data.Dataset with them.

class ListDataset(data.Dataset):

def __init__(self, dataset, mode, normalization='minmax', hidden_classes=None):

    # Initializing variables.
    
    self.root = '/content/drive/My Drive/dataset'
    
    #self.root = './' + dataset + '/'
    self.mode = mode
    self.normalization = normalization
    self.hidden_classes = hidden_classes
    self.transforms = transforms
    
    if self.mode == 'Train':
        self.transforms = transforms.Compose([
                                            #transforms.ToPILImage(mode=None),
                                            #transforms.FiveCrop(256),
                                            #lambda x: Image.open(x).convert('RGB'),
                                            transforms.ToTensor(),
                                            #transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) 
                                             #                                            for  crop in crops])),
                                            transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
                                            
    
                                            ])
    else:
        self.transforms = transforms.Compose([
                                           # transforms.ToPILImage(mode=None),
                                            #lambda x: Image.open(x).convert('RGB'),
                                            transforms.ToTensor(),
                                           transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
                                           ])
    
    
    
        
    
    # Creating list of paths.
    self.imgs = self.make_dataset()

    # Check for consistency in list.
    if len(self.imgs) == 0:

        raise (RuntimeError('Found 0 images, please check the data set'))

def make_dataset(self):

    # Making sure the mode is correct.
    assert self.mode in ['Train', 'Test', 'Validate']

    # Setting string for the mode.
    img_dir = os.path.join(self.root, self.mode, 'image_folder')
    msk_dir = os.path.join(self.root, self.mode, 'mask_folder')
    
    if self.mode == 'Validate':
        img_dir = os.path.join(self.root, 'Train', 'image_folder')
        msk_dir = os.path.join(self.root, 'Train', 'mask_folder')
        
        
        
    data_list = sorted([f for f in os.listdir(msk_dir) if os.path.isfile(os.path.join(msk_dir, f))])
        
    #print(data_list)
    # Creating list containing image and ground truth paths.
    items = []
    for it in data_list:
        item = (os.path.join(img_dir, it.replace('Mask.tif', '.tif')), os.path.join(msk_dir, it))
        items.append(item)
        
        #print(item)
       # print(img_dir, it, it.replace('_noBoundary.tif', '.tif'), os.path.join(msk_dir, it))
    # Returning list.
    return items


def __getitem__(self, index):
    # Reading items from list.
    img_path, msk_path = self.imgs[index]
    
    # Reading images.
    
    image = io.imread(img_path)
    mask =  io.imread(msk_path)
    
    image_array = np.asarray(image)
    mask_array  = np.asarray(mask)
     
    
    
    
    
    #image = self.transforms(image)
    #mask  = self.transforms(mask)
    image_array = self.transforms(image_array)
    mask_array  = self.transforms(mask_array)
    
    # Turning to tensors.
    #image_array = torch.from_numpy(image_array)
    #mask_array = torch.from_numpy(mask_array)
     
    return image_array, mask_array

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