Hi,
i want to build a network that uses 6 images of one object (from different angles) to classify it. Therefore i am building my own ImageTouple Class, but when i try to use apply_tfms i get the following error:
AttributeError Traceback (most recent call last)
<ipython-input-48-825db6ffe917> in <module>
8 img_count = img_count + 5
9 touple = ImageTouple(img1, img2, img3, img4, img5, img6)
---> 10 touple.apply_tfms(tfms)
11
<ipython-input-45-8da4c2a0a986> in apply_tfms(self, tfms, **kwargs)
6
7 def apply_tfms(self, tfms, **kwargs):
----> 8 self.img1 = self.img1.apply_tfms(tfms, **kwargs)
9 self.img2 = self.img2.apply_tfms(tfms, **kwargs)
10 self.img3 = self.img3.apply_tfms(tfms, **kwargs)
~\.conda\envs\PyTorchFastAI\lib\site-packages\fastai\vision\image.py in apply_tfms(self, tfms, do_resolve, xtra, size, resize_method, mult, padding_mode, mode, remove_out)
104 resize_method = ifnone(resize_method, default_rsz)
105 if resize_method <= 2 and size is not None: tfms = self._maybe_add_crop_pad(tfms)
--> 106 tfms = sorted(tfms, key=lambda o: o.tfm.order)
107 if do_resolve: _resolve_tfms(tfms)
108 x = self.clone()
~\.conda\envs\PyTorchFastAI\lib\site-packages\fastai\vision\image.py in <lambda>(o)
104 resize_method = ifnone(resize_method, default_rsz)
105 if resize_method <= 2 and size is not None: tfms = self._maybe_add_crop_pad(tfms)
--> 106 tfms = sorted(tfms, key=lambda o: o.tfm.order)
107 if do_resolve: _resolve_tfms(tfms)
108 x = self.clone()
AttributeError: 'list' object has no attribute 'tfm'
My class so far:
class ImageTouple(ItemBase):
def __init__(self, img1, img2, img3, img4, img5, img6):
self.img1, self.img2, self.img3, self.img4, self.img5, self.img6 = img1, img2, img3, img4, img5, img6
self.obj = (img1, img2, img3, img4, img5, img6)
self.data = [-1+2*img1.data, -1+2*img2.data, -1+2*img3.data, -1+2*img4.data, -1+2*img5.data, -1+2*img6.data]
def apply_tfms(self, tfms, **kwargs):
self.img1 = self.img1.apply_tfms(tfms, **kwargs)
self.img2 = self.img2.apply_tfms(tfms, **kwargs)
self.img3 = self.img3.apply_tfms(tfms, **kwargs)
self.img4 = self.img4.apply_tfms(tfms, **kwargs)
self.img5 = self.img5.apply_tfms(tfms, **kwargs)
self.img6 = self.img6.apply_tfms(tfms, **kwargs)
self.data = [-1+2*self.img1.data, -1+2*self.img2.data, -1+2*self.img3.data, -1+2*self.img4.data, -1+2*self.img5.data, -1+2*self.img6.data]
return self
def to_one(self):
return Image(0.5+torch.cat(self.data, 6)/2)
i get the images with:
img1 = open_image(path/to/image)
and my tfms is:
tfms = get_transforms(max_warp=0, p_affine=0, xtra_tfms=[dihedral(), rand_zoom(1.2, 1.5)])
unfortunatly my python knowledge is not the best, so the only thing i can think of that somehow the image is not correctly used. But i tired to follow the “Custom Item List” as best as i can.
Any help would be appreciated!
EDIT:
I just continued with the tutorial and now i have a dataset (i think?) But i have a question
For the ImageList class i have
class ImageTupleList(ImageList):
def __init__(self, items, itemsB=None, **kwargs):
super().__init__(items,**kwargs)
self.itemsB = itemsB
self.copy_new.append('itemsB')
def get(self, i):
img1 = super().get(i)
img2 = super().get(i+1)
img3 = super().get(i+2)
img4 = super().get(i+3)
img5 = super().get(i+4)
img6 = super().get(i+5)
return ImageTouple(img1, img2, img3, img4, img5, img6)
@classmethod
def from_folders(cls, path, folderA, folderB, **kwargs):
itemsB = ImageList.from_folder(path/folderB).items
res = super().from_folder(path/folderA, itemsB=itemsB, *kwargs)
res.path = path
return res
def reconstruct(self, t:Tensor):
return ImageTuple(Image(t[0]/2+0.5), Image(t[1]/2+0.5), Image(t[2]/2+0.5), Image(t[3]/2+0.5), Image(t[4]/2+0.5), Image(t[5]/2+0.5))
def show_xys(self, xs, ys, figsize:Tuple[int,int]=(12,6), **kwargs):
"Show the `xs` and `ys` on a figure of `figsize`. `kwargs` are passed to the show method."
rows = int(math.sqrt(len(xs)))
fig, axs = plt.subplots(rows,rows,figsize=figsize)
for i, ax in enumerate(axs.flatten() if rows > 1 else [axs]):
xs[i].to_one().show(ax=ax, **kwargs)
plt.tight_layout()
def show_xyzs(self, xs, ys, zs, figsize:Tuple[int,int]=None, **kwargs):
"""Show `xs` (inputs), `ys` (targets) and `zs` (predictions) on a figure of `figsize`.
`kwargs` are passed to the show method."""
figsize = ifnone(figsize, (12,3*len(xs)))
fig,axs = plt.subplots(len(xs), 2, figsize=figsize)
fig.suptitle('Ground truth / Predictions', weight='bold', size=14)
for i,(x,z) in enumerate(zip(xs,zs)):
x.to_one().show(ax=axs[i,0], **kwargs)
z.to_one().show(ax=axs[i,1], **kwargs)
class TargetTupleList(ItemList):
def reconstruct(self, t:Tensor):
if len(t.size()) == 0: return t
return ImageTuple(Image(t[0]/2+0.5), Image(t[1]/2+0.5), Image(t[2]/2+0.5), Image(t[3]/2+0.5), Image(t[4]/2+0.5), Image(t[5]/2+0.5))
What would be the correct way to handle the 6 images in the “get” method? i think i would have to change the i beforehand as every 6th image starte a new data entry