How to convert RGB to greyscale using ImageDataLoaders.from_df

I’m trying to run some pre-trained pytorch models in a x-ray dataset and it is my first time using FastAI. Currently my issue is that the architecture I’m using expects a single-channel image but my pipeline at the moment isn’t capable of converting it.

from sklearn.model_selection import ShuffleSplit 

X = df_train
y = df_train['Target']
sss = ShuffleSplit(n_splits=1, test_size=.2, random_state=42)
train_idx, val_idx = next(sss.split(X, y))
df_train['is_valid'] = False
df_train.loc[val_idx, 'is_valid'] = True


#https://docs.fast.ai/vision.data.html#ImageDataLoaders
dls = ImageDataLoaders.from_df(df_train, 
                               fn_col = 'image_path',
                               label_col = 'Target',
                               label_delim = ' ',
                               valid_col='is_valid',
                               path = INPUT_PATH,
                               item_tfms= Resize(224)#, 
                               #batch_tfms= aug_tfm
                              )

dls.show_batch()

From what I’ve seen on tutorials, a good way to do it would be to wrap a conversion transform and pass it to batch_tfms or item_tfms but I wasn’t successful at that. Here is my latest sorry attempt.

def aug_tfm(img):
    arr = np.array(img)
    
    return PILImageBW.create(img)

When I try that, I get

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
/tmp/ipykernel_34/358471526.py in <module>
     23                                path = INPUT_PATH,
     24                                item_tfms= Resize(224),
---> 25                                batch_tfms= aug_tfm
     26                               )
     27 

/opt/conda/lib/python3.7/site-packages/fastai/vision/data.py in from_df(cls, df, path, valid_pct, seed, fn_col, folder, suff, label_col, label_delim, y_block, valid_col, item_tfms, batch_tfms, **kwargs)
    148                            item_tfms=item_tfms,
    149                            batch_tfms=batch_tfms)
--> 150         return cls.from_dblock(dblock, df, path=path, **kwargs)
    151 
    152     @classmethod

/opt/conda/lib/python3.7/site-packages/fastai/data/core.py in from_dblock(cls, dblock, source, path, bs, val_bs, shuffle, device, **kwargs)
    193     @classmethod
    194     def from_dblock(cls, dblock, source, path='.',  bs=64, val_bs=None, shuffle=True, device=None, **kwargs):
