I’ve tried to address Kaggle “Human protein atlas” competition:
Where the input consist of 4 image per sample (RGB + Yellow) and the output is a multi class label, with a domain of 28 possible classes.
To address this competition I’ve to implement a couple of custom function to cope with “multi channel images” datasets.
1) Multi channel image loader
Inspired by fast.ai “open_image”, this function take as input a list of “Path to image” and transforms it in a single multi channel image.
The current version supports only single channel images (grayscale) as input but It’s not difficult to change the code to support list of images with multiple channels.
It supports any number of input images so you can load for example sections of MRI scan.
import PIL
def openMultiChannelImage(fpArr):
'''
Open multiple images and return a single multi channel image
'''
mat = None
nChannels = len(fpArr)
for i,fp in enumerate(fpArr):
#print('Loading: ', fp)
img = PIL.Image.open(fp)
chan = pil2tensor(img).float().div_(255)
if(mat is None):
mat = torch.zeros((nChannels,chan.shape[1],chan.shape[2]))
mat[i,:,:]=chan
return Image(mat)
# Usage sample
# v = (train_data_and_labels_df[train_df.columns]).values[0,:]
v = array(['00070df0-bbc3-11e8-b2bc-ac1f6b6435d0', # Object reference - not used here
Path('/home/ste/.fastai/data/human-protein-atlas/train/00070df0-bbc3-11e8-b2bc-ac1f6b6435d0_blue.png'),
Path('/home/ste/.fastai/data/human-protein-atlas/train/00070df0-bbc3-11e8-b2bc-ac1f6b6435d0_green.png'),
Path('/home/ste/.fastai/data/human-protein-atlas/train/00070df0-bbc3-11e8-b2bc-ac1f6b6435d0_red.png'),
Path('/home/ste/.fastai/data/human-protein-atlas/train/00070df0-bbc3-11e8-b2bc-ac1f6b6435d0_yellow.png')])
ret = openMultiChannelImage(v[1:])
display(ret)
print(ret.data.shape)
2) Multi channel dataset
It’s a subclass of “ImageMultiDataset” and takes as input “X” an array of Images paths list (where the first element is the sample “id” and the others are Paths) and as output “Y” an array of classes lists (classes are string).
It supports (X,Y) pairs for train and validation set, and “X” only samples for test dataset, so you can use it even for predictions.
class MultiChannelDataset(ImageMultiDataset):
'''
A dataset wuth multi channel support task.
x: is of type x[0]=id, x[1:]=channels
y: labels: matrix of shape #samples: each row is an array of string representing classes
'''
def __init__(self, x:Collection[Any], y:Collection[Collection[str]], classes:Optional[Collection[Any]]=None):
if((y is not None) and (len(y)>0)):
assert len(x)==len(y)
super().__init__(fns=None, labels=y, classes=classes) # No file names! It retrive X in different way
self.x=x # Assign x instead of using fns
#self.y # Using super class initializer
#self.loss_func # Using super class initializer
self.isTest = (y is None) or (len(y)==0)
def _get_x(self,i):
img=openMultiChannelImage(self.x[i,1:])
#print('loaded x:',self.x[i,0],img.data.shape) # WARNING: it's needed to slow down
var = img.data.shape # Probably there is a race ceondition...
return img # it's a pytorch tensor image...
def _get_y(self,i): # Override default behaviour to accomodate test set without y
if(self.isTest):
# https://github.com/fastai/fastai/blob/d09f82a4c994c65d6466248faf2ef856f7c553af/fastai/data_block.py#L179
return [0] # if no label passed, used label of first training item Encoded (as in add_test)
else:
return super()._get_y(i)
@classmethod
def create_test_dataset(cls, x:Collection[Any], classes:Collection[Any]):
return cls(x,[],classes)
This is the link to git hub public repo with the complete notebook.
I’ve tried to train:
- resnet 50: score of .135 and rank of 609/736
- resnet 34: score of .167 and rank of 594/736
There is a lot of room for improvement.
I hope this help you!