{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":113558,"databundleVersionId":14174843,"sourceType":"competition"}],"dockerImageVersionId":30746,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport json\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\n\n# ============================================================\n# MODULE 1: Mask Encoding and Visualization\n# ============================================================\n\ndef rle_encode(mask, fg_val=1):\n    \"\"\"Convert binary mask to Run-Length Encoding (RLE)\"\"\"\n    dots = np.where(mask.T.flatten() == fg_val)[0]\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if b > prev + 1:\n            run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n    return run_lengths\n\n\ndef visualize_mask(mask, title=\"Mask\"):\n    \"\"\"Display binary mask with grid and pixel values\"\"\"\n    plt.figure(figsize=(6, 6))\n    plt.imshow(mask, cmap='gray', vmin=0, vmax=1)\n    plt.title(title)\n    plt.axis('off')\n\n    # Grid\n    for i in range(mask.shape[0] + 1):\n        plt.axhline(i - 0.5, color='red', alpha=0.3, linewidth=0.5)\n        plt.axvline(i - 0.5, color='red', alpha=0.3, linewidth=0.5)\n\n    # Pixel values\n    for i in range(mask.shape[0]):\n        for j in range(mask.shape[1]):\n            plt.text(\n                j, i, str(mask[i, j]),\n                ha='center', va='center',\n                color='blue' if mask[i, j] == 0 else 'white',\n                fontweight='bold'\n            )\n    plt.show()\n\n\n# ============================================================\n# MODULE 2: Example Mask Generators\n# ============================================================\n\ndef create_plus_mask(size=9):\n    \"\"\"Create a plus-shaped binary mask\"\"\"\n    mask = np.zeros((size, size), dtype=np.uint8)\n    mid = size // 2\n    mask[mid, 2:size-2] = 1\n    mask[2:size-2, mid] = 1\n    return mask\n\n\ndef create_minus_mask(size=9):\n    \"\"\"Create a minus-shaped binary mask\"\"\"\n    mask = np.zeros((size, size), dtype=np.uint8)\n    mid = size // 2\n    mask[mid, 2:size-2] = 1\n    return mask\n\n\n# ============================================================\n# MODULE 3: Mask Distribution Analysis\n# ============================================================\n\ndef mask_distribution(train_masks_dir, heatmap_size=(100, 100)):\n    \"\"\"Generate heatmap of forgery mask positions\"\"\"\n    if not os.path.exists(train_masks_dir):\n        return (0.5, 0.5), None\n\n    heatmap = np.zeros(heatmap_size, dtype=np.float32)\n    all_positions = []\n\n    for mask_file in os.listdir(train_masks_dir):\n        if not mask_file.endswith('.npy'):\n            continue\n\n        mask_path = os.path.join(train_masks_dir, mask_file)\n        try:\n            mask = np.load(mask_path)\n\n            if mask.ndim == 3:\n                mask = mask.squeeze(axis=0) if mask.shape[0] == 1 else mask\n                mask = mask.squeeze(axis=2) if mask.shape[-1] == 1 else mask\n\n            if mask.ndim != 2:\n                continue\n\n            y_coords, x_coords = np.where(mask > 0)\n            if len(y_coords) == 0:\n                continue\n\n            height, width = mask.shape\n            for y, x in zip(y_coords, x_coords):\n                norm_y, norm_x = y / height, x / width\n                heatmap_y = min(int(norm_y * heatmap_size[0]), heatmap_size[0] - 1)\n                heatmap_x = min(int(norm_x * heatmap_size[1]), heatmap_size[1] - 1)\n                heatmap[heatmap_y, heatmap_x] += 1\n                all_positions.append((norm_x, norm_y))\n\n        except Exception:\n            continue\n\n    if all_positions:\n        max_pos = np.unravel_index(np.argmax(heatmap), heatmap.shape)\n        max_norm = (max_pos[1] / heatmap_size[1], max_pos[0] / heatmap_size[0])\n        return max_norm, heatmap\n\n    return (0.5, 0.5), heatmap\n\n\ndef plot_heatmap(heatmap):\n    \"\"\"Visualize heatmap\"\"\"\n    plt.figure(figsize=(10, 8))\n    plt.imshow(heatmap, cmap='hot', interpolation='nearest')\n    plt.colorbar()\n    plt.title('Forgery Location Heatmap')\n    plt.xlabel('Normalized X')\n    plt.ylabel('Normalized Y')\n    plt.show()\n\n\n# ============================================================\n# MODULE 4: Submission Generation\n# ============================================================\n\ndef generate_submission(test_images_dir, sample_csv, hottest_pos, output_path='submission.csv'):\n    \"\"\"Generate submission CSV file with RLE or 'authentic' labels\"\"\"\n    sample_submission = pd.read_csv(sample_csv)\n    submission_data = []\n\n    for case_id in sample_submission['case_id']:\n        img_path = os.path.join(test_images_dir, f\"{case_id}.png\")\n\n        with Image.open(img_path) as img:\n            width, height = img.size\n\n        if np.random.random() < 0.01:\n            mask = np.zeros((height, width), dtype=np.uint8)\n            center_x = int(hottest_pos[0] * width)\n            center_y = int(hottest_pos[1] * height)\n            center_x = np.clip(center_x, 4, width - 5)\n            center_y = np.clip(center_y, 4, height - 5)\n\n            h = w = min(8, min(width, height) // 20)\n            mask[center_y - h//2:center_y + h//2, center_x - w//2:center_x + w//2] = 1\n\n            rle = rle_encode(mask)\n            annotation = json.dumps([int(x) for x in rle])\n        else:\n            annotation = 'authentic'\n\n        submission_data.append({'case_id': case_id, 'annotation': annotation})\n\n    submission = pd.DataFrame(submission_data)\n    submission.to_csv(output_path, index=False)\n    print(f\"✅ Submission saved to {output_path}\")\n\n\n# ============================================================\n# MAIN EXECUTION\n# ============================================================\n\nif __name__ == \"__main__\":\n    np.random.seed(52)\n\n    # Example masks\n    example = np.array([[1, 0], [1, 1]])\n    print(f\"Our example:\\n{example}\")\n    print(f\"RLE encoding: {rle_encode(example)}\")\n    visualize_mask(example, \"Example Mask\")\n\n    plus_mask = create_plus_mask()\n    print(f\"RLE Plus: {rle_encode(plus_mask)}\")\n    visualize_mask(plus_mask, \"Plus Mask\")\n\n    minus_mask = create_minus_mask()\n    print(f\"RLE Minus: {rle_encode(minus_mask)}\")\n    visualize_mask(minus_mask, \"Minus Mask\")\n\n    # Mask distribution and heatmap\n    train_masks_dir = '/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_masks'\n    hottest_pos, heatmap = mask_distribution(train_masks_dir)\n    if heatmap is not None:\n        plot_heatmap(heatmap)\n\n    # Generate submission\n    test_images_dir = '/kaggle/input/recodai-luc-scientific-image-forgery-detection/test_images'\n    sample_csv = '/kaggle/input/recodai-luc-scientific-image-forgery-detection/sample_submission.csv'\n    generate_submission(test_images_dir, sample_csv, hottest_pos)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-03T09:41:46.165706Z","iopub.execute_input":"2025-11-03T09:41:46.166067Z","iopub.status.idle":"2025-11-03T09:47:51.582336Z","shell.execute_reply.started":"2025-11-03T09:41:46.166029Z","shell.execute_reply":"2025-11-03T09:47:51.581095Z"}},"outputs":[],"execution_count":null}]}