{"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":97984,"databundleVersionId":14096757,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\nfrom pathlib import Path\nfrom tqdm import tqdm\nimport warnings\nimport os\n\nwarnings.filterwarnings('ignore')\n\nprint(\"=\"*60)\nprint(\"🚀 PHYSIONET ECG BASELINE MODEL\")\nprint(\"=\"*60)\n\n# Quick check of available data\nprint(\"\\n📁 Checking available input files...\")\ninput_path = Path('/kaggle/input/physionet-ecg-image-digitization')\n\nif input_path.exists():\n    print(f\"✅ Competition data found at: {input_path}\")\n    print(f\"\\n📂 Main files:\")\n    for item in sorted(input_path.iterdir())[:10]:  # Show first 10\n        if item.is_file():\n            size_mb = item.stat().st_size / (1024*1024)\n            print(f\"  📄 {item.name:<30} ({size_mb:.2f} MB)\")\n        else:\n            print(f\"  📁 {item.name}/\")\nelse:\n    print(\"❌ Competition data not found! Make sure you're in the competition notebook.\")\n\nprint(\"\\n\" + \"=\"*60)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:11:08.630762Z","iopub.execute_input":"2025-10-23T06:11:08.630963Z","iopub.status.idle":"2025-10-23T06:11:08.639376Z","shell.execute_reply.started":"2025-10-23T06:11:08.630946Z","shell.execute_reply":"2025-10-23T06:11:08.638677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n📊 LOADING COMPETITION DATA\")\nprint(\"=\"*60)\n\nDATA_PATH = Path('/kaggle/input/physionet-ecg-image-digitization')\n\ntrain_df = pd.read_csv(DATA_PATH / 'train.csv')\ntest_df = pd.read_csv(DATA_PATH / 'test.csv')\nsample_sub = pd.read_parquet(DATA_PATH / 'sample_submission.parquet')\n\nprint(f\"✅ Train samples: {len(train_df):,}\")\nprint(f\"✅ Test samples: {test_df['id'].nunique():,}\")\nprint(f\"✅ Predictions needed: {len(sample_sub):,}\")\n\nprint(f\"\\n🔍 Train data preview:\")\nprint(train_df.head())\n\nprint(f\"\\n🔍 Test data preview:\")\nprint(test_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:11:43.685234Z","iopub.execute_input":"2025-10-23T06:11:43.685719Z","iopub.status.idle":"2025-10-23T06:11:44.032161Z","shell.execute_reply.started":"2025-10-23T06:11:43.685689Z","shell.execute_reply":"2025-10-23T06:11:44.031359Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n🔧 DEFINING PREPROCESSING FUNCTIONS\")\nprint(\"=\"*60)\n\ndef load_and_preprocess_image(image_path):\n    \"\"\"Load ECG image and convert to grayscale\"\"\"\n    img = cv2.imread(str(image_path))\n    if img is None:\n        return None\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    return gray\n\ndef remove_grid(image):\n    \"\"\"Remove grid lines from image to isolate signal\"\"\"\n    # Invert image (signal becomes white on black background)\n    inverted = cv2.bitwise_not(image)\n    \n    # Apply Gaussian blur to smooth\n    blurred = cv2.GaussianBlur(inverted, (3, 3), 0)\n    \n    # Threshold to get binary signal\n    _, binary = cv2.threshold(blurred, 30, 255, cv2.THRESH_BINARY)\n    \n    return binary\n\nprint(\"✅ Preprocessing functions ready!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:12:30.375463Z","iopub.execute_input":"2025-10-23T06:12:30.376080Z","iopub.status.idle":"2025-10-23T06:12:30.382168Z","shell.execute_reply.started":"2025-10-23T06:12:30.376054Z","shell.execute_reply":"2025-10-23T06:12:30.381273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n🔧 DEFINING SIGNAL EXTRACTION FUNCTIONS\")\nprint(\"=\"*60)\n\ndef extract_lead_signal(image, lead_position, num_samples):\n    \"\"\"\n    Extract signal for one lead from a specific region of the image.\n    \n    Parameters:\n    - image: preprocessed image\n    - lead_position: (y_start, y_end, x_start, x_end)\n    - num_samples: number of time points to extract\n    \"\"\"\n    y_start, y_end, x_start, x_end = lead_position\n    \n    # Extract lead region\n    lead_region = image[y_start:y_end, x_start:x_end]\n    \n    if lead_region.size == 0:\n        return np.zeros(num_samples)\n    \n    # Sample horizontally to get time-series\n    width = lead_region.shape[1]\n    signal = []\n    \n    for i in range(num_samples):\n        x_pos = int(i * width / num_samples)\n        if x_pos >= width:\n            x_pos = width - 1\n        \n        # Extract column\n        column = lead_region[:, x_pos]\n        \n        if len(column) == 0:\n            signal.append(0)\n            continue\n        \n        # Find brightest point (signal)\n        signal_y = np.argmax(column)\n        \n        # Convert pixel position to voltage (normalized scale)\n        voltage = (signal_y / len(column) - 0.5) * 4\n        signal.append(voltage)\n    \n    return np.array(signal)\n\ndef estimate_lead_positions(image_height, image_width):\n    \"\"\"\n    Estimate positions of 12 leads in standard ECG layout.\n    \n    Layout:\n    Row 1: I, II, III\n    Row 2: aVR, aVL, aVF  \n    Row 3: V1, V2, V3\n    Row 4: V4, V5, V6\n    (Lead II long strip would be at bottom, but simplified here)\n    \"\"\"\n    row_height = image_height // 5\n    col_width = image_width // 3\n    \n    positions = {}\n    leads_layout = [\n        ['I', 'II', 'III'],\n        ['aVR', 'aVL', 'aVF'],\n        ['V1', 'V2', 'V3'],\n        ['V4', 'V5', 'V6']\n    ]\n    \n    for row_idx, row_leads in enumerate(leads_layout):\n        for col_idx, lead_name in enumerate(row_leads):\n            y_start = row_idx * row_height\n            y_end = (row_idx + 1) * row_height\n            x_start = col_idx * col_width\n            x_end = (col_idx + 1) * col_width\n            positions[lead_name] = (y_start, y_end, x_start, x_end)\n    \n    return positions\n\nprint(\"✅ Signal extraction functions ready!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:13:06.285441Z","iopub.execute_input":"2025-10-23T06:13:06.285723Z","iopub.status.idle":"2025-10-23T06:13:06.343528Z","shell.execute_reply.started":"2025-10-23T06:13:06.285705Z","shell.execute_reply":"2025-10-23T06:13:06.342817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n🔧 DEFINING MAIN EXTRACTION PIPELINE\")\nprint(\"=\"*60)\n\ndef extract_ecg_from_image(image_path, fs, lead_durations):\n    \"\"\"\n    Complete pipeline to extract all 12 leads from an ECG image.\n    \"\"\"\n    # Load and preprocess\n    image = load_and_preprocess_image(image_path)\n    if image is None:\n        return None\n    \n    # Remove grid\n    processed = remove_grid(image)\n    \n    # Estimate lead positions\n    positions = estimate_lead_positions(image.shape[0], image.shape[1])\n    \n    # Extract each lead\n    signals = {}\n    for lead, position in positions.items():\n        duration = lead_durations.get(lead, 2.5)\n        num_samples = int(fs * duration)\n        signal = extract_lead_signal(processed, position, num_samples)\n        signals[lead] = signal\n    \n    return signals\n\n# Define standard lead durations\nLEAD_DURATIONS = {\n    'I': 2.5, 'III': 2.5, 'aVR': 2.5, 'aVL': 2.5, 'aVF': 2.5,\n    'V1': 2.5, 'V2': 2.5, 'V3': 2.5, 'V4': 2.5, 'V5': 2.5, 'V6': 2.5,\n    'II': 10.0  # Lead II is longer\n}\n\nprint(\"✅ Main pipeline ready!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:14:04.185356Z","iopub.execute_input":"2025-10-23T06:14:04.185633Z","iopub.status.idle":"2025-10-23T06:14:04.192073Z","shell.execute_reply.started":"2025-10-23T06:14:04.185614Z","shell.execute_reply":"2025-10-23T06:14:04.191448Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n🧪 TESTING ON ONE TRAINING EXAMPLE\")\nprint(\"=\"*60)\n\n# Get first training sample\nsample_id = str(train_df.iloc[0]['id'])\nsample_fs = train_df.iloc[0]['fs']\n\nsample_image = DATA_PATH / 'train' / sample_id / f\"{sample_id}-0001.png\"\n\nprint(f\"📌 Testing on sample: {sample_id}\")\nprint(f\"📊 Sampling frequency: {sample_fs} Hz\")\n\n# Extract signals\nextracted = extract_ecg_from_image(sample_image, sample_fs, LEAD_DURATIONS)\n\nif extracted:\n    print(f\"✅ Extraction successful!\")\n    print(f\"📊 Extracted leads: {list(extracted.keys())}\")\n    \n    # Load ground truth\n    gt_df = pd.read_csv(DATA_PATH / 'train' / sample_id / f\"{sample_id}.csv\")\n    \n    # Plot comparison\n    fig, axes = plt.subplots(2, 1, figsize=(15, 8))\n    \n    # Ground truth\n    axes[0].plot(gt_df['II'][:1000], linewidth=0.8, color='blue', label='Ground Truth')\n    axes[0].set_title('Ground Truth - Lead II (first 1000 samples)', fontsize=12, fontweight='bold')\n    axes[0].set_ylabel('Amplitude (mV)')\n    axes[0].grid(True, alpha=0.3)\n    axes[0].legend()\n    \n    # Extracted\n    axes[1].plot(extracted['II'][:1000], linewidth=0.8, color='red', label='Baseline Extraction')\n    axes[1].set_title('Baseline Extraction - Lead II (first 1000 samples)', fontsize=12, fontweight='bold')\n    axes[1].set_xlabel('Sample Index')\n    axes[1].set_ylabel('Amplitude (mV)')\n    axes[1].grid(True, alpha=0.3)\n    axes[1].legend()\n    \n    plt.tight_layout()\n    plt.savefig('baseline_comparison.png', dpi=100, bbox_inches='tight')\n    plt.show()\n    \n    print(\"\\n💡 This is a ROUGH baseline - signals won't match perfectly yet!\")\nelse:\n    print(\"❌ Extraction failed!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:15:04.314342Z","iopub.execute_input":"2025-10-23T06:15:04.315035Z","iopub.status.idle":"2025-10-23T06:15:05.525548Z","shell.execute_reply.started":"2025-10-23T06:15:04.315009Z","shell.execute_reply":"2025-10-23T06:15:05.524774Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":" \nprint(\"\\n📤 PROCESSING TEST SET\")\nprint(\"=\"*60)\n\n# Get unique test IDs\ntest_ids = test_df['id'].unique()\n\n# IMPORTANT: Start with just 20 images for testing\n# Remove [:20] to process ALL images for final submission\nNUM_TEST_SAMPLES = 20  # Change to len(test_ids) for full submission\n\nprint(f\"🧪 Processing {NUM_TEST_SAMPLES} test images (out of {len(test_ids)} total)\")\nprint(f\"⚠️ This is a TEST RUN - change NUM_TEST_SAMPLES to process all images\")\n\nall_predictions = []\n\n# Process test images\nfor test_id in tqdm(test_ids[:NUM_TEST_SAMPLES], desc=\"Extracting ECGs\"):\n    # Get metadata\n    test_meta = test_df[test_df['id'] == test_id].iloc[0]\n    fs = test_meta['fs']\n    \n    # Path to test image\n    test_image_path = DATA_PATH / 'test' / f\"{test_id}.png\"\n    \n    # Extract signals\n    extracted = extract_ecg_from_image(test_image_path, fs, LEAD_DURATIONS)\n    \n    if extracted is None:\n        # Fallback: use zeros\n        extracted = {lead: np.zeros(int(fs * LEAD_DURATIONS[lead])) \n                    for lead in LEAD_DURATIONS.keys()}\n    \n    # Format predictions\n    for lead, signal in extracted.items():\n        for row_id, value in enumerate(signal):\n            pred_id = f\"{test_id}_{row_id}_{lead}\"\n            all_predictions.append({\n                'id': pred_id,\n                'value': float(value)\n            })\n\nprint(f\"\\n✅ Generated {len(all_predictions):,} predictions\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:16:28.522607Z","iopub.execute_input":"2025-10-23T06:16:28.522890Z","iopub.status.idle":"2025-10-23T06:16:29.064961Z","shell.execute_reply.started":"2025-10-23T06:16:28.522870Z","shell.execute_reply":"2025-10-23T06:16:29.064238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n💾 CREATING SUBMISSION FILE\")\nprint(\"=\"*60)\n\n# Create submission dataframe\nsubmission_df = pd.DataFrame(all_predictions)\n\nprint(f\"📊 Submission shape: {submission_df.shape}\")\nprint(f\"📋 Sample submission shape: {sample_sub.shape}\")\n\nprint(f\"\\n🔍 First 10 predictions:\")\nprint(submission_df.head(10))\n\nprint(f\"\\n🔍 Last 10 predictions:\")\nprint(submission_df.tail(10))\n\n# Save submission\nsubmission_df.to_csv('submission.csv', index=False)\nprint(f\"\\n✅ Saved to: /kaggle/working/submission.csv\")\n\n# Also save as parquet (alternative format)\nsubmission_df.to_parquet('submission.parquet', index=False)\nprint(f\"✅ Also saved as: /kaggle/working/submission.parquet\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:17:21.094794Z","iopub.execute_input":"2025-10-23T06:17:21.095409Z","iopub.status.idle":"2025-10-23T06:17:21.318466Z","shell.execute_reply.started":"2025-10-23T06:17:21.095384Z","shell.execute_reply":"2025-10-23T06:17:21.317820Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n🔍 SUBMISSION VALIDATION\")\nprint(\"=\"*60)\n\n# Check format\nrequired_columns = ['id', 'value']\nhas_required = all(col in submission_df.columns for col in required_columns)\nprint(f\"✅ Has required columns: {has_required}\")\n\n# Check for NaN values\nhas_nan = submission_df['value'].isna().any()\nprint(f\"✅ No NaN values: {not has_nan}\")\n\n# Check value ranges (should be reasonable for ECG in mV)\nprint(f\"\\n📊 Value statistics:\")\nprint(submission_df['value'].describe())\n\n# Check ID format\nsample_ids = submission_df['id'].head(3).tolist()\nprint(f\"\\n🔍 Sample IDs format:\")\nfor sid in sample_ids:\n    print(f\"  {sid}\")\n\nprint(f\"\\n⚠️ NOTE: This is a PARTIAL submission ({NUM_TEST_SAMPLES} images)\")\nprint(f\"   For full submission, set NUM_TEST_SAMPLES = len(test_ids) in Cell 7\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:17:49.150406Z","iopub.execute_input":"2025-10-23T06:17:49.151077Z","iopub.status.idle":"2025-10-23T06:17:49.165124Z","shell.execute_reply.started":"2025-10-23T06:17:49.151050Z","shell.execute_reply":"2025-10-23T06:17:49.164416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n\" + \"=\"*60)\nprint(\"🎉 BASELINE MODEL COMPLETE!\")\nprint(\"=\"*60)\n\nprint(f\"\"\"\n✅ What you accomplished:\n  1. Built image preprocessing pipeline\n  2. Created signal extraction algorithm\n  3. Processed {NUM_TEST_SAMPLES} test images\n  4. Generated submission file\n\n📊 Current status:\n  - Predictions: {len(all_predictions):,}\n  - File: submission.csv (ready to submit!)\n  - Format: Valid ✓\n\n🎯 Next steps to improve:\n\n  IMMEDIATE (to submit):\n  1. Change NUM_TEST_SAMPLES to len(test_ids) in Cell 7\n  2. Run Cell 7-9 again (will take ~30-60 min)\n  3. Submit to competition\n  4. Get baseline score!\n\n  IMPROVEMENTS (after baseline):\n  1. Better lead detection (find actual boundaries)\n  2. Calibrate voltage scale using training data\n  3. Handle different image types better\n  4. Try deep learning (CNN/U-Net)\n  5. Add signal smoothing/filtering\n  6. Detect and remove artifacts\n\n💡 Tips:\n  - Don't worry if first score is low\n  - Learn from leaderboard position\n  - Iterate and improve!\n  - Check discussion forum for ideas\n\n🚀 Ready to submit your baseline? \n   Update Cell 7 and run for full predictions!\n\"\"\")\n\nprint(\"=\"*60)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-23T06:18:26.558430Z","iopub.execute_input":"2025-10-23T06:18:26.558702Z","iopub.status.idle":"2025-10-23T06:18:26.564197Z","shell.execute_reply.started":"2025-10-23T06:18:26.558683Z","shell.execute_reply":"2025-10-23T06:18:26.563465Z"}},"outputs":[],"execution_count":null}]}