{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":113558,"databundleVersionId":14174843,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport 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\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:05.38082Z","iopub.execute_input":"2025-10-27T23:38:05.381433Z","iopub.status.idle":"2025-10-27T23:38:08.66331Z","shell.execute_reply.started":"2025-10-27T23:38:05.381406Z","shell.execute_reply":"2025-10-27T23:38:08.662557Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RecodDataset(Dataset):\n    def __init__(self, image_dir, mask_dir=None, transform=None):\n        self.image_dir = image_dir\n        self.mask_dir = mask_dir\n        self.transform = transform\n\n        # Collect all image paths from subfolders\n        self.image_files = []\n        for root, _, files in os.walk(image_dir):\n            for file in files:\n                if file.endswith(('.png', '.jpg', '.jpeg')):\n                    self.image_files.append(os.path.join(root, file))\n\n        if mask_dir is not None:\n            self.mask_files = []\n            for root, _, files in os.walk(mask_dir):\n                for file in files:\n                    if file.endswith('.npy'):\n                        self.mask_files.append(os.path.join(root, file))\n            self.mask_files = sorted(self.mask_files)\n        else:\n            self.mask_files = None\n\n    def __len__(self):\n        return len(self.image_files)\n\n    def __getitem__(self, idx):\n        img_path = self.image_files[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        if self.mask_files is not None:\n            mask_path = self.mask_files[idx]\n            mask = np.load(mask_path)\n            mask = np.squeeze(mask).astype(np.uint8)\n            mask = Image.fromarray(mask)\n        else:\n            mask = Image.new(\"L\", img.size)\n\n        if self.transform:\n            img = self.transform(img)\n            mask = self.transform(mask)\n\n        mask = torch.unsqueeze(mask, 0)\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.664786Z","iopub.execute_input":"2025-10-27T23:38:08.665063Z","iopub.status.idle":"2025-10-27T23:38:08.673741Z","shell.execute_reply.started":"2025-10-27T23:38:08.665037Z","shell.execute_reply":"2025-10-27T23:38:08.673007Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n\n# Paths\ntrain_forged = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/forged\"\ntrain_masks = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_masks\"\n\n# Display example forged image and its mask\nexample_image_file = os.path.join(train_forged, os.listdir(train_forged)[0])\nexample_mask_file = os.path.join(train_masks, os.listdir(train_masks)[0])\n\n# Open image\nimg = Image.open(example_image_file)\n\n# Open mask\nif example_mask_file.endswith('.png'):\n    mask = Image.open(example_mask_file)\nelif example_mask_file.endswith('.npy'):\n    mask_array = np.load(example_mask_file)\n    # Convert to 2D if needed\n    if mask_array.ndim > 2:\n        mask_array = mask_array.squeeze()  # remove dimensions of size 1\n    mask = Image.fromarray(mask_array.astype(np.uint8))  # convert to uint8 if not\n\n# Plot\nplt.figure(figsize=(10,5))\nplt.subplot(1,2,1)\nplt.imshow(img)\nplt.title(\"Example Forged Image\")\nplt.axis('off')\n\nplt.subplot(1,2,2)\nplt.imshow(mask, cmap='gray')\nplt.title(\"Corresponding Mask\")\nplt.axis('off')\nplt.show()\n\n# Print mask shape for verification\nprint(\"Mask shape after processing:\", np.array(mask).shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.674426Z","iopub.execute_input":"2025-10-27T23:38:08.6746Z","iopub.status.idle":"2025-10-27T23:38:08.891039Z","shell.execute_reply.started":"2025-10-27T23:38:08.674586Z","shell.execute_reply":"2025-10-27T23:38:08.890343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RecodDataset(torch.utils.data.Dataset):\n    def __init__(self, image_dir, mask_dir=None, transform=None):\n        self.image_dir = image_dir\n        self.mask_dir = mask_dir\n        self.transform = transform\n\n        # Load image files\n        self.image_files = sorted(os.listdir(image_dir))\n\n        # Load mask files if mask_dir provided\n        if mask_dir:\n            self.mask_files = sorted(os.listdir(mask_dir))\n        else:\n            self.mask_files = [None] * len(self.image_files)  # No masks\n\n    def __len__(self):\n        return len(self.image_files)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.image_dir, self.image_files[idx])\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Default mask: zeros if authentic\n        if self.mask_files and self.mask_files[idx]:\n            mask_path = os.path.join(self.mask_dir, self.mask_files[idx])\n            mask = np.load(mask_path)\n            mask = np.squeeze(mask).astype(np.uint8)\n            mask = Image.fromarray(mask)\n        else:\n            mask = Image.fromarray(np.zeros((img.height, img.width), dtype=np.uint8))\n\n        if self.transform:\n            img = self.transform(img)\n            mask = self.transform(mask)\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.892438Z","iopub.execute_input":"2025-10-27T23:38:08.892648Z","iopub.status.idle":"2025-10-27T23:38:08.898919Z","shell.execute_reply.started":"2025-10-27T23:38:08.892632Z","shell.execute_reply":"2025-10-27T23:38:08.898218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RecodDataset(torch.utils.data.Dataset):\n    def __init__(self, authentic_dir, forged_dir, mask_dir, transform=None):\n        self.transform = transform\n\n        # Load image paths\n        self.authentic_files = sorted([os.path.join(authentic_dir, f) \n                                       for f in os.listdir(authentic_dir)])\n        self.forged_files = sorted([os.path.join(forged_dir, f) \n                                    for f in os.listdir(forged_dir)])\n\n        self.all_files = self.authentic_files + self.forged_files\n\n        # Load corresponding mask paths (None for authentic images)\n        self.mask_files = [None] * len(self.authentic_files) + \\\n                          sorted([os.path.join(mask_dir, f) \n                                  for f in os.listdir(mask_dir)])\n\n    def __len__(self):\n        return len(self.all_files)\n\n    def __getitem__(self, idx):\n        img_path = self.all_files[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        mask_path = self.mask_files[idx]\n        if mask_path is not None:\n            mask_array = np.load(mask_path)\n            mask_array = np.squeeze(mask_array).astype(np.uint8)\n            mask = Image.fromarray(mask_array)\n        else:\n            mask = Image.fromarray(np.zeros((img.height, img.width), dtype=np.uint8))\n\n        if self.transform:\n            img = self.transform(img)\n            mask = self.transform(mask)\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.899707Z","iopub.execute_input":"2025-10-27T23:38:08.899952Z","iopub.status.idle":"2025-10-27T23:38:08.918854Z","shell.execute_reply.started":"2025-10-27T23:38:08.899929Z","shell.execute_reply":"2025-10-27T23:38:08.918233Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ForgeryDataset(Dataset):\n    def __init__(self, auth_dir, forged_dir, masks_dir, transform=None):\n        self.auth_images = sorted([os.path.join(auth_dir, f) for f in os.listdir(auth_dir)])\n        self.forged_images = sorted([os.path.join(forged_dir, f) for f in os.listdir(forged_dir)])\n        self.masks_dir = masks_dir\n        self.transform = transform\n\n        self.all_images = self.auth_images + self.forged_images\n        self.labels = [0]*len(self.auth_images) + [1]*len(self.forged_images)\n\n    def __len__(self):\n        return len(self.all_images)\n\n    def __getitem__(self, idx):\n        img_path = self.all_images[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Default mask: all zeros\n        mask = np.zeros((img.height, img.width), dtype=np.uint8)\n\n        # Load mask if forged\n        if self.labels[idx] == 1:\n            mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n            mask_array = np.load(mask_file)\n            if mask_array.ndim > 2:\n                mask_array = mask_array.squeeze()\n            mask = mask_array.astype(np.uint8)\n            mask = Image.fromarray(mask)  # Convert to PIL Image\n\n        # Apply transforms to image\n        if self.transform:\n            img = self.transform(img)\n            mask = self.transform(mask)  # resize mask too\n\n        # Convert mask to tensor and add channel\n        mask = torch.tensor(np.array(mask), dtype=torch.float32).unsqueeze(0)\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.919594Z","iopub.execute_input":"2025-10-27T23:38:08.920221Z","iopub.status.idle":"2025-10-27T23:38:08.935705Z","shell.execute_reply.started":"2025-10-27T23:38:08.920203Z","shell.execute_reply":"2025-10-27T23:38:08.934978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ForgeryDataset(torch.utils.data.Dataset):\n    def __init__(self, auth_dir, forged_dir, masks_dir, transform=None):\n        self.auth_images = sorted([os.path.join(auth_dir, f) for f in os.listdir(auth_dir)])\n        self.forged_images = sorted([os.path.join(forged_dir, f) for f in os.listdir(forged_dir)])\n        self.masks_dir = masks_dir\n        self.transform = transform\n\n        self.all_images = self.auth_images + self.forged_images\n        self.labels = [0]*len(self.auth_images) + [1]*len(self.forged_images)\n\n    def __len__(self):\n        return len(self.all_images)\n\n    def __getitem__(self, idx):\n        img_path = self.all_images[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Default mask\n        if self.labels[idx] == 0:  # authentic\n            mask = Image.fromarray(np.zeros((img.height, img.width), dtype=np.uint8))\n        else:  # forged\n            mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n            mask_array = np.load(mask_file)\n            if mask_array.ndim > 2:\n                mask_array = mask_array.squeeze()\n            mask_array = mask_array.astype(np.uint8)\n            mask = Image.fromarray(mask_array)\n\n        # Apply transform to image\n        if self.transform:\n            img = self.transform(img)\n            mask = transforms.Resize((256,256))(mask)  # only resize mask\n            mask = transforms.ToTensor()(mask)         # convert mask to tensor\n\n        # Ensure mask has 1 channel\n        if mask.ndim == 2:  # unlikely after ToTensor, but safe check\n            mask = mask.unsqueeze(0)\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.936905Z","iopub.execute_input":"2025-10-27T23:38:08.937132Z","iopub.status.idle":"2025-10-27T23:38:08.956485Z","shell.execute_reply.started":"2025-10-27T23:38:08.93711Z","shell.execute_reply":"2025-10-27T23:38:08.955941Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ForgeryDataset(torch.utils.data.Dataset):\n    def __init__(self, auth_dir, forged_dir, masks_dir, transform=None):\n        self.auth_images = sorted([os.path.join(auth_dir, f) for f in os.listdir(auth_dir)])\n        self.forged_images = sorted([os.path.join(forged_dir, f) for f in os.listdir(forged_dir)])\n        self.masks_dir = masks_dir\n        self.transform = transform\n\n        self.all_images = self.auth_images + self.forged_images\n        self.labels = [0]*len(self.auth_images) + [1]*len(self.forged_images)\n\n    def __len__(self):\n        return len(self.all_images)\n\n    def __getitem__(self, idx):\n        img_path = self.all_images[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Default mask\n        if self.labels[idx] == 0:  # authentic\n            mask = Image.fromarray(np.zeros((img.height, img.width), dtype=np.uint8))\n        else:  # forged\n            mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n            mask_array = np.load(mask_file)\n            if mask_array.ndim > 2:\n                mask_array = mask_array.squeeze()\n            mask_array = mask_array.astype(np.uint8)\n            mask = Image.fromarray(mask_array)\n\n        # Apply transform to image\n        if self.transform:\n            img = self.transform(img)\n            mask = transforms.Resize((256,256))(mask)  # only resize mask\n            mask = transforms.ToTensor()(mask)         # convert mask to tensor\n\n        # Ensure mask has 1 channel\n        if mask.ndim == 2:  # unlikely after ToTensor, but safe check\n            mask = mask.unsqueeze(0)\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.957158Z","iopub.execute_input":"2025-10-27T23:38:08.957399Z","iopub.status.idle":"2025-10-27T23:38:08.974118Z","shell.execute_reply.started":"2025-10-27T23:38:08.957374Z","shell.execute_reply":"2025-10-27T23:38:08.973438Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nfrom PIL import Image\nimport torch\nfrom torch.utils.data import Dataset\nfrom torchvision import transforms\n\nclass ForgeryDataset(Dataset):\n    def __init__(self, auth_dir, forged_dir, masks_dir, transform=None):\n        self.auth_images = sorted([os.path.join(auth_dir, f) for f in os.listdir(auth_dir)])\n        self.forged_images = sorted([os.path.join(forged_dir, f) for f in os.listdir(forged_dir)])\n        self.masks_dir = masks_dir\n        self.transform = transform\n\n        self.all_images = self.auth_images + self.forged_images\n        self.labels = [0]*len(self.auth_images) + [1]*len(self.forged_images)\n\n    def __len__(self):\n        return len(self.all_images)\n\n    def __getitem__(self, idx):\n        img_path = self.all_images[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Default mask: zeros\n        if self.labels[idx] == 0:  # authentic\n            mask = Image.fromarray(np.zeros((img.height, img.width), dtype=np.uint8))\n        else:  # forged\n            mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n            mask_array = np.load(mask_file)\n\n            # Remove singleton dimensions\n            mask_array = np.squeeze(mask_array)\n\n            # If mask is 1D, reshape to square\n            if mask_array.ndim == 1:\n                size = int(np.sqrt(mask_array.size))\n                mask_array = mask_array[:size*size].reshape((size, size))\n\n            # If mask is 2D or more, take first two dims (height, width)\n            if mask_array.ndim > 2:\n                mask_array = mask_array[0, :mask_array.shape[1], :mask_array.shape[2]]\n\n            mask = Image.fromarray(mask_array.astype(np.uint8))\n\n        # Apply transforms\n        if self.transform:\n            img = self.transform(img)\n            mask = self.transform(mask)\n\n        # Ensure mask has 1 channel\n        if mask.ndim == 2:\n            mask = mask.unsqueeze(0)\n\n        return img, mask\n\n# Paths\ntrain_auth = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/authentic\"\ntrain_forged = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/forged\"\ntrain_masks = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_masks\"\n\n# Transforms\ntransform = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.ToTensor()\n])\n\n# Dataset and DataLoader\ndataset = ForgeryDataset(train_auth, train_forged, train_masks, transform=transform)\ndataloader = torch.utils.data.DataLoader(dataset, batch_size=4, shuffle=True)\n\n# Test one batch\nimgs, masks = next(iter(dataloader))\nprint(\"Batch images shape:\", imgs.shape)  # [4,3,256,256]\nprint(\"Batch masks shape:\", masks.shape)  # [4,1,256,256]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:08.974759Z","iopub.execute_input":"2025-10-27T23:38:08.975004Z","iopub.status.idle":"2025-10-27T23:38:09.564596Z","shell.execute_reply.started":"2025-10-27T23:38:08.974983Z","shell.execute_reply":"2025-10-27T23:38:09.563758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset\nfrom torchvision import transforms\nimport os\nimport numpy as np\nfrom PIL import Image\n\nclass ForgeryDataset(Dataset):\n    def __init__(self, auth_dir, forged_dir, masks_dir, transform=None):\n        self.auth_images = sorted([os.path.join(auth_dir, f) for f in os.listdir(auth_dir)])\n        self.forged_images = sorted([os.path.join(forged_dir, f) for f in os.listdir(forged_dir)])\n        self.masks_dir = masks_dir\n        self.transform = transform\n\n        self.all_images = self.auth_images + self.forged_images\n        self.labels = [0]*len(self.auth_images) + [1]*len(self.forged_images)\n\n    def __len__(self):\n        return len(self.all_images)\n\n    def __getitem__(self, idx):\n        img_path = self.all_images[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Load mask\n        if self.labels[idx] == 0:  # authentic\n            mask_array = np.zeros((img.height, img.width), dtype=np.uint8)\n        else:  # forged\n            mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n            mask_array = np.load(mask_file)\n\n            # Flatten extra singleton dimensions\n            mask_array = np.squeeze(mask_array)\n\n            # Ensure mask is 2D\n            if mask_array.ndim == 1:\n                mask_array = mask_array[np.newaxis, :]\n            elif mask_array.ndim > 2:\n                mask_array = mask_array[:, :, 0]\n\n            mask_array = mask_array.astype(np.uint8)\n\n        mask = Image.fromarray(mask_array)\n\n        # Apply transforms\n        if self.transform:\n            img = self.transform(img)\n\n        mask = transforms.Resize((256,256))(mask)\n        mask = transforms.ToTensor()(mask)  # shape [1, 256, 256]\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:09.56735Z","iopub.execute_input":"2025-10-27T23:38:09.567609Z","iopub.status.idle":"2025-10-27T23:38:09.575885Z","shell.execute_reply.started":"2025-10-27T23:38:09.567592Z","shell.execute_reply":"2025-10-27T23:38:09.574957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision import transforms\n\n# Transform لجميع الصور\nimg_transform = transforms.Compose([\n    transforms.Resize((256, 256)),  # resize أولًا\n    transforms.ToTensor(),           # ثم حول إلى tensor\n])\n\nmask_transform = transforms.Compose([\n    transforms.Resize((256, 256)),  # resize أولًا\n    transforms.ToTensor(),           # ثم إلى tensor\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:09.576583Z","iopub.execute_input":"2025-10-27T23:38:09.576783Z","iopub.status.idle":"2025-10-27T23:38:09.592835Z","shell.execute_reply.started":"2025-10-27T23:38:09.576768Z","shell.execute_reply":"2025-10-27T23:38:09.592227Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ForgeryDataset(Dataset):\n    def __init__(self, auth_dir, forged_dir, masks_dir, img_transform=None, mask_transform=None):\n        self.auth_images = sorted([os.path.join(auth_dir, f) for f in os.listdir(auth_dir)])\n        self.forged_images = sorted([os.path.join(forged_dir, f) for f in os.listdir(forged_dir)])\n        self.masks_dir = masks_dir\n        self.img_transform = img_transform\n        self.mask_transform = mask_transform\n\n        self.all_images = self.auth_images + self.forged_images\n        self.labels = [0]*len(self.auth_images) + [1]*len(self.forged_images)\n\n    def __len__(self):\n        return len(self.all_images)\n\n    def __getitem__(self, idx):\n        img_path = self.all_images[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Load mask\n        if self.labels[idx] == 0:\n            mask = Image.fromarray(np.zeros((img.height, img.width), dtype=np.uint8))\n        else:\n            mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n            mask_array = np.load(mask_file)\n            mask_array = np.squeeze(mask_array)\n            if mask_array.ndim == 1:\n                mask_array = mask_array[np.newaxis, :]\n            elif mask_array.ndim > 2:\n                mask_array = mask_array[:, :, 0]\n            mask_array = mask_array.astype(np.uint8)\n            mask = Image.fromarray(mask_array)\n\n        # Apply transforms\n        if self.img_transform:\n            img = self.img_transform(img)\n        if self.mask_transform:\n            mask = self.mask_transform(mask)\n\n        return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:09.593466Z","iopub.execute_input":"2025-10-27T23:38:09.593669Z","iopub.status.idle":"2025-10-27T23:38:09.611126Z","shell.execute_reply.started":"2025-10-27T23:38:09.593654Z","shell.execute_reply":"2025-10-27T23:38:09.610361Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def __getitem__(self, idx):\n    img_path = self.all_images[idx]\n    img = Image.open(img_path).convert(\"RGB\")\n\n    # Default mask\n    if self.labels[idx] == 0:  # authentic\n        mask_array = np.zeros((img.height, img.width), dtype=np.uint8)\n    else:  # forged\n        mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n        mask_array = np.load(mask_file)\n\n        # Ensure mask is 2D\n        if mask_array.ndim > 2:\n            mask_array = np.squeeze(mask_array)\n        if mask_array.ndim != 2:\n            # reshape to image size as fallback\n            mask_array = np.resize(mask_array, (img.height, img.width))\n        mask_array = mask_array.astype(np.uint8)\n\n    # Convert to PIL Image\n    mask = Image.fromarray(mask_array)\n\n    # Apply transforms\n    if self.transform:\n        img = self.transform(img)\n        mask = transforms.Resize((256,256))(mask)\n        mask = transforms.ToTensor()(mask)\n\n    # Ensure mask has 1 channel\n    if mask.ndim == 2:\n        mask = mask.unsqueeze(0)\n\n    return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:09.611925Z","iopub.execute_input":"2025-10-27T23:38:09.612122Z","iopub.status.idle":"2025-10-27T23:38:09.625145Z","shell.execute_reply.started":"2025-10-27T23:38:09.612108Z","shell.execute_reply":"2025-10-27T23:38:09.624605Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport torchvision\n\n# Get one batch\nimgs, masks = next(iter(train_loader))\n\n# Move to CPU for plotting if on GPU\nimgs = imgs.cpu()\nmasks = masks.cpu()\n\n# Denormalize images if you applied any normalization in transforms\n# For now, assuming no normalization\n\nbatch_size = imgs.size(0)\nfig, axes = plt.subplots(batch_size, 2, figsize=(8, batch_size*4))\n\nfor i in range(batch_size):\n    axes[i, 0].imshow(imgs[i].permute(1, 2, 0))  # [C,H,W] -> [H,W,C]\n    axes[i, 0].set_title(\"Image\")\n    axes[i, 0].axis('off')\n    \n    axes[i, 1].imshow(masks[i][0], cmap='gray')\n    axes[i, 1].set_title(\"Mask\")\n    axes[i, 1].axis('off')\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:09.625886Z","iopub.execute_input":"2025-10-27T23:38:09.626043Z","iopub.status.idle":"2025-10-27T23:38:10.898831Z","shell.execute_reply.started":"2025-10-27T23:38:09.626031Z","shell.execute_reply":"2025-10-27T23:38:10.898205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def __getitem__(self, idx):\n    img_path = self.all_images[idx]\n    img = Image.open(img_path).convert(\"RGB\")\n\n    # Default mask\n    if self.labels[idx] == 0:  # authentic\n        mask_array = np.zeros((img.height, img.width), dtype=np.uint8)\n    else:  # forged\n        mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n        mask_array = np.load(mask_file)\n\n        # Squeeze any extra dimensions\n        mask_array = np.squeeze(mask_array)\n        \n        # Ensure 2D\n        if mask_array.ndim != 2:\n            mask_array = np.resize(mask_array, (img.height, img.width))\n\n        # Convert to uint8\n        mask_array = (mask_array > 0).astype(np.uint8)  # binary mask\n\n    # Convert mask to tensor\n    mask = torch.from_numpy(mask_array).unsqueeze(0).float()  # shape [1, H, W]\n\n    # Apply transforms to image only\n    if self.transform:\n        img = self.transform(img)  # [3, H, W]\n\n    return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:10.899885Z","iopub.execute_input":"2025-10-27T23:38:10.900114Z","iopub.status.idle":"2025-10-27T23:38:10.908304Z","shell.execute_reply.started":"2025-10-27T23:38:10.900096Z","shell.execute_reply":"2025-10-27T23:38:10.907564Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def __getitem__(self, idx):\n    img_path = self.all_images[idx]\n    img = Image.open(img_path).convert(\"RGB\")\n\n    # ===== Mask handling =====\n    if self.labels[idx] == 0:  # authentic\n        mask_array = np.zeros((img.height, img.width), dtype=np.uint8)\n    else:\n        mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n        mask_array = np.load(mask_file)\n        mask_array = np.squeeze(mask_array)\n        mask_array = (mask_array > 0).astype(np.uint8)  # binary\n\n    # Resize both image and mask to fixed size\n    img = img.resize((256,256))\n    mask_array = np.array(Image.fromarray(mask_array).resize((256,256)))\n\n    # Convert to tensor\n    img = transforms.ToTensor()(img)        # [3,256,256]\n    mask = torch.from_numpy(mask_array).unsqueeze(0).float()  # [1,256,256]\n\n    return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:10.90896Z","iopub.execute_input":"2025-10-27T23:38:10.909121Z","iopub.status.idle":"2025-10-27T23:38:10.924628Z","shell.execute_reply.started":"2025-10-27T23:38:10.909108Z","shell.execute_reply":"2025-10-27T23:38:10.923738Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def __getitem__(self, idx):\n    img_path = self.all_images[idx]\n    img = Image.open(img_path).convert(\"RGB\")\n\n    if self.labels[idx] == 0:  # authentic\n        mask_array = np.zeros((img.height, img.width), dtype=np.uint8)\n    else:\n        mask_file = os.path.join(self.masks_dir, os.path.basename(img_path).replace(\".png\", \".npy\"))\n        mask_array = np.load(mask_file)\n\n        # Flatten any extra dimensions\n        while mask_array.ndim > 2:\n            mask_array = np.squeeze(mask_array)\n\n        # Safety: if still not 2D, resize manually\n        if mask_array.ndim != 2:\n            mask_array = np.resize(mask_array, (img.height, img.width))\n\n        # Make binary\n        mask_array = (mask_array > 0).astype(np.uint8)\n\n    # Convert to PIL image\n    mask = Image.fromarray(mask_array)\n\n    # Resize both image and mask\n    img = img.resize(self.img_size)\n    mask = mask.resize(self.img_size)\n\n    # Transform image\n    if self.transform:\n        img = self.transform(img)\n\n    # Convert mask to tensor [1,H,W]\n    mask = torch.from_numpy(np.array(mask)).unsqueeze(0).float()\n\n    return img, mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:10.925441Z","iopub.execute_input":"2025-10-27T23:38:10.925661Z","iopub.status.idle":"2025-10-27T23:38:10.936911Z","shell.execute_reply.started":"2025-10-27T23:38:10.925646Z","shell.execute_reply":"2025-10-27T23:38:10.936156Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), \"/kaggle/working/best_unet.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:10.93773Z","iopub.execute_input":"2025-10-27T23:38:10.937929Z","iopub.status.idle":"2025-10-27T23:38:11.035423Z","shell.execute_reply.started":"2025-10-27T23:38:10.937915Z","shell.execute_reply":"2025-10-27T23:38:11.034511Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nfrom PIL import Image\nimport torch\nfrom torchvision import transforms\nfrom tqdm import tqdm\n\n# ======================\n# RLE encoding function\n# ======================\ndef rle_encode(mask):\n    '''\n    mask: numpy array, 1 - mask, 0 - background\n    Returns run length as string formatted\n    '''\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\n# ======================\n# Paths\n# ======================\nmodel_path = \"/kaggle/working/best_unet.pth\"\ntest_dir = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/test_images\"\nsubmission_path = \"/kaggle/working/sample_submission.csv\"\n\n# ======================\n# Device\n# ======================\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# ======================\n# Load model\n# ======================\nmodel = UNet(in_channels=3, out_channels=1).to(device)\nmodel.load_state_dict(torch.load(model_path, map_location=device))\nmodel.eval()\n\n# ======================\n# Test images\n# ======================\ntest_images = sorted([f for f in os.listdir(test_dir) if f.endswith('.png')])\n\n# ======================\n# Transform\n# ======================\ntransform = transforms.Compose([\n    transforms.Resize((256,256)),\n    transforms.ToTensor(),\n])\n\n# ======================\n# Inference\n# ======================\nannotations = []\ncase_ids = []\n\nwith torch.no_grad():\n    for img_file in tqdm(test_images):\n        case_ids.append(os.path.splitext(img_file)[0])  # filename without extension\n        img_path = os.path.join(test_dir, img_file)\n        img = Image.open(img_path).convert(\"RGB\")\n        img_tensor = transform(img).unsqueeze(0).to(device)  # [1,3,256,256]\n\n        output = model(img_tensor)\n        pred_mask = torch.sigmoid(output).squeeze().cpu().numpy()  # [256,256]\n        pred_mask = (pred_mask > 0.5).astype(np.uint8)\n\n        if pred_mask.sum() == 0:\n            annotations.append(\"authentic\")\n        else:\n            # Resize mask back to original size for RLE\n            pred_mask_full = np.array(Image.fromarray(pred_mask*255).resize(img.size, resample=Image.NEAREST))\n            pred_mask_bin = (pred_mask_full > 127).astype(np.uint8)\n            rle = rle_encode(pred_mask_bin)\n            annotations.append(rle)\n\n# ======================\n# Save submission\n# ======================\nsubmission = pd.DataFrame({\n    \"case_id\": case_ids,\n    \"annotation\": annotations\n})\nsubmission.to_csv(submission_path, index=False)\nprint(f\"Submission file saved at: {submission_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-27T23:38:11.036287Z","iopub.execute_input":"2025-10-27T23:38:11.036552Z","iopub.status.idle":"2025-10-27T23:38:11.207358Z","shell.execute_reply.started":"2025-10-27T23:38:11.036531Z","shell.execute_reply":"2025-10-27T23:38:11.206636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}