{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n!pip install efficientnet_pytorch torchtoolbox\n\nimport torch\nimport torchvision\nimport torch.nn.functional as F\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader, Subset\nimport torchtoolbox.transform as transforms\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom sklearn.metrics import accuracy_score, roc_auc_score\nfrom sklearn.model_selection import StratifiedKFold, GroupKFold\nimport pandas as pd\nimport numpy as np\nimport gc\nimport os\nimport cv2\nimport time\nimport datetime\nimport warnings\nimport random\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class AdvancedHairAugmentation:\n    \"\"\"\n    Impose an image of a hair to the target image\n\n    Args:\n        hairs (int): maximum number of hairs to impose\n        hairs_folder (str): path to the folder with hairs images\n    \"\"\"\n\n    def __init__(self, hairs: int = 5, hairs_folder: str = \"\"):\n        self.hairs = hairs\n        self.hairs_folder = hairs_folder\n\n    def __call__(self, img):\n        \"\"\"\n        Args:\n            img (PIL Image): Image to draw hairs on.\n\n        Returns:\n            PIL Image: Image with drawn hairs.\n        \"\"\"\n        n_hairs = random.randint(0, self.hairs)\n        \n        if not n_hairs:\n            return img\n        \n        height, width, _ = img.shape  # target image width and height\n        hair_images = [im for im in os.listdir(self.hairs_folder) if 'png' in im]\n        \n        for _ in range(n_hairs):\n            hair = cv2.imread(os.path.join(self.hairs_folder, random.choice(hair_images)))\n            hair = cv2.flip(hair, random.choice([-1, 0, 1]))\n            hair = cv2.rotate(hair, random.choice([0, 1, 2]))\n\n            h_height, h_width, _ = hair.shape  # hair image width and height\n            roi_ho = random.randint(0, img.shape[0] - hair.shape[0])\n            roi_wo = random.randint(0, img.shape[1] - hair.shape[1])\n            roi = img[roi_ho:roi_ho + h_height, roi_wo:roi_wo + h_width]\n\n            # Creating a mask and inverse mask\n            img2gray = cv2.cvtColor(hair, cv2.COLOR_BGR2GRAY)\n            ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)\n            mask_inv = cv2.bitwise_not(mask)\n\n            # Now black-out the area of hair in ROI\n            img_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)\n\n            # Take only region of hair from hair image.\n            hair_fg = cv2.bitwise_and(hair, hair, mask=mask)\n\n            # Put hair in ROI and modify the target image\n            dst = cv2.add(img_bg, hair_fg)\n\n            img[roi_ho:roi_ho + h_height, roi_wo:roi_wo + h_width] = dst\n                \n        return img\n\n    def __repr__(self):\n        return f'{self.__class__.__name__}(hairs={self.hairs}, hairs_folder=\"{self.hairs_folder}\")'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class MultimodalDataset(Dataset):\n    \"\"\"\n    Custom dataset definition\n    \"\"\"\n    def __init__(self, csv_path, img_path, transform=None):\n        \"\"\"\n        \"\"\"\n        self.df = pd.read_csv(csv_path)\n        self.img_path = img_path\n        self.transform = transform\n        \n            \n    def __getitem__(self, index):\n        \"\"\"\n        \"\"\"\n        img_name = self.df.iloc[index][\"image_name\"] \n        img_path = os.path.join(self.img_path, img_name)\n        image = Image.open(img_path)\n        if self.df.iloc[index][\"augmented\"]==1{\n            image = AdvancedHairAugmentation()(image)\n        }\n        image = image.convert(\"RGB\")\n        image = np.asarray(image)\n        if self.transform is not None:\n            image = self.transform(image)\n            \n        dtype = torch.cuda.FloatTensor if torch.cuda.is_available() else torch.FloatTensor # ???\n        features = np.fromstring(self.df.iloc[index][\"features\"][1:-1], sep=\",\") #turns features into an array\n        features = torch.from_numpy(features.astype(\"float\")) #turns the features array into a vector\n        #label = int(self.df.iloc[index]['label'])\n        labels = torch.tensor(list(self.df.iloc[index][\"target\"]), dtype = torch.float64)\n        #print(\"Label type: \", type(label))\n        #label = np.int_(label) #???\n        #print(\"label type post casting: \", type(label))\n        return image, features, labels\n        \n    def __len__(self):\n        return len(self.df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_dataloaders(input_size, batch_size, augment=False, shuffle = True):\n    # How to transform the image when you are loading them.\n    # you'll likely want to mess with the transforms on the training set.\n    \n    # For now, we resize/crop the image to the correct input size for our network,\n    # then convert it to a [C,H,W] tensor, then normalize it to values with a given mean/stdev. These normalization constants\n    # are derived from aggregating lots of data and happen to produce better results.\n    data_transforms = {\n        'train': transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize(input_size),\n            transforms.CenterCrop(input_size),\n            #Add extra transformations for data augmentation\n            AdvancedHairAugmentation(hairs_folder=\"/kaggle/input/melanoma-hairs/\")\n            transforms.RandomApply([\n                transforms.RandomChoice([\n                    transforms.RandomAffine(degrees=20),\n                    transforms.RandomAffine(degrees=0,scale=(0.1, 0.15)),\n                    transforms.RandomAffine(degrees=0,translate=(0.2,0.2)),\n                    #transforms.RandomAffine(degrees=0,shear=0.15),\n                    transforms.RandomHorizontalFlip(p=1.0)\n                ] if augment else [transforms.RandomAffine(degrees=0)])#else do nothing\n            ], p=0.5),\n            transforms.ToTensor(),\n            transforms.Normalize([0.5], [0.225])\n        ]),\n        #'val': transforms.Compose([\n            #transforms.ToPILImage(),\n            #transforms.Resize(input_size),\n            #transforms.CenterCrop(input_size),\n            #transforms.ToTensor(),\n            #transforms.Normalize([0.5], [0.225])\n        #]),\n        'test': transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.Resize(input_size),\n            transforms.CenterCrop(input_size),\n            transforms.ToTensor(),\n            transforms.Normalize([0.5], [0.225])\n        ])\n    }\n    # Create training and validation datasets\n    data_subsets = {x: MultimodalDataset(csv_path=\"../input/melanoma/features_\"+x+\".csv\", \n                                         img_path=\"../input/siim-isic-melanoma-classification/jpeg/\"+x, \n                                         transform=data_transforms[x]) for x in data_transforms.keys()}\n    print(data_transforms[0])\n    # Create training and validation dataloaders\n    # Never shuffle the test set\n    dataloaders_dict = {x: DataLoader(data_subsets[x], batch_size=batch_size, shuffle=False if x != 'train' else shuffle, num_workers=4) for x in data_transforms.keys()}\n    return dataloaders_dict","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"shuffle_datasets = True\ndataloaders = get_dataloaders(224, 64, shuffle_datasets)\ndataloaders","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}