PyTorch语义分割自定义RandomCrop报错:'Image'对象无dim属性
解决AttributeError: 'Image' object has no attribute 'dim'问题
这个错误的根源很明确:你正在使用torch.nn.functional.pad(也就是代码里的F.pad)处理PIL Image对象,但这个函数是专门为PyTorch张量(Tensor)设计的——PIL Image并没有dim属性,所以调用时就触发了这个报错。
下面给你两种针对性的解决方案,推荐第一种更贴合你现有代码逻辑的:
方案1:改用torchvision.transforms.Pad处理PIL Image
torchvision.transforms.Pad是专门为PIL Image设计的填充工具,完全可以替代F.pad完成你的需求。修改你的__call__方法中填充图像和mask的部分:
def __call__(self, data): img, mask = data["image"], data["mask"] # 处理宽度不足的情况 if self.pad_if_needed and img.size[0] < self.size[1]: pad_left = (self.size[1] - img.size[0]) // 2 pad_right = self.size[1] - img.size[0] - pad_left pad_transform = transforms.Pad((pad_left, 0, pad_right, 0), fill=self.fill, padding_mode=self.padding_mode) img = pad_transform(img) mask = pad_transform(mask) # 处理高度不足的情况 if self.pad_if_needed and img.size[1] < self.size[0]: pad_top = (self.size[0] - img.size[1]) // 2 pad_bottom = self.size[0] - img.size[1] - pad_top pad_transform = transforms.Pad((0, pad_top, 0, pad_bottom), fill=self.fill, padding_mode=self.padding_mode) img = pad_transform(img) mask = pad_transform(mask) i, j, h, w = self.get_params(img, self.size) crop_image = transforms.functional.crop(img, i, j, h, w) crop_mask = transforms.functional.crop(mask, i, j, h, w) return {"image": crop_image, "mask": crop_mask}
关键改动说明:
- 用
transforms.Pad替代F.pad,它直接支持PIL Image输入 - 计算左右/上下的填充量(这里做了对称填充,如果你需要单边填充可以改回原逻辑)
- 对图像和mask使用完全相同的填充变换,确保二者空间位置完全对齐
方案2:先将PIL Image转为张量再处理
如果你更习惯用F.pad的API,可以先把PIL Image转为张量,处理完后再转回PIL Image:
def __call__(self, data): img, mask = data["image"], data["mask"] # 转为张量 img_tensor = transforms.ToTensor()(img) mask_tensor = transforms.ToTensor()(mask) # 处理宽度不足的情况 if self.pad_if_needed and img.size[0] < self.size[1]: img_tensor = F.pad(img_tensor, (self.size[1] - img.size[0], 0), value=self.fill, mode=self.padding_mode) mask_tensor = F.pad(mask_tensor, (self.size[1] - mask.size[0], 0), value=self.fill, mode=self.padding_mode) # 处理高度不足的情况 if self.pad_if_needed and img.size[1] < self.size[0]: img_tensor = F.pad(img_tensor, (0, self.size[0] - img.size[1]), value=self.fill, mode=self.padding_mode) mask_tensor = F.pad(mask_tensor, (0, self.size[0] - mask.size[1]), value=self.fill, mode=self.padding_mode) # 转回PIL Image img = transforms.ToPILImage()(img_tensor) mask = transforms.ToPILImage()(mask_tensor) i, j, h, w = self.get_params(img, self.size) crop_image = transforms.functional.crop(img, i, j, h, w) crop_mask = transforms.functional.crop(mask, i, j, h, w) return {"image": crop_image, "mask": crop_mask}
不过这个方案多了张量和PIL的转换步骤,效率略低,所以更推荐方案1。
另外,关于你提到的先按0.98-1.1比例缩放的需求,可以单独写一个自定义变换类,和这个RandomCrop一起用transforms.Compose组合起来,比如:
class RandomResize(object): def __call__(self, data): img, mask = data["image"], data["mask"] scale = random.uniform(0.98, 1.1) new_size = (int(img.size[0]*scale), int(img.size[1]*scale)) img = img.resize(new_size, Image.BILINEAR) mask = mask.resize(new_size, Image.NEAREST) # mask用最近邻插值避免标签失真 return {"image": img, "mask": mask} # 组合变换 transform = transforms.Compose([ RandomResize(), RandomCrop(size=(256, 256)) # 替换成你的目标尺寸 ])
这样就能保证图像和mask先按相同比例缩放,再做随机裁剪,完全满足你的需求。
内容的提问来源于stack exchange,提问作者Atahan Özer
相关产品推荐
相关产品推荐