--> 195         return dblock.dataloaders(source, path=path, bs=bs, val_bs=val_bs, shuffle=shuffle, device=device, **kwargs)
    196 
    197     _docs=dict(__getitem__="Retrieve `DataLoader` at `i` (`0` is training, `1` is validation)",

/opt/conda/lib/python3.7/site-packages/fastai/data/block.py in dataloaders(self, source, path, verbose, **kwargs)
    113         dsets = self.datasets(source, verbose=verbose)
    114         kwargs = {**self.dls_kwargs, **kwargs, 'verbose': verbose}
--> 115         return dsets.dataloaders(path=path, after_item=self.item_tfms, after_batch=self.batch_tfms, **kwargs)
    116 
    117     _docs = dict(new="Create a new `DataBlock` with other `item_tfms` and `batch_tfms`",

/opt/conda/lib/python3.7/site-packages/fastai/data/core.py in dataloaders(self, bs, shuffle_train, shuffle, val_shuffle, n, path, dl_type, dl_kwargs, device, drop_last, val_bs, **kwargs)
    235         def_kwargs = {'bs':bs if val_bs is None else val_bs,'shuffle':val_shuffle,'n':None,'drop_last':False}
    236         dls = [dl] + [dl.new(self.subset(i), **merge(kwargs,def_kwargs,val_kwargs,dl_kwargs[i]))
--> 237                       for i in range(1, self.n_subsets)]
    238         return self._dbunch_type(*dls, path=path, device=device)
    239 

/opt/conda/lib/python3.7/site-packages/fastai/data/core.py in <listcomp>(.0)
    235         def_kwargs = {'bs':bs if val_bs is None else val_bs,'shuffle':val_shuffle,'n':None,'drop_last':False}
    236         dls = [dl] + [dl.new(self.subset(i), **merge(kwargs,def_kwargs,val_kwargs,dl_kwargs[i]))
--> 237                       for i in range(1, self.n_subsets)]
    238         return self._dbunch_type(*dls, path=path, device=device)
    239 

/opt/conda/lib/python3.7/site-packages/fastai/data/core.py in new(self, dataset, cls, **kwargs)
     64         if not hasattr(self, '_n_inp') or not hasattr(self, '_types'):
     65             try:
---> 66                 self._one_pass()
     67                 res._n_inp,res._types = self._n_inp,self._types
     68             except Exception as e:

/opt/conda/lib/python3.7/site-packages/fastai/data/core.py in _one_pass(self)
     51         b = self.do_batch([self.do_item(None)])
     52         if self.device is not None: b = to_device(b, self.device)
---> 53         its = self.after_batch(b)
     54         self._n_inp = 1 if not isinstance(its, (list,tuple)) or len(its)==1 else len(its)-1
     55         self._types = explode_types(its)

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in __call__(self, o)
    198         self.fs = self.fs.sorted(key='order')
    199 
--> 200     def __call__(self, o): return compose_tfms(o, tfms=self.fs, split_idx=self.split_idx)
    201     def __repr__(self): return f"Pipeline: {' -> '.join([f.name for f in self.fs if f.name != 'noop'])}"
    202     def __getitem__(self,i): return self.fs[i]

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in compose_tfms(x, tfms, is_enc, reverse, **kwargs)
    148     for f in tfms:
    149         if not is_enc: f = f.decode
--> 150         x = f(x, **kwargs)
    151     return x
    152 

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in __call__(self, x, **kwargs)
     71     @property
     72     def name(self): return getattr(self, '_name', _get_name(self))
---> 73     def __call__(self, x, **kwargs): return self._call('encodes', x, **kwargs)
     74     def decode  (self, x, **kwargs): return self._call('decodes', x, **kwargs)
     75     def __repr__(self): return f'{self.name}:\nencodes: {self.encodes}decodes: {self.decodes}'

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in _call(self, fn, x, split_idx, **kwargs)
     81     def _call(self, fn, x, split_idx=None, **kwargs):
     82         if split_idx!=self.split_idx and self.split_idx is not None: return x
---> 83         return self._do_call(getattr(self, fn), x, **kwargs)
     84 
     85     def _do_call(self, f, x, **kwargs):

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in _do_call(self, f, x, **kwargs)
     88             ret = f.returns(x) if hasattr(f,'returns') else None
     89             return retain_type(f(x, **kwargs), x, ret)
---> 90         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     91         return retain_type(res, x)
     92 

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in <genexpr>(.0)
     88             ret = f.returns(x) if hasattr(f,'returns') else None
     89             return retain_type(f(x, **kwargs), x, ret)
---> 90         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     91         return retain_type(res, x)
     92 

/opt/conda/lib/python3.7/site-packages/fastcore/transform.py in _do_call(self, f, x, **kwargs)
     87             if f is None: return x
     88             ret = f.returns(x) if hasattr(f,'returns') else None
---> 89             return retain_type(f(x, **kwargs), x, ret)
     90         res = tuple(self._do_call(f, x_, **kwargs) for x_ in x)
     91         return retain_type(res, x)

/opt/conda/lib/python3.7/site-packages/fastcore/dispatch.py in __call__(self, *args, **kwargs)
    121         elif self.inst is not None: f = MethodType(f, self.inst)
    122         elif self.owner is not None: f = MethodType(f, self.owner)
--> 123         return f(*args, **kwargs)
    124 
    125     def __get__(self, inst, owner):

/tmp/ipykernel_34/358471526.py in aug_tfm(img)
     13     arr = np.array(img)
     14 
---> 15     return PILImageBW.create(img)
     16 
     17 #https://docs.fast.ai/vision.data.html#ImageDataLoaders

/opt/conda/lib/python3.7/site-packages/fastai/vision/core.py in create(cls, fn, **kwargs)
    103     def create(cls, fn:(Path,str,Tensor,ndarray,bytes), **kwargs)->None:
    104         "Open an `Image` from path `fn`"
--> 105         if isinstance(fn,TensorImage): fn = fn.permute(1,2,0).type(torch.uint8)
    106         if isinstance(fn, TensorMask): fn = fn.type(torch.uint8)
    107         if isinstance(fn,Tensor): fn = fn.numpy()

/opt/conda/lib/python3.7/site-packages/fastai/torch_core.py in __torch_function__(self, func, types, args, kwargs)
    339         convert=False
    340         if _torch_handled(args, self._opt, func): convert,types = type(self),(torch.Tensor,)
--> 341         res = super().__torch_function__(func, types, args=args, kwargs=kwargs)
    342         if convert: res = convert(res)
    343         if isinstance(res, TensorBase): res.set_meta(self, as_copy=True)

/opt/conda/lib/python3.7/site-packages/torch/_tensor.py in __torch_function__(cls, func, types, args, kwargs)
   1021 
   1022         with _C.DisableTorchFunction():
-> 1023             ret = func(*args, **kwargs)
   1024             return _convert(ret, cls)
   1025 

RuntimeError: number of dims don't match in permute

I know this is somewhat a beginner question but I’ve been at it for a couple of days and no one had exactly this question around here, so I’m posting it. I’d appreciate any suggestions.

Perhaps look at the Contrast() data augmentation?

1 Like