{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Usable & complete data loading utility\n\nThis kernel is an alternative to my own previous kernel : https://www.kaggle.com/guillaumedesforges/loading-the-cropped-dogs-seamlessly-with-pytorch\n\nThere were many images with more than one dog, by leveraging the annotation added to the competition we can get around 1500 more dogs (credits to @ddrbcn).\n\nPlus, this kernel loads the images to the RAM, giving a huge boost in performance !"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# required imports\nimport os\nimport xml.etree.ElementTree as ET\nimport torchvision\nfrom tqdm import tqdm_notebook as tqdm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class FullCroppedDogsFolderDataset(torchvision.datasets.vision.VisionDataset):\n    def __init__(self, root, transform=None, target_transform=None):\n        super().__init__(root, transform=transform, target_transform=target_transform)\n        self.transform = transform\n        self.target_transform = target_transform\n        \n        self.samples = self._load_subfolders_images(self.root)\n        if len(self.samples) == 0:\n            raise RuntimeError(\"Found 0 files in subfolders of: {}\".format(self.root))\n            \n    def _load_subfolders_images(self, root):\n        IMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp')\n\n        def is_valid_file(x):\n            return torchvision.datasets.folder.has_file_allowed_extension(x, IMG_EXTENSIONS)\n\n        required_transforms = torchvision.transforms.Compose([\n            torchvision.transforms.Resize(64),\n            torchvision.transforms.CenterCrop(64),\n        ])\n\n        imgs = []\n\n        paths = []\n        for root, _, fnames in sorted(os.walk(root)):\n            for fname in sorted(fnames):\n                path = os.path.join(root, fname)\n                paths.append(path)\n\n        pbar = tqdm(paths, desc='Loading cropped images')\n\n        for path in pbar:\n            if is_valid_file(path):\n                # Load image\n                img = torchvision.datasets.folder.default_loader(path)\n\n                # Get bounding boxes\n                annotation_basename = os.path.splitext(os.path.basename(path))[0]\n                annotation_dirname = next(dirname for dirname in os.listdir('../input/annotation/Annotation/') if dirname.startswith(annotation_basename.split('_')[0]))\n                annotation_filename = os.path.join('../input/annotation/Annotation', annotation_dirname, annotation_basename)\n                tree = ET.parse(annotation_filename)\n                root = tree.getroot()\n                objects = root.findall('object')\n                for o in objects:\n                    bndbox = o.find('bndbox')\n                    xmin = int(bndbox.find('xmin').text)\n                    ymin = int(bndbox.find('ymin').text)\n                    xmax = int(bndbox.find('xmax').text)\n                    ymax = int(bndbox.find('ymax').text)\n\n                    bbox = (xmin, ymin, xmax, ymax)\n\n                    object_img = required_transforms(img.crop(bbox))\n                    imgs.append(object_img)\n                \n                pbar.set_postfix_str(\"{} cropped images loaded\".format(len(imgs)))\n\n        return imgs\n    \n    def __getitem__(self, index):\n        sample = self.samples[index]\n        target = 1\n        \n        if self.transform is not None:\n            sample = self.transform(sample)\n        if self.target_transform is not None:\n            target = self.target_transform(target)\n\n        return sample, target\n\n    def __len__(self):\n        return len(self.samples)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = FullCroppedDogsFolderDataset(\n    '../input/all-dogs/',\n    transform=torchvision.transforms.Compose([\n        torchvision.transforms.RandomHorizontalFlip(),\n        torchvision.transforms.ToTensor(),\n    ])\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(dataset)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Benchmark against previous loader\n# https://www.kaggle.com/guillaumedesforges/loading-the-cropped-dogs-seamlessly-with-pytorch\n# I had 5.43 ms (against 4.6 ms for vanilla ImageFolder)\n%%timeit\ndataset[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Memory usage is ~= 2GB."},{"metadata":{},"cell_type":"markdown","source":"I hope you'll find this kernel helpful ! I tried to come up with something flexible enough but give a boost in performance.\n\nLet me know if you can think of any improvements. :-)\n\nIf you'll use this loader, a +1 is always welcome ! ;-)\n\n\nCheers,\n\nGuillaume"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}