{"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":14878066,"sourceType":"competition"},{"sourceId":14167480,"sourceType":"datasetVersion","datasetId":9030673},{"sourceId":4534,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":3326,"modelId":986},{"sourceId":686586,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":520737,"modelId":534998}],"dockerImageVersionId":31153,"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, #1a1f2c 0%, #2d3748 50%, #4a5568 100%);\n    border: 2px solid #63b3ed;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(99, 179, 237, 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(99, 179, 237, 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(99, 179, 237, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #63b3ed;\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(99, 179, 237, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    📊 Baseline Strategy: Understanding RLE & Simple Submission\n</h1>\n\n<div style=\"\n    background: rgba(99, 179, 237, 0.1);\n    border-left: 4px solid #63b3ed;\n    border-radius: 8px;\n    padding: 20px;\n    margin: 20px 0;\n    position: relative;\n    z-index: 1;\n\">\n    <h3 style=\"\n        color: #63b3ed;\n        margin-top: 0;\n        font-size: 1.3em;\n        display: flex;\n        align-items: center;\n        gap: 10px;\n    \">\n        🎯 What we'll do in this notebook:\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>🧪 Understand RLE metric with practical examples</li>\n        <li>🚀 Create a simple \"authentic-only\" submission</li>\n        <li>📈 Learn why this strategy works in some competitions</li>\n        <li>🎲 Test our baseline on the leaderboard(score 0.30 or 30% f1-score)</li>\n    </ul>\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: #63b3ed;\n        margin-top: 0;\n        font-size: 1.3em;\n        display: flex;\n        align-items: center;\n        gap: 10px;\n    \">\n        💡 Why \"authentic-only\" submission?\n    </h3>\n    <p style=\"color: #f1f5f9; font-size: 1.1em; line-height: 1.6;\">\n        <strong>Experienced competitors often start with this approach!</strong> In many datasets, \n        the majority of images don't contain any objects/forgeries. By submitting \"authentic\" for all images, \n        we get a baseline score that helps us understand the data distribution.\n    </p>\n    \n<div style=\"\n        background: rgba(99, 179, 237, 0.15);\n        border-radius: 8px;\n        padding: 15px;\n        margin: 15px 0;\n    \">\n        <h4 style=\"color: #63b3ed; margin-top: 0;\">When this strategy works well:</h4>\n        <ul style=\"color: #f1f5f9; line-height: 1.5;\">\n            <li>📊 <strong>Imbalanced datasets</strong> - when most images are truly \"authentic\"</li>\n            <li>⚡ <strong>Quick baseline</strong> - to test submission pipeline</li>\n            <li>📈 <strong>Metric understanding</strong> - see how the scoring system works</li>\n            <li>🔍 <strong>Data exploration</strong> - understand the competition dynamics</li>\n        </ul>\n</div>\n    \n<div style=\"\n        background: rgba(247, 127, 127, 0.15);\n        border-radius: 8px;\n        padding: 15px;\n        margin: 15px 0;\n    \">\n        <h4 style=\"color: #f77f7f; margin-top: 0;\">⚠️ Important note:</h4>\n        <p style=\"color: #f1f5f9; margin: 0;\">\n            This is just a <strong>starting point</strong>! While it gives us a quick baseline, \n            to actually compete we'll need to build proper segmentation models. But first, \n            let's make sure our submission pipeline works correctly!\n        </p>\n</div>\n</div>\n</div>","metadata":{}},{"cell_type":"code","source":"import os\nimport json\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:49.915513Z","iopub.execute_input":"2025-12-17T17:55:49.916032Z","iopub.status.idle":"2025-12-17T17:55:50.195292Z","shell.execute_reply.started":"2025-12-17T17:55:49.916009Z","shell.execute_reply":"2025-12-17T17:55:50.194754Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def rle_encode(mask):\n    \"\"\"\n    Convert binary mask to RLE string.\n    Returns: string like \"3 5 2 1\" meaning [3 zeros, 5 ones, 2 zeros, 1 one]\n    \"\"\"\n    pixels = mask.flatten()\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)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.196553Z","iopub.execute_input":"2025-12-17T17:55:50.197000Z","iopub.status.idle":"2025-12-17T17:55:50.201131Z","shell.execute_reply.started":"2025-12-17T17:55:50.196981Z","shell.execute_reply":"2025-12-17T17:55:50.200522Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize_mask(mask, title):\n    \"\"\"Visualize mask with pixel values and grid\"\"\"\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    # Add 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    # Show pixel values\n    for i in range(mask.shape[0]):\n        for j in range(mask.shape[1]):\n            plt.text(j, i, str(mask[i, j]), ha='center', va='center', \n                    color='blue' if mask[i, j] == 0 else 'white', fontweight='bold')\n    \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.201732Z","iopub.execute_input":"2025-12-17T17:55:50.201966Z","iopub.status.idle":"2025-12-17T17:55:50.216667Z","shell.execute_reply.started":"2025-12-17T17:55:50.201946Z","shell.execute_reply":"2025-12-17T17:55:50.216224Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"simple_mask = np.array([\n    [1, 0],\n    [1, 1]\n])\nprint(f\"Mask:\\n{simple_mask}\")\nprint(f\"Flattened: {simple_mask.flatten()}\")\nprint(f\"RLE: '{rle_encode(simple_mask)}'\")\nvisualize_mask(simple_mask, \"Simple 2x2 Mask\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.218345Z","iopub.execute_input":"2025-12-17T17:55:50.218612Z","iopub.status.idle":"2025-12-17T17:55:50.420281Z","shell.execute_reply.started":"2025-12-17T17:55:50.218588Z","shell.execute_reply":"2025-12-17T17:55:50.419579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plus_mask = np.zeros((9, 9), dtype=np.uint8)\nplus_mask[2:7, 4] = 1  # Vertical line\nplus_mask[4, 2:7] = 1  # Horizontal line\n\nprint(\"Mask visualization:\")\nfor i in range(9):\n    print(' '.join(map(str, plus_mask[i])))\n\nprint(f\"Flattened (first 20): {' '.join(map(str, plus_mask.flatten()[:20]))}...\")\nprint(f\"RLE: '{rle_encode(plus_mask)}'\")\nvisualize_mask(plus_mask, \"Plus Shape Mask\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.421722Z","iopub.execute_input":"2025-12-17T17:55:50.421964Z","iopub.status.idle":"2025-12-17T17:55:50.623379Z","shell.execute_reply.started":"2025-12-17T17:55:50.421946Z","shell.execute_reply":"2025-12-17T17:55:50.622819Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"minus_mask = np.zeros((9, 9), dtype=np.uint8)\nminus_mask[4, 2:7] = 1  # Horizontal line\n\nprint(\"Mask visualization:\")\nfor i in range(9):\n    print(' '.join(map(str, minus_mask[i])))\n\nprint(f\"RLE: '{rle_encode(minus_mask)}'\")\nvisualize_mask(minus_mask, \"Minus Shape Mask\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.624271Z","iopub.execute_input":"2025-12-17T17:55:50.624506Z","iopub.status.idle":"2025-12-17T17:55:50.871447Z","shell.execute_reply.started":"2025-12-17T17:55:50.624490Z","shell.execute_reply":"2025-12-17T17:55:50.870683Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_mask = np.array([[1, 1, 0, 0, 1, 0]])\nprint(f\"Test mask: {test_mask.flatten()}\")\n\n# Step by step explanation\npixels = test_mask.flatten()\nprint(f\"1. Flatten: {pixels}\")\n\npadded = np.concatenate([[0], pixels, [0]])\nprint(f\"2. Add borders: {padded}\")\n\nchanges = np.where(padded[1:] != padded[:-1])[0] + 1\nprint(f\"3. Find changes: {changes}\")\n\nruns = changes.copy()\nruns[1::2] -= runs[::2]\nprint(f\"4. Calculate lengths: {runs}\")\n\nresult = ' '.join(str(x) for x in runs)\nprint(f\"5. Final RLE: '{result}'\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.872240Z","iopub.execute_input":"2025-12-17T17:55:50.872484Z","iopub.status.idle":"2025-12-17T17:55:50.879164Z","shell.execute_reply.started":"2025-12-17T17:55:50.872467Z","shell.execute_reply":"2025-12-17T17:55:50.878377Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a1f2c 0%, #2d3748 50%, #4a5568 100%);\n    border: 2px solid #63b3ed;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(99, 179, 237, 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(99, 179, 237, 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(99, 179, 237, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #63b3ed;\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(99, 179, 237, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    Create sumission models 2 Dino and ensemlbing\n</h1>","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport json\nimport math\nimport torch\nimport numpy as np\nimport pandas as pd\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom PIL import Image\nfrom pathlib import Path\nfrom transformers import AutoImageProcessor, AutoModel\n\nclass CONFIG:\n    test_images_path = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/test_images\"\n    sample_sub_path = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/sample_submission.csv\"\n    model1_path = \"/kaggle/input/modelsbest309base/best_model.pth\"\n    model2_path = \"/kaggle/input/dinobestmodel/pytorch/default/1/dino197.pth\"\n    dino_path = \"/kaggle/input/dinov2/pytorch/base/1\"\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    img_size = 512\n    use_tta = True\n    ensemble_strategy = \"average\"\n    min_area = 150\n    min_confidence = 0.33","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:55:50.879999Z","iopub.execute_input":"2025-12-17T17:55:50.880243Z","iopub.status.idle":"2025-12-17T17:56:17.291961Z","shell.execute_reply.started":"2025-12-17T17:55:50.880224Z","shell.execute_reply":"2025-12-17T17:56:17.291411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Decoder(nn.Module):\n    \n    def __init__(self, in_ch=768, out_ch=1):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Conv2d(in_ch, 256, 3, padding=1), nn.ReLU(),\n            nn.Conv2d(256, 64, 3, padding=1), nn.ReLU(),\n            nn.Conv2d(64, out_ch, 1)\n        )\n    \n    def forward(self, f, size):\n        return self.net(F.interpolate(f, size=size, mode=\"bilinear\", align_corners=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:56:17.292713Z","iopub.execute_input":"2025-12-17T17:56:17.293228Z","iopub.status.idle":"2025-12-17T17:56:17.298427Z","shell.execute_reply.started":"2025-12-17T17:56:17.293198Z","shell.execute_reply":"2025-12-17T17:56:17.297738Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DinoSegmenter(nn.Module):\n    \n    def __init__(self, encoder, processor):\n        super().__init__()\n        self.encoder = encoder\n        self.processor = processor\n        self.seg_head = Decoder(768, 1)\n    \n    def forward_features(self, x):\n        imgs = (x*255).clamp(0, 255).byte().permute(0, 2, 3, 1).cpu().numpy()\n        inputs = self.processor(images=list(imgs), return_tensors=\"pt\").to(x.device)\n        \n        with torch.no_grad():\n            feats = self.encoder(**inputs).last_hidden_state\n        \n        B, N, C = feats.shape\n        fmap = feats[:, 1:, :].permute(0, 2, 1)\n        \n        s = int(math.sqrt(N-1))\n        fmap = fmap.reshape(B, C, s, s)\n        \n        return fmap\n    \n    def forward_seg(self, x):\n        fmap = self.forward_features(x)\n        return self.seg_head(fmap, (CONFIG.img_size, CONFIG.img_size))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:56:17.301189Z","iopub.execute_input":"2025-12-17T17:56:17.301601Z","iopub.status.idle":"2025-12-17T17:56:17.551731Z","shell.execute_reply.started":"2025-12-17T17:56:17.301575Z","shell.execute_reply":"2025-12-17T17:56:17.550899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Model(nn.Module):\n    \n    def __init__(self):\n        super().__init__()\n        self.encoder = nn.Sequential(\n            nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(),\n            nn.Conv2d(32, 32, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(),\n            nn.Conv2d(64, 64, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n        )\n        \n        self.decoder = nn.Sequential(\n            nn.Conv2d(64, 32, 3, padding=1), nn.ReLU(),\n            nn.Upsample(scale_factor=2, mode='bilinear'),\n            nn.Conv2d(32, 32, 3, padding=1), nn.ReLU(),\n            nn.Upsample(scale_factor=2, mode='bilinear'),\n            nn.Conv2d(32, 1, 1),\n        )\n    \n    def forward(self, x):\n        x = self.encoder(x)\n        x = self.decoder(x)\n        x = F.interpolate(x, size=(CONFIG.img_size, CONFIG.img_size), mode='bilinear', align_corners=False)\n        \n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:56:17.552360Z","iopub.execute_input":"2025-12-17T17:56:17.552644Z","iopub.status.idle":"2025-12-17T17:56:17.562843Z","shell.execute_reply.started":"2025-12-17T17:56:17.552619Z","shell.execute_reply":"2025-12-17T17:56:17.562194Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_model(model_path):\n    try:\n        checkpoint = torch.load(model_path, map_location=CONFIG.device)\n        \n        if isinstance(checkpoint, dict):\n            state_dict = None\n            if 'model_state_dict' in checkpoint:\n                state_dict = checkpoint['model_state_dict']\n            elif 'state_dict' in checkpoint:\n                state_dict = checkpoint['state_dict']\n            elif 'model' in checkpoint:\n                model = checkpoint['model']\n                if hasattr(model, 'eval'):\n                    model.eval()\n                return model.to(CONFIG.device)\n            else:\n                state_dict = checkpoint\n            \n            try:\n                processor = AutoImageProcessor.from_pretrained(CONFIG.dino_path, local_files_only=True)\n                encoder = AutoModel.from_pretrained(CONFIG.dino_path, local_files_only=True).eval().to(CONFIG.device)\n                model = DinoSegmenter(encoder, processor).to(CONFIG.device)\n                \n                if state_dict is not None:\n                    model.load_state_dict(state_dict, strict=False)\n                \n                model.eval()\n                return model\n            except:\n                try:\n                    model = Model().to(CONFIG.device)\n                    if state_dict is not None:\n                        model.load_state_dict(state_dict, strict=False)\n                    model.eval()\n                    return model\n                except:\n                    return None\n        \n        elif hasattr(checkpoint, 'eval'):\n            checkpoint.eval()\n            return checkpoint.to(CONFIG.device)\n        \n        return None\n    except Exception as e:\n        print(f\"Error {Path(model_path).name}: {e}\")\n        return None\n\ndef predict_with_tta(model, image_tensor):\n    predictions = []\n    \n    with torch.no_grad():\n        if hasattr(model, 'forward_seg'):\n            pred = torch.sigmoid(model.forward_seg(image_tensor))\n        else:\n            pred = torch.sigmoid(model(image_tensor))\n    \n    predictions.append(pred)\n    \n    with torch.no_grad():\n        if hasattr(model, 'forward_seg'):\n            pred = torch.sigmoid(model.forward_seg(torch.flip(image_tensor, dims=[3])))\n        else:\n            pred = torch.sigmoid(model(torch.flip(image_tensor, dims=[3])))\n    \n    predictions.append(torch.flip(pred, dims=[3]))\n    \n    with torch.no_grad():\n        if hasattr(model, 'forward_seg'):\n            pred = torch.sigmoid(model.forward_seg(torch.flip(image_tensor, dims=[2])))\n        else:\n            pred = torch.sigmoid(model(torch.flip(image_tensor, dims=[2])))\n    \n    predictions.append(torch.flip(pred, dims=[2]))\n    \n    if CONFIG.use_tta:\n        with torch.no_grad():\n            if hasattr(model, 'forward_seg'):\n                pred = torch.sigmoid(model.forward_seg(torch.rot90(image_tensor, 1, [2, 3])))\n            else:\n                pred = torch.sigmoid(model(torch.rot90(image_tensor, 1, [2, 3])))\n        \n        predictions.append(torch.rot90(pred, -1, [2, 3]))\n        \n        return torch.stack(predictions).mean(0)[0, 0].detach().cpu().numpy()\n    else:\n        return predictions[0][0, 0].detach().cpu().numpy()\n\ndef postprocess(pred, original_size):\n    pred = cv2.GaussianBlur(pred, (3, 3), 0)\n    mean_val = np.mean(pred)\n    std_val = np.std(pred)\n    thr = mean_val + 0.3 * std_val\n    mask = (pred > thr).astype(np.uint8)\n    \n    if mask.sum() > 0:\n        mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((5, 5), np.uint8))\n        mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((3, 3), np.uint8))\n    \n    mask = cv2.resize(mask, original_size, interpolation=cv2.INTER_NEAREST)\n    return mask\n\ndef rle_encode(mask):\n    pixels = mask.T.flatten()\n    dots = np.where(pixels == 1)[0]\n    \n    if len(dots) == 0:\n        return \"authentic\"\n    \n    run_lengths = []\n    prev = -2\n    \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    \n    return json.dumps([int(x) for x in run_lengths])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:56:17.563540Z","iopub.execute_input":"2025-12-17T17:56:17.563827Z","iopub.status.idle":"2025-12-17T17:56:17.587104Z","shell.execute_reply.started":"2025-12-17T17:56:17.563811Z","shell.execute_reply":"2025-12-17T17:56:17.586323Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model1 = load_model(CONFIG.model1_path)\nmodel2 = load_model(CONFIG.model2_path)\n\nmodels = {}\nif model1:\n    models['model1'] = model1\n    print(f\"model 1 success!\")\nif model2:\n    models['model2'] = model2\n    print(f\"model 2 success!\")\n\n\npredictions = []\nimage_files = sorted([f for f in os.listdir(CONFIG.test_images_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp'))])\n\nfor image_name in image_files:\n    image_path = Path(CONFIG.test_images_path) / image_name\n    image = Image.open(image_path).convert(\"RGB\")\n    \n    image_array = np.array(image.resize((CONFIG.img_size, CONFIG.img_size)), np.float32) / 255\n    image_tensor = torch.from_numpy(image_array).permute(2, 0, 1)[None].to(CONFIG.device)\n    \n    ensemble_preds = []\n    for model in models.values():\n        pred = predict_with_tta(model, image_tensor)\n        ensemble_preds.append(pred)\n    \n    if CONFIG.ensemble_strategy == \"max\" and ensemble_preds:\n        final_pred = np.max(ensemble_preds, axis=0)\n    else:\n        final_pred = np.mean(ensemble_preds, axis=0) if ensemble_preds else np.zeros((CONFIG.img_size, CONFIG.img_size))\n        \n    mask = postprocess(final_pred, image.size)\n    area = int(mask.sum())\n    \n    if area > 0:\n        mask_resized = cv2.resize(mask, (CONFIG.img_size, CONFIG.img_size), interpolation=cv2.INTER_NEAREST)\n        mean_inside = float(final_pred[mask_resized == 1].mean()) if (mask_resized == 1).any() else 0.0\n    else:\n        mean_inside = 0.0\n        \n    if area < CONFIG.min_area or mean_inside < CONFIG.min_confidence:\n        annotation = \"authentic\"\n    else:\n        annotation = rle_encode(mask)\n        \n    predictions.append({\n        \"case_id\": Path(image_name).stem,\n        \"annotation\": annotation\n    })\n    \npredictions_df = pd.DataFrame(predictions)\npredictions_df[\"case_id\"] = predictions_df[\"case_id\"].astype(str)\n    \nsubmission = pd.read_csv(CONFIG.sample_sub_path)\nsubmission[\"case_id\"] = submission[\"case_id\"].astype(str)\nsubmission = submission[[\"case_id\"]].merge(predictions_df[[\"case_id\", \"annotation\"]], on=\"case_id\", how=\"left\")\n\nsubmission[\"annotation\"] = submission[\"annotation\"].fillna(\"authentic\")\nsubmission[[\"case_id\", \"annotation\"]].to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-17T17:56:17.587935Z","iopub.execute_input":"2025-12-17T17:56:17.588173Z","iopub.status.idle":"2025-12-17T17:56:33.810028Z","shell.execute_reply.started":"2025-12-17T17:56:17.588150Z","shell.execute_reply":"2025-12-17T17:56:33.809183Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"\n    background: linear-gradient(135deg, #1a1f2c 0%, #2d3748 50%, #4a5568 100%);\n    border: 2px solid #63b3ed;\n    border-radius: 15px;\n    padding: 25px;\n    margin: 20px 0;\n    box-shadow: 0 0 30px rgba(99, 179, 237, 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(99, 179, 237, 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(99, 179, 237, 0.2) 0%, transparent 70%);\n    border-radius: 50%;\n\"></div>\n\n<h1 style=\"\n    color: #63b3ed;\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(99, 179, 237, 0.6);\n    position: relative;\n    z-index: 1;\n\">\n    If i have mistake write pls in comments\n</h1>","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}