{"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":97984,"databundleVersionId":14096757,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# ECG Image Structure Analysis","metadata":{"_uuid":"7bd1dc04-6d32-41eb-9add-421ee0b66299","_cell_guid":"aa79a834-4179-4344-8b7d-8d0898bcb3ef","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"markdown","source":"## 1. Setup","metadata":{"_uuid":"603fab69-60aa-4235-98b6-efe16acde16e","_cell_guid":"1c4e9dd7-2c94-4d8b-b6fa-52c5067df2fd","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom pathlib import Path\nfrom PIL import Image\nimport cv2\nimport warnings\n\nwarnings.filterwarnings('ignore')\nplt.rcParams['figure.figsize'] = (15, 8)\n\n# Set paths\nDATA_PATH = Path('/kaggle/input/physionet-ecg-image-digitization')\nTRAIN_PATH = DATA_PATH / 'train'\n\n# Load metadata\ntrain_df = pd.read_csv(DATA_PATH / 'train.csv')\nprint(f\"Loaded {len(train_df)} training samples\")","metadata":{"_uuid":"21475f1e-51c5-47fc-9728-df25d3315c08","_cell_guid":"40fd81f6-c63a-42f3-bcb1-187b32a65724","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:39.907584Z","iopub.execute_input":"2025-10-21T18:53:39.907876Z","iopub.status.idle":"2025-10-21T18:53:41.800315Z","shell.execute_reply.started":"2025-10-21T18:53:39.907854Z","shell.execute_reply":"2025-10-21T18:53:41.799165Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Analyze Image Dimensions and Properties","metadata":{"_uuid":"1405df70-c173-46fb-a10c-768c4a3bec66","_cell_guid":"2035abd4-7d93-49c6-ab6b-169917f8d1f8","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Sample a few images and check their properties\nsample_ids = train_df['id'].head(10).tolist()\nimage_properties = []\n\nfor sample_id in sample_ids:\n    sample_dir = TRAIN_PATH / str(sample_id)\n    # Check the original image (0001)\n    img_path = sample_dir / f\"{sample_id}-0001.png\"\n\n    if img_path.exists():\n        img = Image.open(img_path)\n        img_array = np.array(img)\n\n        image_properties.append({\n            'id': sample_id,\n            'width': img.size[0],\n            'height': img.size[1],\n            'mode': img.mode,\n            'channels': img_array.shape[2] if len(img_array.shape) == 3 else 1,\n            'dtype': img_array.dtype,\n            'min_val': img_array.min(),\n            'max_val': img_array.max(),\n            'mean_val': img_array.mean()\n        })\n\nprops_df = pd.DataFrame(image_properties)\nprint(\"\\nImage Properties Summary:\")\nprint(props_df)\n\nprint(\"\\nImage Dimensions Distribution:\")\nprint(props_df[['width', 'height']].describe())","metadata":{"_uuid":"3e8d15fd-7ddf-4953-8647-174b93b6ceda","_cell_guid":"ba39ec8c-1cf3-4f29-aaf0-c92df668d005","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:41.801350Z","iopub.execute_input":"2025-10-21T18:53:41.801684Z","iopub.status.idle":"2025-10-21T18:53:43.119641Z","shell.execute_reply.started":"2025-10-21T18:53:41.801655Z","shell.execute_reply":"2025-10-21T18:53:43.118551Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Visualize Image Structure with Annotations","metadata":{"_uuid":"dcc8018f-8f22-42b8-88c2-304cdb52151f","_cell_guid":"75d3c26d-97fe-475f-b874-cede69d0a3c2","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Load a sample image and analyze its structure\nsample_id = str(train_df['id'].iloc[0])\nsample_dir = TRAIN_PATH / sample_id\nimg_path = sample_dir / f\"{sample_id}-0001.png\"\n\n# Load with PIL and OpenCV\nimg_pil = Image.open(img_path)\nimg_cv = cv2.imread(str(img_path))\nimg_rgb = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)\nimg_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)\n\nprint(f\"Sample ID: {sample_id}\")\nprint(f\"Image shape (RGB): {img_rgb.shape}\")\nprint(f\"Image shape (Gray): {img_gray.shape}\")\n\n# Display original and grayscale\nfig, axes = plt.subplots(1, 2, figsize=(18, 8))\n\naxes[0].imshow(img_rgb)\naxes[0].set_title('Original ECG Image (RGB)')\naxes[0].axis('off')\n\naxes[1].imshow(img_gray, cmap='gray')\naxes[1].set_title('Grayscale ECG Image')\naxes[1].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"a2e4aaaa-893a-419f-8c4a-1853263343e9","_cell_guid":"0b254a96-3aef-43c1-aa5f-f5e0170e56bf","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:43.121618Z","iopub.execute_input":"2025-10-21T18:53:43.121920Z","iopub.status.idle":"2025-10-21T18:53:44.790951Z","shell.execute_reply.started":"2025-10-21T18:53:43.121898Z","shell.execute_reply":"2025-10-21T18:53:44.789617Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Analyze Horizontal and Vertical Projections","metadata":{"_uuid":"8439f802-1c97-4997-b394-700f64ca0faa","_cell_guid":"75257d60-ea03-4b42-9fb9-b35ae1316c42","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Horizontal and vertical projections can help identify grid lines and lead regions\nhorizontal_projection = np.sum(img_gray, axis=1)\nvertical_projection = np.sum(img_gray, axis=0)\n\nfig, axes = plt.subplots(2, 2, figsize=(18, 10))\n\n# Show image with projections\naxes[0, 0].imshow(img_gray, cmap='gray')\naxes[0, 0].set_title('ECG Image')\naxes[0, 0].axis('off')\n\n# Horizontal projection\naxes[0, 1].plot(horizontal_projection, range(len(horizontal_projection)))\naxes[0, 1].set_ylim(len(horizontal_projection), 0)\naxes[0, 1].set_title('Horizontal Projection')\naxes[0, 1].set_xlabel('Sum of pixel intensities')\naxes[0, 1].set_ylabel('Row index')\naxes[0, 1].grid(True, alpha=0.3)\n\n# Vertical projection\naxes[1, 0].plot(vertical_projection)\naxes[1, 0].set_title('Vertical Projection')\naxes[1, 0].set_xlabel('Column index')\naxes[1, 0].set_ylabel('Sum of pixel intensities')\naxes[1, 0].grid(True, alpha=0.3)\n\n# Combined view\naxes[1, 1].imshow(img_gray, cmap='gray')\naxes[1, 1].set_title('ECG Image')\naxes[1, 1].set_xlabel('Column index')\naxes[1, 1].set_ylabel('Row index')\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"b5c90a27-2b56-4075-97ad-c86fcb589130","_cell_guid":"7167a124-ee14-43b9-91d5-172d8fef8e5e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:44.791964Z","iopub.execute_input":"2025-10-21T18:53:44.792259Z","iopub.status.idle":"2025-10-21T18:53:46.116771Z","shell.execute_reply.started":"2025-10-21T18:53:44.792235Z","shell.execute_reply":"2025-10-21T18:53:46.115670Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Detect Grid Lines","metadata":{"_uuid":"6e27529a-d03e-4342-a4ef-da86179c2586","_cell_guid":"18c8fdeb-5ef8-418e-ba0d-3ab00fe3e599","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Try to detect horizontal and vertical grid lines using edge detection\nedges = cv2.Canny(img_gray, 50, 150)\n\n# Detect lines using Hough Transform\nlines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=100, minLineLength=100, maxLineGap=10)\n\n# Draw detected lines\nimg_with_lines = img_rgb.copy()\nif lines is not None:\n    print(f\"Detected {len(lines)} line segments\")\n\n    # Separate horizontal and vertical lines\n    horizontal_lines = []\n    vertical_lines = []\n\n    for line in lines:\n        x1, y1, x2, y2 = line[0]\n        angle = np.abs(np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi)\n\n        if angle < 10 or angle > 170:  # Horizontal\n            horizontal_lines.append(line)\n            cv2.line(img_with_lines, (x1, y1), (x2, y2), (255, 0, 0), 1)\n        elif 80 < angle < 100:  # Vertical\n            vertical_lines.append(line)\n            cv2.line(img_with_lines, (x1, y1), (x2, y2), (0, 255, 0), 1)\n\n    print(f\"Horizontal lines: {len(horizontal_lines)}\")\n    print(f\"Vertical lines: {len(vertical_lines)}\")\n\nfig, axes = plt.subplots(1, 3, figsize=(20, 7))\n\naxes[0].imshow(img_gray, cmap='gray')\naxes[0].set_title('Original Image')\naxes[0].axis('off')\n\naxes[1].imshow(edges, cmap='gray')\naxes[1].set_title('Edge Detection (Canny)')\naxes[1].axis('off')\n\naxes[2].imshow(img_with_lines)\naxes[2].set_title(f'Detected Lines (H: blue, V: green)')\naxes[2].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"c43e0c5d-ee7d-4ac0-83ba-6611aa492790","_cell_guid":"2b54ac60-8144-4da3-bc09-d8e107e25a6a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:46.118054Z","iopub.execute_input":"2025-10-21T18:53:46.118402Z","iopub.status.idle":"2025-10-21T18:53:47.966874Z","shell.execute_reply.started":"2025-10-21T18:53:46.118375Z","shell.execute_reply":"2025-10-21T18:53:47.965606Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Analyze Color Channels","metadata":{"_uuid":"36e55568-f48c-4178-979d-445a1fee2145","_cell_guid":"78cafb06-5d9d-45dd-a6fe-b971726e567f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Analyze RGB channels separately - ECG signals might be in specific channels\nr_channel = img_rgb[:, :, 0]\ng_channel = img_rgb[:, :, 1]\nb_channel = img_rgb[:, :, 2]\n\nfig, axes = plt.subplots(2, 3, figsize=(18, 12))\n\n# Show each channel\naxes[0, 0].imshow(r_channel, cmap='Reds')\naxes[0, 0].set_title('Red Channel')\naxes[0, 0].axis('off')\n\naxes[0, 1].imshow(g_channel, cmap='Greens')\naxes[0, 1].set_title('Green Channel')\naxes[0, 1].axis('off')\n\naxes[0, 2].imshow(b_channel, cmap='Blues')\naxes[0, 2].set_title('Blue Channel')\naxes[0, 2].axis('off')\n\n# Histograms\naxes[1, 0].hist(r_channel.flatten(), bins=50, color='red', alpha=0.7)\naxes[1, 0].set_title('Red Channel Histogram')\naxes[1, 0].set_xlabel('Pixel Value')\naxes[1, 0].set_ylabel('Frequency')\n\naxes[1, 1].hist(g_channel.flatten(), bins=50, color='green', alpha=0.7)\naxes[1, 1].set_title('Green Channel Histogram')\naxes[1, 1].set_xlabel('Pixel Value')\naxes[1, 1].set_ylabel('Frequency')\n\naxes[1, 2].hist(b_channel.flatten(), bins=50, color='blue', alpha=0.7)\naxes[1, 2].set_title('Blue Channel Histogram')\naxes[1, 2].set_xlabel('Pixel Value')\naxes[1, 2].set_ylabel('Frequency')\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"20887d20-d35a-418b-b36c-26def806467b","_cell_guid":"5eec5cec-bb4d-4504-851a-b642218c21ec","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:47.967945Z","iopub.execute_input":"2025-10-21T18:53:47.968249Z","iopub.status.idle":"2025-10-21T18:53:50.104611Z","shell.execute_reply.started":"2025-10-21T18:53:47.968225Z","shell.execute_reply":"2025-10-21T18:53:50.103620Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7. Extract and Visualize a Horizontal Slice","metadata":{"_uuid":"78685909-aa71-4149-a3cc-5720c749c303","_cell_guid":"0fb2815f-8068-43e8-9e06-2f75b3673bb3","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Extract a horizontal slice to see the signal pattern\nslice_height = img_gray.shape[0] // 4  # Take a slice from 1/4 height\nslice_data = img_gray[slice_height, :]\n\nfig, axes = plt.subplots(2, 1, figsize=(18, 8))\n\n# Show where the slice is taken from\naxes[0].imshow(img_gray, cmap='gray')\naxes[0].axhline(y=slice_height, color='r', linestyle='--', linewidth=2)\naxes[0].set_title(f'ECG Image with Horizontal Slice at row {slice_height}')\naxes[0].axis('off')\n\n# Plot the slice\naxes[1].plot(slice_data)\naxes[1].set_title('Pixel Intensity Along Horizontal Slice')\naxes[1].set_xlabel('Column Index (x)')\naxes[1].set_ylabel('Pixel Intensity')\naxes[1].grid(True, alpha=0.3)\naxes[1].set_xlim(0, len(slice_data))\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"84eedb6a-449c-4f1d-94f0-32dae9314c01","_cell_guid":"b68e2b9e-dfe9-4cda-9514-3e2c75ae6c12","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:50.105609Z","iopub.execute_input":"2025-10-21T18:53:50.105867Z","iopub.status.idle":"2025-10-21T18:53:50.754001Z","shell.execute_reply.started":"2025-10-21T18:53:50.105847Z","shell.execute_reply":"2025-10-21T18:53:50.752898Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 8. Compare Multiple Image Types","metadata":{"_uuid":"86a1f8ce-8711-42fa-9245-09b24592a672","_cell_guid":"10628d49-be19-48e8-875b-a65a866f45ae","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Compare different image types (original, scanned, photographed, damaged)\nimage_types_to_check = ['0001', '0003', '0004', '0005', '0010']\nimage_type_names = {\n    '0001': 'Original',\n    '0003': 'Color Scan',\n    '0004': 'B&W Scan',\n    '0005': 'Mobile Photo',\n    '0010': 'Damaged'\n}\n\nfig, axes = plt.subplots(len(image_types_to_check), 2, figsize=(18, 4*len(image_types_to_check)))\n\nfor idx, img_type in enumerate(image_types_to_check):\n    img_path = sample_dir / f\"{sample_id}-{img_type}.png\"\n\n    if img_path.exists():\n        img = cv2.imread(str(img_path))\n        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n\n        # Show RGB\n        axes[idx, 0].imshow(img_rgb)\n        axes[idx, 0].set_title(f'{image_type_names.get(img_type, img_type)} - RGB')\n        axes[idx, 0].axis('off')\n\n        # Show grayscale histogram\n        axes[idx, 1].hist(img_gray.flatten(), bins=50, alpha=0.7)\n        axes[idx, 1].set_title(f'{image_type_names.get(img_type, img_type)} - Intensity Histogram')\n        axes[idx, 1].set_xlabel('Pixel Value')\n        axes[idx, 1].set_ylabel('Frequency')\n        axes[idx, 1].grid(True, alpha=0.3)\n    else:\n        axes[idx, 0].text(0.5, 0.5, 'Image not found', ha='center', va='center')\n        axes[idx, 0].axis('off')\n        axes[idx, 1].text(0.5, 0.5, 'Image not found', ha='center', va='center')\n        axes[idx, 1].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"71d981e2-2e65-4ad4-839a-2770eae5eca4","_cell_guid":"b6c84933-0df8-484b-9539-76986dd8a5e6","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:50.755137Z","iopub.execute_input":"2025-10-21T18:53:50.755574Z","iopub.status.idle":"2025-10-21T18:53:58.822931Z","shell.execute_reply.started":"2025-10-21T18:53:50.755526Z","shell.execute_reply":"2025-10-21T18:53:58.821697Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 9. Cropped Region Analysis","metadata":{"_uuid":"179d579c-b74b-4c6b-850c-27745ab4a449","_cell_guid":"08adb4f4-1279-4bb5-8131-e8c45e3378d3","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Take a closer look at a small region to see grid and signal details\n# Crop a region from the middle of the image\ncrop_height = 400\ncrop_width = 800\nstart_y = (img_gray.shape[0] - crop_height) // 2\nstart_x = (img_gray.shape[1] - crop_width) // 2\n\ncropped = img_rgb[start_y:start_y+crop_height, start_x:start_x+crop_width]\ncropped_gray = img_gray[start_y:start_y+crop_height, start_x:start_x+crop_width]\n\nfig, axes = plt.subplots(2, 2, figsize=(16, 12))\n\n# Show crop location\naxes[0, 0].imshow(img_rgb)\naxes[0, 0].add_patch(plt.Rectangle((start_x, start_y), crop_width, crop_height,\n                                   fill=False, edgecolor='red', linewidth=2))\naxes[0, 0].set_title('Full Image with Crop Region')\naxes[0, 0].axis('off')\n\n# Show cropped region\naxes[0, 1].imshow(cropped)\naxes[0, 1].set_title('Cropped Region (Color)')\naxes[0, 1].axis('off')\n\n# Show cropped grayscale\naxes[1, 0].imshow(cropped_gray, cmap='gray')\naxes[1, 0].set_title('Cropped Region (Grayscale)')\naxes[1, 0].axis('off')\n\n# Show inverted (dark signals on light background)\naxes[1, 1].imshow(255 - cropped_gray, cmap='gray')\naxes[1, 1].set_title('Cropped Region (Inverted)')\naxes[1, 1].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"ff732e0b-54bb-4eb0-9bfd-1040e2fe1372","_cell_guid":"6cd45ad0-4ca5-414e-a33b-5aebc843624c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:53:58.825592Z","iopub.execute_input":"2025-10-21T18:53:58.825897Z","iopub.status.idle":"2025-10-21T18:54:01.475344Z","shell.execute_reply.started":"2025-10-21T18:53:58.825873Z","shell.execute_reply":"2025-10-21T18:54:01.473634Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 10. Summary and Key Observations","metadata":{"_uuid":"248738ec-7354-4815-9adb-81898d10a142","_cell_guid":"c7a3e410-2a6a-4226-9f0c-c84751d2f23f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"print(\"=\"*80)\nprint(\"IMAGE ANALYSIS SUMMARY\")\nprint(\"=\"*80)\nprint(\"\\n1. IMAGE PROPERTIES:\")\nprint(f\"   - Typical dimensions: {props_df['width'].mode().values[0]}x{props_df['height'].mode().values[0]}\")\nprint(f\"   - Color mode: {props_df['mode'].mode().values[0]}\")\nprint(f\"   - Pixel value range: {props_df['min_val'].min()}-{props_df['max_val'].max()}\")\n\nprint(\"\\n2. GRID STRUCTURE:\")\nif lines is not None:\n    print(f\"   - Horizontal grid lines detected: {len(horizontal_lines)}\")\n    print(f\"   - Vertical grid lines detected: {len(vertical_lines)}\")","metadata":{"_uuid":"4f3bc194-3101-4050-b7b5-1db6ba7bc91e","_cell_guid":"fb926aa7-cb91-4c90-8f85-b9c7fd67b65e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-21T18:57:20.280736Z","iopub.execute_input":"2025-10-21T18:57:20.281420Z","iopub.status.idle":"2025-10-21T18:57:20.289218Z","shell.execute_reply.started":"2025-10-21T18:57:20.281391Z","shell.execute_reply":"2025-10-21T18:57:20.288043Z"}},"outputs":[],"execution_count":null}]}