{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.8.0"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":113558,"databundleVersionId":14456136,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## 1. Setup & Imports\n\nInstall and import required libraries.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, classification_report\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Set random seeds\nnp.random.seed(42)\ntorch.manual_seed(42)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f'Using device: {device}')","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Load Dataset\n\nLoading dataset: **physionet-ecg-images**\n\nCompetition: `recodai-luc-scientific-image-forgery-detection`","metadata":{}},{"cell_type":"code","source":"# Competition Data Loading\nfrom pathlib import Path\nimport pandas as pd\nimport os\n\n# Define data path\nDATA_PATH = Path('/kaggle/input/recodai-luc-scientific-image-forgery-detection')\nprint(f\"📁 Data path: {DATA_PATH}\")\nprint(f\"📁 Path exists: {DATA_PATH.exists()}\")\n\n# List all files in data directory\nif DATA_PATH.exists():\n    all_files = list(DATA_PATH.rglob('*'))\n    print(f\"\\n📊 Found {len(all_files)} total files/folders\")\n    \n    # Show top-level structure\n    top_level = [f.name for f in DATA_PATH.iterdir()]\n    print(f\"📂 Top-level contents: {top_level}\")\n    \n    # Try to load common files\n    try:\n        if (DATA_PATH / 'train.csv').exists():\n            train_df = pd.read_csv(DATA_PATH / 'train.csv')\n            print(f\"\\n✅ Loaded train.csv: {train_df.shape}\")\n            print(f\"Columns: {train_df.columns.tolist()}\")\n        else:\n            print(\"⚠ train.csv not found\")\n    except Exception as e:\n        print(f\"✗ Error loading train.csv: {e}\")\n    \n    try:\n        if (DATA_PATH / 'test.csv').exists():\n            test_df = pd.read_csv(DATA_PATH / 'test.csv')\n            print(f\"\\n✅ Loaded test.csv: {test_df.shape}\")\n            print(f\"Columns: {test_df.columns.tolist()}\")\n        else:\n            print(\"⚠ test.csv not found\")\n    except Exception as e:\n        print(f\"✗ Error loading test.csv: {e}\")\nelse:\n    print(f\"❌ Data path does not exist: {DATA_PATH}\")\n    print(\"\\n💡 Make sure competition is added to notebook metadata!\")\n","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Exploratory Data Analysis\n\n**Analyzing the competition data structure**","metadata":{}},{"cell_type":"code","source":"# Exploratory Data Analysis\ntry:\n    print('🔧 === EXPLORATORY DATA ANALYSIS ===\\n')\n    \n    import matplotlib.pyplot as plt\n    import seaborn as sns\n    import numpy as np\n    import pandas as pd\n    import os\n    from pathlib import Path\n\n    # Check train_df and test_df existence\n    if 'train_df' not in locals():\n        raise ValueError(\"train_df is not loaded.\")\n    if 'test_df' not in locals():\n        raise ValueError(\"test_df is not loaded.\")\n    \n    # 1. Basic Info\n    print(\"📊 Train DataFrame shape:\", train_df.shape)\n    print(\"📊 Test DataFrame shape:\", test_df.shape)\n    print(\"\\n📝 Train columns:\", train_df.columns.tolist())\n    print(\"📝 Test columns:\", test_df.columns.tolist())\n    \n    print(\"\\n🔍 Train DataFrame info:\")\n    train_df.info()\n    print(\"\\n🔍 Test DataFrame info:\")\n    test_df.info()\n    \n    print(\"\\n📈 Train DataFrame describe:\")\n    display(train_df.describe(include='all').T)\n    print(\"\\n📈 Test DataFrame describe:\")\n    display(test_df.describe(include='all').T)\n    \n    # 2. Check for missing values\n    print(\"\\n❓ Missing values in train_df:\")\n    print(train_df.isnull().sum())\n    print(\"\\n❓ Missing values in test_df:\")\n    print(test_df.isnull().sum())\n    \n    # 3. Distribution of target variable (if present)\n    target_col = None\n    for col in ['label', 'target', 'class', 'is_forgery']:\n        if col in train_df.columns:\n            target_col = col\n            break\n\n    if target_col:\n        print(f\"\\n🎯 Target column detected: '{target_col}'\")\n        print(train_df[target_col].value_counts())\n        plt.figure(figsize=(6,3))\n        sns.countplot(x=target_col, data=train_df)\n        plt.title(f\"Distribution of Target: {target_col}\")\n        plt.show()\n    else:\n        print(\"\\n⚠ No obvious target column found in train_df.\")\n\n    # 4. Check for image columns and sample images\n    image_col = None\n    for col in ['image', 'img_path', 'file_name', 'filename', 'image_path']:\n        if col in train_df.columns:\n            image_col = col\n            break\n\n    if image_col:\n        print(f\"\\n🖼️ Image column detected: '{image_col}'\")\n        # Show a few sample images from train and test\n        from PIL import Image\n        sample_train = train_df[image_col].sample(min(5, len(train_df)), random_state=42)\n        print(\"\\nShowing sample images from train set:\")\n        fig, axes = plt.subplots(1, len(sample_train), figsize=(15,3))\n        for ax, img_name in zip(axes, sample_train):\n            img_path = DATA_PATH / img_name\n            if img_path.exists():\n                img = Image.open(img_path)\n                ax.imshow(img)\n                ax.set_title(os.path.basename(img_name))\n                ax.axis('off')\n            else:\n                ax.set_title(f\"Not found:\\n{img_name}\")\n                ax.axis('off')\n        plt.tight_layout()\n        plt.show()\n    else:\n        print(\"\\n⚠ No image path column found in train_df.\")\n\n    # 5. Image file stats (dimensions, formats)\n    if image_col:\n        print(\"\\n📏 Gathering image file statistics (train set)...\")\n        img_shapes = []\n        img_modes = []\n        img_formats = []\n        sample_paths = train_df[image_col].sample(min(100, len(train_df)), random_state=42)\n        for img_name in sample_paths:\n            img_path = DATA_PATH / img_name\n            if img_path.exists():\n                try:\n                    with Image.open(img_path) as img:\n                        img_shapes.append(img.size)\n                        img_modes.append(img.mode)\n                        img_formats.append(img.format)\n                except Exception as e:\n                    img_shapes.append(None)\n                    img_modes.append(None)\n                    img_formats.append(None)\n        if img_shapes:\n            widths, heights = zip(*[s for s in img_shapes if s is not None])\n            plt.figure(figsize=(6,3))\n            sns.histplot(widths, bins=20, kde=True, color='skyblue', label='Width')\n            sns.histplot(heights, bins=20, kde=True, color='salmon', label='Height')\n            plt.legend()\n            plt.title(\"Image Width/Height Distribution (sample)\")\n            plt.show()\n            print(\"Image modes (sample):\", pd.Series(img_modes).value_counts())\n            print(\"Image formats (sample):\", pd.Series(img_formats).value_counts())\n        else:\n            print(\"⚠ No valid images found for stats.\")\n    else:\n        print(\"\\n⚠ Skipping image file stats (no image column).\")\n    \n    # 6. Correlation matrix for numeric columns\n    num_cols = train_df.select_dtypes(include=[np.number]).columns\n    if len(num_cols) > 1:\n        print(\"\\n🔗 Correlation matrix (train set):\")\n        corr = train_df[num_cols].corr()\n        plt.figure(figsize=(8,6))\n        sns.heatmap(corr, annot=True, fmt=\".2f\", cmap='coolwarm')\n        plt.title(\"Numeric Feature Correlation (train)\")\n        plt.show()\n    else:\n        print(\"\\n⚠ Not enough numeric columns for correlation matrix.\")\n\n    print('\\n✅ Exploratory Data Analysis complete!')\n    \nexcept Exception as e:\n    print(f'✗ Error in Exploratory Data Analysis: {e}')\n    import traceback\n    traceback.print_exc()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Data Preprocessing\n\n**Competition:** recodai-luc-scientific-image-forgery-detection\n\n**Note:** Following research-based implementation strategy","metadata":{}},{"cell_type":"code","source":"# Data Preprocessing\ntry:\n    print('🔧 === DATA PREPROCESSING ===\\n')\n    \n    # --- Configuration ---\n    IMG_SIZE = (256, 256)  # Standard size for forgery detection[1][3]\n    IMAGE_COL = 'image_id' if 'image_id' in train_df.columns else train_df.columns[0]\n    \n    # --- Helper Functions ---\n    from PIL import Image\n    import numpy as np\n\n    def load_and_resize_image(img_path, size=IMG_SIZE):\n        try:\n            with Image.open(img_path) as img:\n                img = img.convert('RGB')\n                img = img.resize(size)\n                return np.array(img)\n        except Exception as e:\n            print(f'✗ Error loading image {img_path}: {e}')\n            return None\n\n    def zero_one_range(img_arr):\n        return img_arr.astype(np.float32) / 255.0  # Normalize to [0,1][2]\n\n    def to_grayscale(img_arr):\n        return np.mean(img_arr, axis=2).astype(np.float32) if img_arr.ndim == 3 else img_arr\n\n    def normalize(img_arr):\n        mean = np.mean(img_arr)\n        std = np.std(img_arr)\n        return (img_arr - mean) / (std + 1e-8)\n\n    # --- Preprocessing Pipeline ---\n    def preprocess_image(img_path):\n        img = load_and_resize_image(img_path)\n        if img is None:\n            return None\n        img = zero_one_range(img)\n        img_gray = to_grayscale(img)\n        img_norm = normalize(img_gray)\n        return img_norm\n\n    # --- Apply Preprocessing to Train/Test Sets ---\n    print('Loading and preprocessing train images...')\n    train_img_paths = [DATA_PATH / fname for fname in train_df[IMAGE_COL]]\n    train_imgs = []\n    for img_path in train_img_paths:\n        img_arr = preprocess_image(img_path)\n        if img_arr is not None:\n            train_imgs.append(img_arr)\n    print(f'Processed {len(train_imgs)} train images.')\n\n    print('Loading and preprocessing test images...')\n    test_img_paths = [DATA_PATH / fname for fname in test_df[IMAGE_COL]]\n    test_imgs = []\n    for img_path in test_img_paths:\n        img_arr = preprocess_image(img_path)\n        if img_arr is not None:\n            test_imgs.append(img_arr)\n    print(f'Processed {len(test_imgs)} test images.')\n\n    # --- Visualize Sample Preprocessed Images ---\n    import matplotlib.pyplot as plt\n    if train_imgs:\n        plt.figure(figsize=(12, 4))\n        for i in range(3):\n            plt.subplot(1, 3, i+1)\n            plt.imshow(train_imgs[i], cmap='gray')\n            plt.title(f'Train Sample {i+1}')\n            plt.axis('off')\n        plt.suptitle('Sample Preprocessed Train Images')\n        plt.show()\n    else:\n        print('⚠ No train images to display.')\n\n    if test_imgs:\n        plt.figure(figsize=(12, 4))\n        for i in range(3):\n            plt.subplot(1, 3, i+1)\n            plt.imshow(test_imgs[i], cmap='gray')\n            plt.title(f'Test Sample {i+1}')\n            plt.axis('off')\n        plt.suptitle('Sample Preprocessed Test Images')\n        plt.show()\n    else:\n        print('⚠ No test images to display.')\n\n    # --- Store Preprocessed Data for Downstream Tasks ---\n    train_df['preprocessed_img'] = [img for img in train_imgs]\n    test_df['preprocessed_img'] = [img for img in test_imgs]\n\n    print('✅ Data Preprocessing complete!')\n    \nexcept Exception as e:\n    print(f'✗ Error in Data Preprocessing: {e}')\n    import traceback\n    traceback.print_exc()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Model Architecture\n\n**Approach:** Neural network baseline","metadata":{}},{"cell_type":"code","source":"# Model Architecture\ntry:\n    print('🔧 === MODEL ARCHITECTURE ===\\n')\n    import torch\n    import torch.nn as nn\n    import torch.nn.functional as F\n    import torchvision.models as models\n    import matplotlib.pyplot as plt\n\n    # --- Modular Pipeline Components ---\n\n    class GridDetector(nn.Module):\n        def __init__(self, config):\n            super().__init__()\n            # Simple CNN for grid detection (placeholder, can be replaced with advanced model)\n            self.conv = nn.Sequential(\n                nn.Conv2d(1, 16, 3, padding=1),\n                nn.ReLU(),\n                nn.MaxPool2d(2),\n                nn.Conv2d(16, 32, 3, padding=1),\n                nn.ReLU(),\n                nn.MaxPool2d(2)\n            )\n            self.fc = nn.Linear(32 * 64 * 64, 2)  # Example output: grid presence, orientation\n\n        def forward(self, x):\n            x = self.conv(x)\n            x = x.view(x.size(0), -1)\n            return self.fc(x)\n\n        def detect(self, img_tensor):\n            self.eval()\n            with torch.no_grad():\n                out = self.forward(img_tensor.unsqueeze(0).to(device))\n            # Dummy grid info for demonstration\n            grid_info = {'present': bool(out.argmax().item()), 'orientation': 'horizontal'}\n            return grid_info\n\n    class LeadSegmenter(nn.Module):\n        def __init__(self, config):\n            super().__init__()\n            # U-Net style encoder-decoder for lead segmentation\n            self.encoder = nn.Sequential(\n                nn.Conv2d(1, 16, 3, padding=1),\n                nn.ReLU(),\n                nn.MaxPool2d(2),\n                nn.Conv2d(16, 32, 3, padding=1),\n                nn.ReLU(),\n                nn.MaxPool2d(2)\n            )\n            self.decoder = nn.Sequential(\n                nn.ConvTranspose2d(32, 16, 2, stride=2),\n                nn.ReLU(),\n                nn.ConvTranspose2d(16, 1, 2, stride=2),\n                nn.Sigmoid()\n            )\n\n        def forward(self, x):\n            x = self.encoder(x)\n            x = self.decoder(x)\n            return x\n\n        def segment(self, img_tensor, grid_info):\n            self.eval()\n            with torch.no_grad():\n                mask = self.forward(img_tensor.unsqueeze(0).to(device))\n            # For demonstration, split into 2 leads by cropping\n            h = img_tensor.shape[-2]\n            lead1 = img_tensor[..., :h//2, :]\n            lead2 = img_tensor[..., h//2:, :]\n            return [lead1, lead2]\n\n    class WaveformSegmenter(nn.Module):\n        def __init__(self, config):\n            super().__init__()\n            # Simple CNN for waveform segmentation\n            self.conv = nn.Sequential(\n                nn.Conv2d(1, 8, 3, padding=1),\n                nn.ReLU(),\n                nn.MaxPool2d(2),\n                nn.Conv2d(8, 1, 3, padding=1),\n                nn.Sigmoid()\n            )\n\n        def forward(self, x):\n            return self.conv(x)\n\n        def segment(self, lead_tensor):\n            self.eval()\n            with torch.no_grad():\n                mask = self.forward(lead_tensor.unsqueeze(0).to(device))\n            return mask.squeeze(0).cpu()\n\n    class SignalExtractor:\n        def __init__(self, config):\n            pass\n\n        def extract(self, mask, grid_info):\n            # Dummy signal extraction: mean pixel value per column\n            signal = mask.squeeze().mean(dim=0).cpu().numpy()\n            return signal\n\n    class PostProcessor:\n        def __init__(self, config):\n            pass\n\n        def calibrate(self, signal, grid_info):\n            # Dummy calibration: normalize to [0, 1]\n            signal = (signal - signal.min()) / (signal.max() - signal.min() + 1e-8)\n            return signal\n\n    class ECGDigitizationPipeline:\n        def __init__(self, config):\n            self.grid_detector = GridDetector(config).to(device)\n            self.lead_segmenter = LeadSegmenter(config).to(device)\n            self.waveform_segmenter = WaveformSegmenter(config).to(device)\n            self.signal_extractor = SignalExtractor(config)\n            self.postprocessor = PostProcessor(config)\n\n        def read_image(self, img_path):\n            import cv2\n            img = cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE)\n            img = cv2.resize(img, (256, 256))\n            img_tensor = torch.tensor(img, dtype=torch.float32).unsqueeze(0) / 255.0\n            return img_tensor\n\n        def process(self, img_path):\n            img_tensor = self.read_image(img_path)\n            grid_info = self.grid_detector.detect(img_tensor)\n            leads = self.lead_segmenter.segment(img_tensor, grid_info)\n            masks = [self.waveform_segmenter.segment(lead.unsqueeze(0)) for lead in leads]\n            signals = [self.signal_extractor.extract(mask, grid_info) for mask in masks]\n            processed_signals = [self.postprocessor.calibrate(sig, grid_info) for sig in signals]\n            return processed_signals\n\n    # --- Example usage on preprocessed images ---\n    config = {'input_size': 256}\n    pipeline = ECGDigitizationPipeline(config)\n\n    # Visualize pipeline output for a few train images\n    if 'train_df' in locals() and not train_df.empty and 'preprocessed_img' in train_df.columns:\n        print('Running pipeline on sample train images...')\n        sample_imgs = train_df['preprocessed_img'][:3]\n        fig, axes = plt.subplots(len(sample_imgs), 2, figsize=(10, 3*len(sample_imgs)))\n        for i, img_arr in enumerate(sample_imgs):\n            # Save temp image for pipeline (simulate file input)\n            import cv2, tempfile\n            with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:\n                cv2.imwrite(tmp.name, (img_arr * 255).astype('uint8'))\n                signals = pipeline.process(tmp.name)\n            axes[i, 0].imshow(img_arr, cmap='gray')\n            axes[i, 0].set_title(f'Preprocessed Image {i+1}')\n            axes[i, 0].axis('off')\n            for sig in signals:\n                axes[i, 1].plot(sig)\n            axes[i, 1].set_title(f'Extracted Signals {i+1}')\n            axes[i, 1].set_xlabel('Time')\n            axes[i, 1].set_ylabel('Normalized Amplitude')\n        plt.tight_layout()\n        plt.show()\n    else:\n        print('⚠ No preprocessed train images available for pipeline demo.')\n\n    print('✅ Model Architecture complete!')\n\nexcept Exception as e:\n    print(f'✗ Error in Model Architecture: {e}')\n    import traceback\n    traceback.print_exc()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Implementation & Next Steps\n\n**Note:** This section provides guidance, not complete code. Actual implementation depends on competition task.","metadata":{}},{"cell_type":"code","source":"print('📋 === IMPLEMENTATION GUIDE ===\\n')\n\nprint('Competition task determines implementation approach\\n')\nprint('Possible approaches:')\nprint('  - Classification: Train classifier, predict labels')\nprint('  - Regression: Train regressor, predict values')\nprint('  - Generation: Generate required outputs')\nprint('  - Processing: Transform/extract data')\n\nprint('\\n⚠️ TODO: Implement competition-specific solution')\n","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7. Submission\n\n**Generate submission file in competition format**","metadata":{}},{"cell_type":"code","source":"print('📤 === SUBMISSION GENERATION ===\\n')\n\nprint('⚠️ TODO: Check competition submission format')\nprint('Typical formats: CSV, Parquet, JSON')\n\n# Generic template (uncomment and modify):\n# submission = pd.DataFrame({\n#     'id': test_ids,\n#     'prediction': predictions  # YOUR PREDICTIONS HERE\n# })\n# submission.to_csv('submission.csv', index=False)\n# print('✅ Submission created!')\n","metadata":{},"outputs":[],"execution_count":null}]}