{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":113558,"databundleVersionId":14174843,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    👋 Hi everyone! 🌟\n</h1>\n\n<p style=\"\n    text-align: center; \n    font-size: 1.2em;\n    margin-bottom: 25px;\n    position: relative;\n    z-index: 1;\n    font-weight: 500;\n\">\n    I hope this notebook will be useful for beginners! 🚀\n</p>\n\n<div style=\"\n    background: rgba(163, 230, 53, 0.1);\n    border-left: 4px solid #a3e635;\n    border-radius: 8px;\n    padding: 20px;\n    margin: 20px 0;\n    position: relative;\n    z-index: 1;\n\">\n    <h3 style=\"\n        color: #a3e635;\n        margin-top: 0;\n        font-size: 1.3em;\n        display: flex;\n        align-items: center;\n        gap: 10px;\n    \">\n        🎯 Task: <span style=\"color: #f1f5f9;\">Semantic Segmentation</span>\n    </h3>\n</div>\n\n<div style=\"\n    background: rgba(255, 255, 255, 0.05);\n    border-radius: 10px;\n    padding: 20px;\n    position: relative;\n    z-index: 1;\n\">\n    <h3 style=\"\n        color: #a3e635;\n        margin-top: 0;\n        font-size: 1.3em;\n        display: flex;\n        align-items: center;\n        gap: 10px;\n    \">\n        ⚡ Challenges:\n    </h3>\n    <ul style=\"\n        color: #f1f5f9;\n        font-size: 1.1em;\n        line-height: 1.6;\n        margin-bottom: 0;\n    \">\n        <li>🖼️ Different sizes of images</li>\n        <li>🧠 Difficult task for conventional models</li>\n        <li>⚠️ Penalties for mistakes</li>\n    </ul>\n</div>","metadata":{}},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Importing libraries\n</h1>","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport json\nimport torch\nimport torchvision\nimport numpy as np\nimport pandas as pd\nimport torch.nn as nn\nimport albumentations as A\nimport matplotlib.pyplot as plt\nimport torch.nn.functional as F\n\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom collections import defaultdict\nfrom albumentations.pytorch import ToTensorV2\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.models.detection import MaskRCNN\nfrom sklearn.model_selection import train_test_split\nfrom torchvision.models.detection.rpn import AnchorGenerator\nfrom torchvision.transforms import functional as F_transforms\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Checking GPU availability\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:33:30.107779Z","iopub.execute_input":"2025-10-24T00:33:30.108621Z","iopub.status.idle":"2025-10-24T00:34:12.226032Z","shell.execute_reply.started":"2025-10-24T00:33:30.108586Z","shell.execute_reply":"2025-10-24T00:34:12.225361Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Let's check the image sizes, sorry for the big conclusion, it's just that size is very important in CV tasks.\n</h1>","metadata":{}},{"cell_type":"code","source":"def get_unique_sizes(directory):\n    size_counts = defaultdict(int)\n    for root, _, files in os.walk(directory):\n        for file in files:\n            if file.lower().endswith(('.png', '.jpg', '.jpeg', 'JPG')):\n                try:\n                    with Image.open(os.path.join(root, file)) as img:\n                        size = img.size\n                        size_counts[size] += 1\n                except Exception as e:\n                    print(f\"Error {file}: {e}\")\n\n    return size_counts\n\nfolders = [\n    \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/authentic\",\n    \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/forged\",\n    \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/test_images\"\n]\n\nfor folder in folders:\n    print(f\"\\n📂 Folder: {folder}\")\n    sizes = get_unique_sizes(folder)\n\n    if not sizes:\n        print(\"No images or mistake in code\")\n        continue\n    \n    sorted_sizes = sorted(sizes.items(), key=lambda x: x[1], reverse=True)\n\n    print(\"┌───────────────┬───────────────┬─────────┐\")\n    print(\"│  Width (px)  │ Height (px) │ Quantity │\")\n    print(\"├───────────────┼───────────────┼─────────┤\")\n    for (w, h), count in sorted_sizes:\n        print(f\"│ {w:<13} │ {h:<13} │ {count:<7} │\")\n    print(\"└───────────────┴───────────────┴─────────┘\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:12.227305Z","iopub.execute_input":"2025-10-24T00:34:12.227680Z","iopub.status.idle":"2025-10-24T00:34:46.590368Z","shell.execute_reply.started":"2025-10-24T00:34:12.227661Z","shell.execute_reply":"2025-10-24T00:34:46.589622Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Check all data structure\n</h1>","metadata":{}},{"cell_type":"code","source":"def analyze_data_structure():\n    base_path = '/kaggle/input/recodai-luc-scientific-image-forgery-detection'\n    \n    # Checking train images\n    train_authentic_path = os.path.join(base_path, 'train_images/authentic')\n    train_forged_path = os.path.join(base_path, 'train_images/forged')\n    train_masks_path = os.path.join(base_path, 'train_masks')\n    test_images_path = os.path.join(base_path, 'test_images')\n    \n    print(f\"Authentic images: {len(os.listdir(train_authentic_path))}\")\n    print(f\"Forged images: {len(os.listdir(train_forged_path))}\")\n    print(f\"Masks: {len(os.listdir(train_masks_path))}\")\n    print(f\"Test images: {len(os.listdir(test_images_path))}\")\n    \n    # Let's analyze some examples of masks\n    mask_files = os.listdir(train_masks_path)[:5]\n    print(f\"Examples of mask files: {mask_files}\")\n    \n    # Checking the mask format\n    sample_mask = np.load(os.path.join(train_masks_path, mask_files[0]))\n    print(f\"Mask format: {sample_mask.shape}, dtype: {sample_mask.dtype}\")\n    \n    test_files = os.listdir(test_images_path)\n    print(f\"Test images: {test_files}\")\n    \n    return {\n        'train_authentic': train_authentic_path,\n        'train_forged': train_forged_path,\n        'train_masks': train_masks_path,\n        'test_images': test_images_path\n    }\n\npaths = analyze_data_structure()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:46.591247Z","iopub.execute_input":"2025-10-24T00:34:46.591504Z","iopub.status.idle":"2025-10-24T00:34:46.628158Z","shell.execute_reply.started":"2025-10-24T00:34:46.591479Z","shell.execute_reply":"2025-10-24T00:34:46.627397Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    🌟 Let's take a look at the image\n</h1>\n\n<div style=\"\n    background: rgba(255, 255, 255, 0.05);\n    border-radius: 10px;\n    padding: 20px;\n    position: relative;\n    z-index: 1;\n\">\n    <h3 style=\"\n        color: #a3e635;\n        margin-top: 0;\n        font-size: 1.3em;\n        display: flex;\n        align-items: center;\n        gap: 10px;\n    \">\n        Types:\n    </h3>\n    <ul style=\"\n        color: #f1f5f9;\n        font-size: 1.1em;\n        line-height: 1.6;\n        margin-bottom: 0;\n    \">\n        <li>Authentic: Real images without manipulation</li>\n        <li>Tampered: Areas that have been manipulated</li>\n    </ul>\n</div>","metadata":{}},{"cell_type":"code","source":"num_samples = 3 # counts images/masks\n\n# Visualize authentic images\nauthentic_files = sorted(os.listdir(paths['train_authentic']))[:num_samples]\nforged_files = sorted(os.listdir(paths['train_forged']))[:num_samples]\nmask_files = sorted(os.listdir(paths['train_masks']))[:num_samples]\n    \nfig, axes = plt.subplots(3, num_samples, figsize=(15, 10))\n    \n# Authentic images\nfor i, file in enumerate(authentic_files):\n    img_path = os.path.join(paths['train_authentic'], file)\n    img = Image.open(img_path)\n    axes[0, i].imshow(img)\n    axes[0, i].set_title(f'Authentic: {file}')\n    axes[0, i].axis('off')\n    \n# Forged images\nfor i, file in enumerate(forged_files):\n    img_path = os.path.join(paths['train_forged'], file)\n    img = Image.open(img_path)\n    axes[1, i].imshow(img)\n    axes[1, i].set_title(f'Forged: {file}')\n    axes[1, i].axis('off')\n    \n# Masks\nfor i, file in enumerate(mask_files):\n    mask_path = os.path.join(paths['train_masks'], file)\n    mask = np.load(mask_path)\n    mask = np.squeeze(mask)\n    axes[2, i].imshow(mask, cmap='gray')\n    axes[2, i].set_title(f'Mask: {file}')\n    axes[2, i].axis('off')\n    \nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:46.629538Z","iopub.execute_input":"2025-10-24T00:34:46.630093Z","iopub.status.idle":"2025-10-24T00:34:48.337176Z","shell.execute_reply.started":"2025-10-24T00:34:46.630067Z","shell.execute_reply":"2025-10-24T00:34:48.336181Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Stats about image sizes\n</h1>","metadata":{}},{"cell_type":"code","source":"all_sizes = []\n\n# Authentic images\nfor file in os.listdir(paths['train_authentic'])[:50]:\n    img_path = os.path.join(paths['train_authentic'], file)\n    img = Image.open(img_path)\n    all_sizes.append(img.size)\n\n# Forged images  \nfor file in os.listdir(paths['train_forged'])[:50]:\n    img_path = os.path.join(paths['train_forged'], file)\n    img = Image.open(img_path)\n    all_sizes.append(img.size)\n\nsizes_df = pd.DataFrame(all_sizes, columns=['width', 'height'])\nprint(\"Image size Statistics:\")\nprint(sizes_df.describe())\n\n# Visualization of the size distribution\nplt.figure(figsize=(12, 4))\n\nplt.subplot(1, 2, 1)\nplt.hist(sizes_df['width'], bins=20, alpha=0.7, color='blue')\nplt.title('Width distribution')\nplt.xlabel('Width')\nplt.ylabel('Frequency')\n\nplt.subplot(1, 2, 2)\nplt.hist(sizes_df['height'], bins=20, alpha=0.7, color='red')\nplt.title('Height distribution')\nplt.xlabel('Height')\nplt.ylabel('Frequency')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:48.338013Z","iopub.execute_input":"2025-10-24T00:34:48.338237Z","iopub.status.idle":"2025-10-24T00:34:48.781706Z","shell.execute_reply.started":"2025-10-24T00:34:48.338220Z","shell.execute_reply":"2025-10-24T00:34:48.780955Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Dataset Implementation for Forgery Detection\n</h1>","metadata":{}},{"cell_type":"code","source":"class ForgeryDataset(Dataset):\n    def __init__(self, authentic_path, forged_path, masks_path, transform=None, is_train=True):\n        self.transform = transform\n        self.is_train = is_train\n        \n        # Collect all data samples\n        self.samples = []\n        \n        # Authentic images\n        for file in os.listdir(authentic_path):\n            img_path = os.path.join(authentic_path, file)\n            base_name = file.split('.')[0]\n            mask_path = os.path.join(masks_path, f\"{base_name}.npy\")\n            \n            self.samples.append({\n                'image_path': img_path,\n                'mask_path': mask_path,\n                'is_forged': False,\n                'image_id': base_name\n            })\n        \n        # Forged images\n        for file in os.listdir(forged_path):\n            img_path = os.path.join(forged_path, file)\n            base_name = file.split('.')[0]\n            mask_path = os.path.join(masks_path, f\"{base_name}.npy\")\n            \n            self.samples.append({\n                'image_path': img_path,\n                'mask_path': mask_path,\n                'is_forged': True,\n                'image_id': base_name\n            })\n    \n    def __len__(self):\n        return len(self.samples)\n    \n    def __getitem__(self, idx):\n        sample = self.samples[idx]\n        \n        # Load image\n        image = Image.open(sample['image_path']).convert('RGB')\n        image = np.array(image)  # (H, W, 3)\n        \n        # Load and process mask\n        if os.path.exists(sample['mask_path']):\n            mask = np.load(sample['mask_path'])\n            \n            # Handle multi-channel masks\n            if mask.ndim == 3:\n                if mask.shape[0] <= 10:  # channels first (C, H, W)\n                    mask = np.any(mask, axis=0)\n                elif mask.shape[-1] <= 10:  # channels last (H, W, C)\n                    mask = np.any(mask, axis=-1)\n                else:\n                    raise ValueError(f\"Ambiguous 3D mask shape: {mask.shape}\")\n            \n            mask = (mask > 0).astype(np.uint8)\n        else:\n            mask = np.zeros((image.shape[0], image.shape[1]), dtype=np.uint8)\n    \n        # Shape validation\n        assert image.shape[:2] == mask.shape, f\"Shape mismatch: img {image.shape}, mask {mask.shape}\"\n        \n        # Apply transformations\n        if self.transform:\n            transformed = self.transform(image=image, mask=mask)\n            image = transformed['image']\n            mask = transformed['mask']\n        else:\n            image = F_transforms.to_tensor(image)\n            mask = torch.tensor(mask, dtype=torch.uint8)\n        \n        # Prepare targets for Mask R-CNN\n        if sample['is_forged'] and mask.sum() > 0:\n            boxes, labels, masks = self.mask_to_boxes(mask)\n            \n            target = {\n                'boxes': boxes,\n                'labels': labels,\n                'masks': masks,\n                'image_id': torch.tensor([idx]),\n                'area': (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0]),\n                'iscrowd': torch.zeros((len(boxes),), dtype=torch.int64)\n            }\n        else:\n            # For authentic images or images without masks\n            target = {\n                'boxes': torch.zeros((0, 4), dtype=torch.float32),\n                'labels': torch.zeros(0, dtype=torch.int64),\n                'masks': torch.zeros((0, image.shape[1], image.shape[2]), dtype=torch.uint8),\n                'image_id': torch.tensor([idx]),\n                'area': torch.zeros(0, dtype=torch.float32),\n                'iscrowd': torch.zeros((0,), dtype=torch.int64)\n            }\n        \n        return image, target\n    \n    def mask_to_boxes(self, mask):\n        \"\"\"Convert segmentation mask to bounding boxes for Mask R-CNN\"\"\"\n        if isinstance(mask, torch.Tensor):\n            mask_np = mask.numpy()\n        else:\n            mask_np = mask\n        \n        # Find contours in the mask\n        contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n        \n        boxes = []\n        masks = []\n        \n        for contour in contours:\n            if len(contour) > 0:\n                x, y, w, h = cv2.boundingRect(contour)\n                # Filter out very small regions\n                if w > 5 and h > 5:\n                    boxes.append([x, y, x + w, y + h])\n                    # Create binary mask for this contour\n                    contour_mask = np.zeros_like(mask_np)\n                    cv2.fillPoly(contour_mask, [contour], 1)\n                    masks.append(contour_mask)\n        \n        if boxes:\n            boxes = torch.tensor(boxes, dtype=torch.float32)\n            labels = torch.ones((len(boxes),), dtype=torch.int64)\n            masks = torch.tensor(np.array(masks), dtype=torch.uint8)\n        else:\n            boxes = torch.zeros((0, 4), dtype=torch.float32)\n            labels = torch.zeros(0, dtype=torch.int64)\n            masks = torch.zeros((0, mask_np.shape[0], mask_np.shape[1]), dtype=torch.uint8)\n        \n        return boxes, labels, masks\n\n# Transformations for learning\ntrain_transform = A.Compose([\n    A.Resize(256, 256),\n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.RandomRotate90(p=0.5),\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2(),\n])\n\nval_transform = A.Compose([\n    A.Resize(256, 256),\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2(),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:48.782605Z","iopub.execute_input":"2025-10-24T00:34:48.783153Z","iopub.status.idle":"2025-10-24T00:34:48.806977Z","shell.execute_reply.started":"2025-10-24T00:34:48.783125Z","shell.execute_reply":"2025-10-24T00:34:48.806278Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Creating datasets and dataloaders\n</h1>","metadata":{}},{"cell_type":"code","source":"full_dataset = ForgeryDataset(\n    paths['train_authentic'], \n    paths['train_forged'], \n    paths['train_masks'],\n    transform=train_transform\n)\n\n# Split into train/val\ntrain_size = int(0.8 * len(full_dataset))\nval_size = len(full_dataset) - train_size\ntrain_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])\n\n# Changing transformations for the val dataset\nval_dataset.dataset.transform = val_transform\n\n# Creating dataloaders\ntrain_loader = DataLoader(train_dataset, batch_size=4, shuffle=True, collate_fn=lambda x: tuple(zip(*x)))\nval_loader = DataLoader(val_dataset, batch_size=4, shuffle=False, collate_fn=lambda x: tuple(zip(*x)))\n\nprint(f\"Train samples: {len(train_dataset)}\")\nprint(f\"Val samples: {len(val_dataset)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:48.807791Z","iopub.execute_input":"2025-10-24T00:34:48.808288Z","iopub.status.idle":"2025-10-24T00:34:48.839607Z","shell.execute_reply.started":"2025-10-24T00:34:48.808269Z","shell.execute_reply":"2025-10-24T00:34:48.839049Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Creating a lightweight Mask R-CNN model\n</h1>","metadata":{}},{"cell_type":"code","source":"def create_light_mask_rcnn(num_classes=2):\n    backbone = torchvision.models.mobilenet_v3_small(pretrained=False).features\n    backbone.out_channels = 576\n    \n    # extracts characteristics from an image\n    backbone = nn.Sequential(\n        backbone,\n        nn.Conv2d(576, 256, kernel_size=1),\n        nn.ReLU(inplace=True)\n    )\n    backbone.out_channels = 256\n    \n    # Anchor generator\n    anchor_generator = AnchorGenerator(\n        sizes=((16, 32, 64, 128),),\n        aspect_ratios=((0.5, 1.0, 2.0),)\n    )\n    \n    # ROI pools\n    roi_pooler = torchvision.ops.MultiScaleRoIAlign(\n        featmap_names=['0'],\n        output_size=5,\n        sampling_ratio=1\n    )\n    \n    mask_roi_pooler = torchvision.ops.MultiScaleRoIAlign(\n        featmap_names=['0'],\n        output_size=10,\n        sampling_ratio=1\n    )\n    \n    model = MaskRCNN(\n        backbone,\n        num_classes=num_classes,\n        rpn_anchor_generator=anchor_generator,\n        box_roi_pool=roi_pooler,\n        mask_roi_pool=mask_roi_pooler,\n        min_size=224,\n        max_size=224,\n        rpn_pre_nms_top_n_train=1000,\n        rpn_pre_nms_top_n_test=1000,\n        rpn_post_nms_top_n_train=200,\n        rpn_post_nms_top_n_test=200,\n        box_detections_per_img=100\n    )\n    \n    return model\n\nmodel = create_light_mask_rcnn()\nmodel.to(device)\n\nprint(f\"Number of parameters: {sum(p.numel() for p in model.parameters()):,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:48.840278Z","iopub.execute_input":"2025-10-24T00:34:48.840531Z","iopub.status.idle":"2025-10-24T00:34:49.220610Z","shell.execute_reply.started":"2025-10-24T00:34:48.840507Z","shell.execute_reply":"2025-10-24T00:34:49.219965Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Functions for train and validate\n</h1>","metadata":{}},{"cell_type":"code","source":"def train_epoch(model, dataloader, optimizer, device):\n    model.train()\n    total_loss = 0\n    \n    for batch_idx, (images, targets) in enumerate(tqdm(dataloader, desc=\"Training\")):\n        images = [img.to(device) for img in images]\n        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n        \n        # Forward pass\n        loss_dict = model(images, targets)\n        losses = sum(loss for loss in loss_dict.values())\n        \n        # Backward pass\n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n        \n        total_loss += losses.item()\n    \n    return total_loss / len(dataloader)\n\ndef validate_epoch(model, dataloader, device):\n    model.train()  # For validation, we use train mode because of the features of Mask R-CNN\n    total_loss = 0\n    \n    with torch.no_grad():\n        for batch_idx, (images, targets) in enumerate(tqdm(dataloader, desc=\"Validation\")):\n            images = [img.to(device) for img in images]\n            targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n            \n            loss_dict = model(images, targets)\n            losses = sum(loss for loss in loss_dict.values())\n            total_loss += losses.item()\n    \n    return total_loss / len(dataloader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:49.221350Z","iopub.execute_input":"2025-10-24T00:34:49.221595Z","iopub.status.idle":"2025-10-24T00:34:49.228094Z","shell.execute_reply.started":"2025-10-24T00:34:49.221577Z","shell.execute_reply":"2025-10-24T00:34:49.227495Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Vizualize batch before learning\n</h1>","metadata":{}},{"cell_type":"code","source":"def visualize_batch_samples(dataloader, model=None, device=device):\n    images, targets = next(iter(dataloader))\n    \n    fig, axes = plt.subplots(2, 4, figsize=(20, 10))\n    \n    for i in range(min(4, len(images))):\n        # Original image\n        img = images[i].cpu().permute(1, 2, 0).numpy()\n        img = img * np.array([0.229, 0.224, 0.225]) + np.array([0.485, 0.456, 0.406])  # denormalize\n        img = np.clip(img, 0, 1)\n        \n        axes[0, i].imshow(img)\n        axes[0, i].set_title(f'Image {i}')\n        axes[0, i].axis('off')\n        \n        # Mask\n        mask = torch.zeros_like(images[i][0])\n        for target_mask in targets[i]['masks']:\n            mask = torch.max(mask, target_mask.cpu())\n        \n        axes[1, i].imshow(mask, cmap='hot')\n        axes[1, i].set_title(f'Mask {i}')\n        axes[1, i].axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\nvisualize_batch_samples(train_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:49.230015Z","iopub.execute_input":"2025-10-24T00:34:49.230415Z","iopub.status.idle":"2025-10-24T00:34:50.255852Z","shell.execute_reply.started":"2025-10-24T00:34:49.230399Z","shell.execute_reply":"2025-10-24T00:34:50.255092Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Learn our R-CNN small model\n</h1>","metadata":{}},{"cell_type":"code","source":"optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=0.001)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)\n\nnum_epochs = 3\ntrain_losses = []\nval_losses = []\n\nfor epoch in range(num_epochs):\n    print(f\"Epoch {epoch+1}/{num_epochs}\")\n    \n    # Train\n    train_loss = train_epoch(model, train_loader, optimizer, device)\n    train_losses.append(train_loss)\n    \n    # Validation\n    val_loss = validate_epoch(model, val_loader, device)\n    val_losses.append(val_loss)\n    \n    # Scheduler step\n    scheduler.step()\n    \n    print(f\"Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}\")\n    \n    # We save the model every 2 epochs\n    if (epoch + 1) % 2 == 0:\n        torch.save(model.state_dict(), f'mask_rcnn_epoch_{epoch+1}.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:34:50.256740Z","iopub.execute_input":"2025-10-24T00:34:50.257002Z","iopub.status.idle":"2025-10-24T00:41:00.917228Z","shell.execute_reply.started":"2025-10-24T00:34:50.256985Z","shell.execute_reply":"2025-10-24T00:41:00.916458Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Show results loss function\n</h1>","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(10, 5))\n\nplt.plot(train_losses, label='Train Loss')\nplt.plot(val_losses, label='Val Loss')\n\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Training and Validation Loss')\n\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:41:00.917892Z","iopub.execute_input":"2025-10-24T00:41:00.918120Z","iopub.status.idle":"2025-10-24T00:41:01.072848Z","shell.execute_reply.started":"2025-10-24T00:41:00.918104Z","shell.execute_reply":"2025-10-24T00:41:01.072238Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Load and preprocess test images\n</h1>","metadata":{}},{"cell_type":"code","source":"def rle_encode(mask):\n    \"\"\"\n    Encode binary mask to RLE in the format required by the competition.\n    Returns a JSON string like \"[123,4,567,8]\"\n    \"\"\"\n    # Ensure mask is 2D and binary\n    mask = mask.astype(bool)\n    \n    # Flatten in Fortran order\n    flat = mask.T.flatten()\n    \n    # Find indices where value is True\n    dots = np.where(flat)[0]\n    \n    if len(dots) == 0:\n        return json.dumps([])  # or just return 'authentic' upstream\n    \n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if b > prev + 1:\n            run_lengths.extend([b + 1, 0])  # 1-based index\n        run_lengths[-1] += 1\n        prev = b\n    \n    # Convert numpy ints to Python ints for JSON compatibility\n    run_lengths = [int(x) for x in run_lengths]\n    return json.dumps(run_lengths)\n\ndef predict_test_images(model, test_path, device):\n    model.eval()\n    predictions = {}\n    \n    test_files = sorted(os.listdir(test_path))\n    \n    transform = A.Compose([\n        A.Resize(256, 256),\n        A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n        ToTensorV2(),\n    ])\n    \n    for file in tqdm(test_files, desc=\"Processing test images\"):\n        case_id = file.split('.')[0]\n        \n        # Load and preprocess image\n        img_path = os.path.join(test_path, file)\n        image = Image.open(img_path).convert('RGB')\n        image_np = np.array(image)\n        \n        original_size = image_np.shape[:2]\n        \n        # Apply transformations\n        transformed = transform(image=image_np)\n        image_tensor = transformed['image'].unsqueeze(0).to(device)\n        \n        # Model prediction\n        with torch.no_grad():\n            prediction = model(image_tensor)\n        \n        # Process predictions\n        masks = prediction[0]['masks']\n        scores = prediction[0]['scores']\n        \n        # Filter by confidence threshold\n        confidence_threshold = 0.4     # CHANGE THIS TO SEE RESULTS(changes)\n        valid_detections = scores > confidence_threshold\n        \n        if valid_detections.sum() == 0:\n            # No detections -> authentic image\n            predictions[case_id] = \"authentic\"\n        else:\n            # Combine all detected masks\n            combined_mask = torch.zeros((256, 256), device=device)\n            for i in range(len(masks)):\n                if valid_detections[i]:\n                    mask = masks[i, 0] > 0.5  # Binarize mask\n                    combined_mask = torch.logical_or(combined_mask, mask)\n            \n            # Convert to numpy and resize to original dimensions\n            combined_mask_np = combined_mask.cpu().numpy().astype(np.uint8)\n            combined_mask_resized = cv2.resize(combined_mask_np, \n                                             (original_size[1], original_size[0]),\n                                             interpolation=cv2.INTER_NEAREST)\n            \n            # RLE encoding\n            if combined_mask_resized.sum() == 0:\n                predictions[case_id] = \"authentic\"\n            else:\n                rle_json = rle_encode_competition(combined_mask_resized)\n                predictions[case_id] = rle_json\n    \n    return predictions\n\npredictions = predict_test_images(model, paths['test_images'], device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:41:01.073535Z","iopub.execute_input":"2025-10-24T00:41:01.073734Z","iopub.status.idle":"2025-10-24T00:41:01.137065Z","shell.execute_reply.started":"2025-10-24T00:41:01.073720Z","shell.execute_reply":"2025-10-24T00:41:01.136311Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a2f0c 0%, #2d4a1a 50%, #4a4a1a 100%);\n    border: 2px solid #a3e635;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(163, 230, 53, 0.4),\n                inset 0 0 20px rgba(255, 255, 255, 0.1);\n    color: #f1f5f9;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    position: relative;\n    overflow: hidden;\n\">\n\n<div style=\"\n    position: absolute;\n    top: -20px;\n    right: -20px;\n    width: 100px;\n    height: 100px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.25) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<div style=\"\n    position: absolute;\n    bottom: -40px;\n    left: -40px;\n    width: 120px;\n    height: 120px;\n    background: radial-gradient(circle, rgba(163, 230, 53, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #a3e635;\n    margin: 0 0 20px 0;\n    text-align: center;\n    font-weight: 700;\n    font-size: 1.8em;\n    text-shadow: 0 0 15px rgba(163, 230, 53, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Create submission file\n</h1>","metadata":{}},{"cell_type":"code","source":"# Reading the sample submission for the correct order\nsample_submission = pd.read_csv('/kaggle/input/recodai-luc-scientific-image-forgery-detection/sample_submission.csv')\n    \n# Create DataFrame with predictions\nsubmission_data = []\nfor case_id in sample_submission['case_id']:\n    case_id_str = str(case_id)\n    if case_id_str in predictions:\n        submission_data.append({'case_id': case_id, 'annotation': predictions[case_id_str]})\n    else:\n        # If case_id not in predictions, use authentic as default\n        submission_data.append({'case_id': case_id, 'annotation': 'authentic'})\n    \nsubmission = pd.DataFrame(submission_data)\n    \n# Save submission file\nsubmission.to_csv('submission.csv', index=False)\n    \n# Prediction statistics\nauthentic_count = (submission['annotation'] == 'authentic').sum()\nforged_count = len(submission) - authentic_count\n\nprint(f\"Prediction Statistics:\")\nprint(f\"Authentic: {authentic_count}\")\nprint(f\"Forged: {forged_count}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:41:01.137849Z","iopub.execute_input":"2025-10-24T00:41:01.138144Z","iopub.status.idle":"2025-10-24T00:41:01.154764Z","shell.execute_reply.started":"2025-10-24T00:41:01.138122Z","shell.execute_reply":"2025-10-24T00:41:01.154251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\n\n# We take the first file from the test folder\ntest_files = sorted(os.listdir(paths['test_images']))\nfile = test_files[0]\nimg_path = os.path.join(paths['test_images'], file)\n\n# Uploading an image\nimage = Image.open(img_path).convert('RGB')\nimage_np = np.array(image)\n\n# Transformations\ntransform = A.Compose([\n    A.Resize(256, 256),\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2(),\n])\n\n# Apply transformations and make predictions\ntransformed = transform(image=image_np)\nimage_tensor = transformed['image'].unsqueeze(0).to(device)\n\nwith torch.no_grad():\n    prediction = model(image_tensor)\n\nmasks = prediction[0]['masks']\nscores = prediction[0]['scores']\nconfidence_threshold = 0.5\nvalid_detections = scores > confidence_threshold\n\n# Creating a shape: original on the left, mask on the right\nfig, axes = plt.subplots(1, 2, figsize=(12, 6))\n\n# Original\naxes[0].imshow(image_np)\naxes[0].set_title(f'Original: {file}', fontsize=14, fontweight='bold')\naxes[0].axis('off')\n\n# Mask\nif valid_detections.sum() == 0:\n    combined_mask = np.zeros((256, 256))\n    title = 'Predicted: Authentic'\n    cmap = 'gray'\nelse:\n    combined_mask = np.zeros((256, 256), dtype=np.float32)\n    for idx in range(len(masks)):\n        if valid_detections[idx]:\n            mask = masks[idx, 0] > 0.5\n            combined_mask = np.maximum(combined_mask, mask.cpu().numpy().astype(np.float32))\n    \n    title = f'Predicted: Forged ({valid_detections.sum()} regions)'\n    cmap = 'hot'\n\nim = axes[1].imshow(combined_mask, cmap=cmap, vmin=0, vmax=1)\naxes[1].set_title(title, fontsize=14, fontweight='bold')\naxes[1].axis('off')\n\nif valid_detections.sum() > 0:\n    plt.colorbar(im, ax=axes[1], fraction=0.046, pad=0.04)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:41:01.155541Z","iopub.execute_input":"2025-10-24T00:41:01.155860Z","iopub.status.idle":"2025-10-24T00:41:01.675145Z","shell.execute_reply.started":"2025-10-24T00:41:01.155837Z","shell.execute_reply":"2025-10-24T00:41:01.674434Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.read_csv('/kaggle/working/submission.csv')\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:41:01.676026Z","iopub.execute_input":"2025-10-24T00:41:01.676318Z","iopub.status.idle":"2025-10-24T00:41:01.690979Z","shell.execute_reply.started":"2025-10-24T00:41:01.676296Z","shell.execute_reply":"2025-10-24T00:41:01.690483Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), 'final_mask_rcnn_model.pth')\nprint(\"The final model is saved as: 'final_mask_rcnn_model.pth'\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-24T00:41:01.691597Z","iopub.execute_input":"2025-10-24T00:41:01.691790Z","iopub.status.idle":"2025-10-24T00:41:01.776652Z","shell.execute_reply.started":"2025-10-24T00:41:01.691775Z","shell.execute_reply":"2025-10-24T00:41:01.776072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}