{"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":"none","dataSources":[{"sourceId":113558,"databundleVersionId":14174843,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":13691271,"sourceType":"datasetVersion","datasetId":8698359},{"sourceId":275483013,"sourceType":"kernelVersion"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Visualizing the training results.\n## Model Architecture\n- https://www.kaggle.com/code/manojkumars00/intra-image-similarity-with-self-correlation-arch/output\n\n## Baseline Training\n- 15 Epochs\n- Data Augumentation\n- - Horizantal & Vertical Flip\n- Metrics\n- - IOU : 0.5148\n  - Dice Coeff : 0.6797\n- https://www.kaggle.com/code/manojkumars00/intra-image-similarity-learning/notebook\n\n\n## Further Steps Planned\n- Synthetic Data Generation\n- Trying out Instance Segmentation ","metadata":{}},{"cell_type":"code","source":"from intra_image_similarity_with_self_correlation_arch import CmfdInstanceModel, SematicCmfdModel\nimport torch\n\nbackbone_name = \"nvidia/mit-b2\"\n\nsematic_encoder_model = SematicCmfdModel(backbone_name=backbone_name, apply_softmax=False)\nmodel = CmfdInstanceModel(sematic_encoder_model, backbone_name=backbone_name, topK=8, n_instances=5, decoder_hidden_dim=128, num_decoder_layers=3, decoder_mask_embed_dim=512, decoder_nheads=8)    \nmodel_image_processor = model.image_processor\n\nckpt_path = \"/kaggle/input/self-corr-image-similarity-sematic-15-epoch/checkpoints/best.pt\"\ncheckpoint = torch.load(ckpt_path, map_location=\"cpu\")\nstate_dict = checkpoint[\"model\"]\nmissing, unexpected = model.load_state_dict(state_dict, strict=False)\nprint(\"Missing keys:\", missing)\nprint(\"Unexpected keys:\", unexpected)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:07:43.207867Z","iopub.execute_input":"2025-11-11T09:07:43.208108Z","iopub.status.idle":"2025-11-11T09:08:38.301730Z","shell.execute_reply.started":"2025-11-11T09:07:43.208088Z","shell.execute_reply":"2025-11-11T09:08:38.300537Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom types import SimpleNamespace","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.302997Z","iopub.execute_input":"2025-11-11T09:08:38.303807Z","iopub.status.idle":"2025-11-11T09:08:38.308344Z","shell.execute_reply.started":"2025-11-11T09:08:38.303775Z","shell.execute_reply":"2025-11-11T09:08:38.307374Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom PIL import Image\nimport numpy as np\n\nimport matplotlib.pyplot as plt\nimport torch.nn.functional as F\n\nimport os\nimport random\nfrom skimage import measure\n\nimport cv2\nfrom shapely.geometry import Polygon","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.310397Z","iopub.execute_input":"2025-11-11T09:08:38.310713Z","iopub.status.idle":"2025-11-11T09:08:38.511066Z","shell.execute_reply.started":"2025-11-11T09:08:38.310691Z","shell.execute_reply":"2025-11-11T09:08:38.509956Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_image(image_path):\n    image = Image.open(image_path).convert('RGB')\n    image = np.array(image)\n    return image\n\ndef load_mask(mask_path):\n    mask = np.load(mask_path)\n    return mask","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.511986Z","iopub.execute_input":"2025-11-11T09:08:38.512676Z","iopub.status.idle":"2025-11-11T09:08:38.517807Z","shell.execute_reply.started":"2025-11-11T09:08:38.512641Z","shell.execute_reply":"2025-11-11T09:08:38.516918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def apply_model_pre_processor(image):\n    enc = model_image_processor(\n        image,\n        return_tensors=\"pt\"\n    )\n    return enc[\"pixel_values\"]\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.518731Z","iopub.execute_input":"2025-11-11T09:08:38.519279Z","iopub.status.idle":"2025-11-11T09:08:38.536596Z","shell.execute_reply.started":"2025-11-11T09:08:38.519257Z","shell.execute_reply":"2025-11-11T09:08:38.535687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_sample(image, mask, pred_mask):\n    # Read images\n    input_img = image\n    target_mask = mask\n    mask_img = pred_mask\n\n    # assume mask_img[0] is the binary mask\n    pred_mask = (pred_mask > 0.5).astype(np.uint8)\n\n    # find contours (boundaries) of forged areas\n    contours = measure.find_contours(pred_mask, level=0.5)\n\n    # Plot side-by-side\n    plt.figure(figsize=(12, 4))\n\n    plt.subplot(1, 3, 1)\n    plt.imshow(input_img)\n    plt.title(\"Model Input Image\")\n    plt.axis('off')\n\n    plt.subplot(1, 3, 2)\n    plt.imshow(target_mask)\n    plt.title(\"Target Mask With Predited Dot Overlay\")\n    plt.axis('off')\n\n    # draw contours over forged image\n    for contour in contours:\n        poly = Polygon(contour[:, ::-1]) \n        buffer_distance = 7  # pixels or coordinate units\n        buffered_poly = poly.buffer(buffer_distance)\n        \n        # Convert back to array for plotting\n        buffered_contour = np.array(buffered_poly.exterior.coords)\n        # plt.plot(contour[:, 1], contour[:, 0], color='red', linewidth=1)\n        plt.plot(buffered_contour[:, 0], buffered_contour[:, 1], color='red', linestyle='--')\n\n    plt.subplot(1, 3, 3)\n    plt.imshow(pred_mask, cmap='gray')\n    plt.title(\"Predicted Mask\")\n    plt.axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.537495Z","iopub.execute_input":"2025-11-11T09:08:38.537867Z","iopub.status.idle":"2025-11-11T09:08:38.550495Z","shell.execute_reply.started":"2025-11-11T09:08:38.537837Z","shell.execute_reply":"2025-11-11T09:08:38.549610Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"authentic_images_dir = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/authentic\"\nforged_images_dir = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/forged\"\nforged_images_mask_dir = \"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_masks\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.551525Z","iopub.execute_input":"2025-11-11T09:08:38.551831Z","iopub.status.idle":"2025-11-11T09:08:38.571969Z","shell.execute_reply.started":"2025-11-11T09:08:38.551805Z","shell.execute_reply":"2025-11-11T09:08:38.570921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"authentic_images_files = sorted(os.listdir(authentic_images_dir))\nforged_images_files = sorted(os.listdir(forged_images_dir))\nforged_images_mask_files = sorted(os.listdir(forged_images_mask_dir))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:08:38.573088Z","iopub.execute_input":"2025-11-11T09:08:38.573396Z","iopub.status.idle":"2025-11-11T09:08:38.699552Z","shell.execute_reply.started":"2025-11-11T09:08:38.573362Z","shell.execute_reply":"2025-11-11T09:08:38.698466Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Checking out the how the model learned on training data","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport torch\nimport torch.nn.functional as F\n\nSHOW_BINARY = False   # True -> show thresholded masks; False -> show probability heatmaps\nTHRESH = 0.5\n\nfor i in range(25):\n    # Load image and mask\n    image = load_image(f\"{forged_images_dir}/{forged_images_files[i]}\")\n    gt_mask = load_mask(f\"{forged_images_mask_dir}/{forged_images_mask_files[i]}\")  # shape [5, H, W]\n\n    # Preprocess\n    processed_image = apply_model_pre_processor(image)\n\n    # Run inference\n    with torch.no_grad():\n        out = model(processed_image)              # model output (dict or tensor)\n    pred_logits = out[\"mask_logits\"] if isinstance(out, dict) else out  # [1, 5, 512, 512]\n\n    class_probs = F.softmax(out[\"class_logits\"], dim=2)       # [1, 5, num_classes]\n    max_probs, pred_classes = torch.max(class_probs, dim=2)  # both [1, 5]\n    threshold = 0.6\n    \n    confidence_mask = (max_probs >= threshold).float()\n    confidence_mask = confidence_mask.unsqueeze(-1).unsqueeze(-1)\n\n    pred_logits = pred_logits*confidence_mask\n\n\n    # Elementwise sigmoid per instance/channel\n    prob_masks = torch.sigmoid(pred_logits)       # [1, 5, 512, 512]\n\n    # Resize predictions to original size\n    if SHOW_BINARY:\n        bin_masks = (prob_masks > THRESH).float()\n        resized = F.interpolate(bin_masks, size=(image.shape[0], image.shape[1]), mode=\"nearest\")\n        vis_masks = resized.squeeze(0).cpu().numpy()  # [5, H, W]\n    else:\n        resized = F.interpolate(prob_masks, size=(image.shape[0], image.shape[1]), mode=\"bilinear\", align_corners=False)\n        vis_masks = resized.squeeze(0).cpu().numpy()  # [5, H, W]\n\n    # print(\"Pred mask shape:\", vis_masks.shape)\n    # print(\"GT mask shape:\", gt_mask.shape)\n\n    # Plot: Original image + 5 predicted masks + 5 ground truth masks\n    fig, axes = plt.subplots(2, 6, figsize=(20, 8))\n    \n    # Row 1: Original + Predictions\n    axes[0, 0].imshow(image)\n    axes[0, 0].set_title(\"Original Image\")\n    axes[0, 0].axis(\"off\")\n\n    for j in range(5):\n        axes[0, j + 1].imshow(vis_masks[j], cmap=\"viridis\", vmin=0, vmax=1)\n        axes[0, j + 1].set_title(f\"Pred Mask {j+1}\")\n        axes[0, j + 1].axis(\"off\")\n\n    # Row 2: Ground Truth\n    axes[1, 0].text(0.5, 0.5, \"Ground Truth Masks\", ha='center', va='center', fontsize=14)\n    axes[1, 0].axis(\"off\")\n    for j in range(gt_mask.shape[0]):\n        axes[1, j + 1].imshow(gt_mask[j], cmap=\"viridis\", vmin=0, vmax=1)\n        axes[1, j + 1].set_title(f\"GT Mask {j+1}\")\n        axes[1, j + 1].axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T09:26:12.822957Z","iopub.execute_input":"2025-11-11T09:26:12.823267Z","iopub.status.idle":"2025-11-11T09:28:40.433785Z","shell.execute_reply.started":"2025-11-11T09:26:12.823245Z","shell.execute_reply":"2025-11-11T09:28:40.432792Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}