{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":97984,"databundleVersionId":14096757},{"sourceType":"datasetVersion","sourceId":15613723,"datasetId":9992354,"databundleVersionId":16547648},{"sourceType":"datasetVersion","sourceId":15433411,"datasetId":9872944,"databundleVersionId":16352653},{"sourceType":"datasetVersion","sourceId":15585543,"datasetId":9971684,"databundleVersionId":16517673},{"sourceType":"datasetVersion","sourceId":13731160,"datasetId":8733970,"databundleVersionId":14479231},{"sourceType":"datasetVersion","sourceId":15281722,"datasetId":9775234,"databundleVersionId":16183481}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install connected-components-3d --no-index --find-links=file:///kaggle/input/datasets/tylerde/my-pip-packages/ -q\n\nimport sys, os, cv2, numpy as np, pandas as pd, torch\nfrom scipy import signal as scipy_signal\n\nsys.path.append('/kaggle/input/datasets/tylerde/ecg-model-0001')\n\nfrom model import ECGRowNet\nfrom config import CFG\nfrom inference import predict_image\n\ndevice = 'cuda:0'\nDATA_DIR = '/kaggle/input/datasets/tylerde/ecg-training-data-0001-only/data'\nKAGGLE_DIR = '/kaggle/input/competitions/physionet-ecg-image-digitization'\nMODEL_DIR = '/kaggle/input/datasets/tylerde/ecg-model-0001'\n\n# Load model\nmodel = ECGRowNet(CFG).to(device)\nckpt = torch.load(f'{MODEL_DIR}/best_fold0.pth', map_location=device, weights_only=False)\nmodel.load_state_dict(ckpt['state_dict'])\nmodel.eval()\n\n# Pick 5 training samples we KNOW the answer to\nfold_df = pd.read_csv(f'{DATA_DIR}/train_fold.csv', dtype={'id': str, 'type_id': str})\nsamples = fold_df[fold_df['fold'] == 0].head(5)\n\nROW_TO_LEADS = [\n    ['I', 'aVR', 'V1', 'V4'],\n    ['II', 'aVL', 'V2', 'V5'],\n    ['III', 'aVF', 'V3', 'V6'],\n]\n\nfor _, row in samples.iterrows():\n    sid = str(row['id'])\n    \n    # Step 1: predict (same as submission)\n    rect_path = f'{DATA_DIR}/rectified/{sid}-0001.rect.png'\n    series = predict_image(model, rect_path, device, use_tta=True)\n    \n    # Step 2: load ground truth\n    gt_df = pd.read_csv(f'{KAGGLE_DIR}/train/{sid}/{sid}.csv')\n    \n    # Step 3: split into leads (same as our Cell 5)\n    series_by_lead = {}\n    for row_idx in range(3):\n        leads = ROW_TO_LEADS[row_idx]\n        lengths = [len(gt_df[lead].dropna()) for lead in leads]\n        if leads[0] == 'II':\n            lengths[0] = lengths[0] - sum(lengths[1:])\n        total_len = sum(lengths)\n        row_resampled = scipy_signal.resample(series[row_idx], total_len).astype(np.float32)\n        idx = np.cumsum(lengths)[:-1]\n        splits = np.split(row_resampled, idx)\n        for lead, s in zip(leads, splits):\n            series_by_lead[lead] = s\n    \n    ii_len = len(gt_df['II'].dropna())\n    series_by_lead['II'] = scipy_signal.resample(series[3], ii_len).astype(np.float32)\n    \n    # Step 4: compute SNR (same as competition)\n    total_sig, total_noise = 0, 0\n    for lead in ['I','II','III','aVR','aVL','aVF','V1','V2','V3','V4','V5','V6']:\n        gt = gt_df[lead].dropna().values.astype(np.float32)\n        pred = series_by_lead[lead][:len(gt)]\n        total_sig += (gt ** 2).sum()\n        total_noise += ((pred - gt) ** 2).sum()\n    \n    snr = 10 * np.log10(total_sig / (total_noise + 1e-7))\n    print(f'{sid}: SNR = {snr:.2f} dB')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-09T18:25:09.490464Z","iopub.execute_input":"2026-04-09T18:25:09.490756Z","iopub.status.idle":"2026-04-09T18:25:31.443278Z","shell.execute_reply.started":"2026-04-09T18:25:09.490715Z","shell.execute_reply":"2026-04-09T18:25:31.44251Z"}},"outputs":[{"name":"stdout","text":"90355132: SNR = 27.17 dB\n108599929: SNR = 24.52 dB\n112870634: SNR = 10.34 dB\n144746082: SNR = 29.99 dB\n157249266: SNR = 22.49 dB\n","output_type":"stream"}],"execution_count":1},{"cell_type":"code","source":"# === Test on dirty images (batch2) ===\n\nDATA_DIR2 = '/kaggle/input/datasets/tylerde/ecg-training-data-0003-0005-0006/data'\nfold_df2 = pd.read_csv(f'{DATA_DIR2}/train_fold.csv', dtype={'id': str, 'type_id': str})\n\nfor type_id in ['0003', '0005', '0006']:\n    subset = fold_df2[fold_df2['type_id'] == type_id].head(3)\n    snrs = []\n    \n    for _, row in subset.iterrows():\n        sid = str(row['id'])\n        rect_path = f'{DATA_DIR2}/rectified/{sid}-{type_id}.rect.png'\n        series = predict_image(model, rect_path, device, use_tta=True)\n        \n        gt_df = pd.read_csv(f'{KAGGLE_DIR}/train/{sid}/{sid}.csv')\n        \n        series_by_lead = {}\n        for row_idx in range(3):\n            leads = ROW_TO_LEADS[row_idx]\n            lengths = [len(gt_df[lead].dropna()) for lead in leads]\n            if leads[0] == 'II':\n                lengths[0] = lengths[0] - sum(lengths[1:])\n            total_len = sum(lengths)\n            row_resampled = scipy_signal.resample(series[row_idx], total_len).astype(np.float32)\n            idx = np.cumsum(lengths)[:-1]\n            splits = np.split(row_resampled, idx)\n            for lead, s in zip(leads, splits):\n                series_by_lead[lead] = s\n        \n        ii_len = len(gt_df['II'].dropna())\n        series_by_lead['II'] = scipy_signal.resample(series[3], ii_len).astype(np.float32)\n        \n        total_sig, total_noise = 0, 0\n        for lead in ['I','II','III','aVR','aVL','aVF','V1','V2','V3','V4','V5','V6']:\n            gt = gt_df[lead].dropna().values.astype(np.float32)\n            pred = series_by_lead[lead][:len(gt)]\n            total_sig += (gt ** 2).sum()\n            total_noise += ((pred - gt) ** 2).sum()\n        \n        snr = 10 * np.log10(total_sig / (total_noise + 1e-7))\n        snrs.append(snr)\n    \n    print(f'Type {type_id}: {[f\"{s:.1f}\" for s in snrs]} mean={np.mean(snrs):.1f} dB')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-09T18:25:31.449756Z","iopub.execute_input":"2026-04-09T18:25:31.450188Z","iopub.status.idle":"2026-04-09T18:25:52.228303Z","shell.execute_reply.started":"2026-04-09T18:25:31.450157Z","shell.execute_reply":"2026-04-09T18:25:52.22762Z"}},"outputs":[{"name":"stdout","text":"Type 0003: ['7.6', '7.4', '6.5'] mean=7.2 dB\nType 0005: ['-11.2', '-10.2', '-14.8'] mean=-12.1 dB\nType 0006: ['-5.0', '-4.0', '-6.1'] mean=-5.1 dB\n","output_type":"stream"}],"execution_count":2},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}