{"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"},{"sourceId":13574721,"sourceType":"datasetVersion","datasetId":8621452}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport cv2\n\nimport matplotlib.pyplot as plt\nimport matplotlib\n\ndef ROUND(x):\n\tif isinstance(x, list):\n\t\treturn [int(round(xx)) for xx in x]\n\telse:\n\t\treturn int(round(x))\n\n\n##############################################################################3\nkaggle_dir = '/kaggle/input/physionet-ecg-image-digitization'\nprocessed_dir = '/kaggle/input/hengck23-gridpoint-demo'\n\nimage_id ='4211091537'\n\n\n\n# show the reference gridpoint annotation\n\nimage_file = f'{kaggle_dir}/train/{image_id}/{image_id}-0001.png' \nimage = cv2.imread(image_file, cv2.IMREAD_COLOR_RGB)\ngridpoint = np.load(f'{processed_dir}/{image_id}-0001.gridpoint_xy.npy' )\n\ndef draw_annotation(image, gridpoint):\n    \n    H,W = image.shape[:2]\n    overlay = image // 2\n    overlay1 = image // 2\n    \n    for j in range(44):\n        for i in range(57):\n            x,y = gridpoint[j,i]\n            if (x==0) &(y==0): continue\n            \n            y = ROUND(y)\n            x = ROUND(x)\n            \n            cv2.circle(overlay, (x, y), radius=4, color=[0, 255, 0], thickness=-1)\n            cv2.circle(overlay1, (x, y), radius=10, color=[0, 255, 0], thickness=-1)\n    \n    flat = gridpoint.reshape(-1,2)\n    miny = ROUND(flat[:,1].min())\n    maxy = ROUND(flat[:,1].max())\n    minx = ROUND(flat[:,0].min())\n    maxx = ROUND(flat[:,0].max())\n    \n    plt.imshow(np.hstack([image,overlay1]))\n    plt.show()\n    plt.imshow(overlay[maxy-500:maxy, minx: minx+1500])\n    plt.show()\n            \n# show results\ndraw_annotation(image, gridpoint)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2025-11-01T11:32:10.039604Z","iopub.execute_input":"2025-11-01T11:32:10.039804Z","iopub.status.idle":"2025-11-01T11:32:11.843022Z","shell.execute_reply.started":"2025-11-01T11:32:10.039786Z","shell.execute_reply":"2025-11-01T11:32:11.841977Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimage_file = f'{kaggle_dir}/train/{image_id}/{image_id}-0006.png' \nimage = cv2.imread(image_file, cv2.IMREAD_COLOR_RGB)\ngridpoint = np.load(f'{processed_dir}/{image_id}-0006.gridpoint_xy.npy' )\ndraw_annotation(image, gridpoint)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T11:32:11.845316Z","iopub.execute_input":"2025-11-01T11:32:11.845603Z","iopub.status.idle":"2025-11-01T11:32:16.059511Z","shell.execute_reply.started":"2025-11-01T11:32:11.845583Z","shell.execute_reply":"2025-11-01T11:32:16.058479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_file = f'{kaggle_dir}/train/{image_id}/{image_id}-0005.png' \nimage = cv2.imread(image_file, cv2.IMREAD_COLOR_RGB)\ngridpoint = np.load(f'{processed_dir}/{image_id}-0005.gridpoint_xy.npy' )\ndraw_annotation(image, gridpoint)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T11:32:16.060389Z","iopub.execute_input":"2025-11-01T11:32:16.060618Z","iopub.status.idle":"2025-11-01T11:32:19.475051Z","shell.execute_reply.started":"2025-11-01T11:32:16.060600Z","shell.execute_reply":"2025-11-01T11:32:19.474150Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Example usage in recification","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport cv2\n\nimport torch\nimport torch.nn.functional as F\n\nimport matplotlib.pyplot as plt\nimport matplotlib\n\n\ndef rectify_image(\n\timage, gridpoint_xy\n):\n\tH, W = 1700, 2200 #reference size\n\tH1,W1 = image.shape[:2]\n\tsparse_map = gridpoint_xy/[[[W1-1,H1-1]]]*2 -1\n\tsparse_map = torch.from_numpy(np.ascontiguousarray(sparse_map.transpose(2, 0, 1))).unsqueeze(0).float()\n\tdense_map  = F.interpolate(sparse_map, size=(H, W), mode='bilinear', align_corners=True)\n\tdistort    = torch.from_numpy(np.ascontiguousarray(image.transpose(2, 0, 1))).unsqueeze(0).float()\n\trectified = F.grid_sample(\n\t\tdistort, dense_map.permute(0, 2, 3, 1), mode='bilinear', padding_mode='border', align_corners=False\n\t)\n\trectified = rectified.data.cpu().numpy()\n\trectified = rectified[0].transpose(1, 2, 0).astype(np.uint8)\n\treturn rectified\n\nkaggle_dir = '/kaggle/input/physionet-ecg-image-digitization'\nprocessed_dir = '/kaggle/input/hengck23-gridpoint-demo'\n\nall=[]\nimage_id ='4211091537'\nfor type_id in ['0005', '0006', '0012']: \n    print('type_id:', type_id)\n\n    image_file = f'{kaggle_dir}/train/{image_id}/{image_id}-{type_id}.png' \n    image = cv2.imread(image_file, cv2.IMREAD_COLOR_RGB)\n    gridpoint_xy = np.load(f'{processed_dir}/{image_id}-{type_id}.gridpoint_xy.npy' ) #detected grid point from unet ,,,\n    \n    rectified = rectify_image(\n    \timage, gridpoint_xy\n    )\n    \n    plt.imshow(image)\n    plt.show()\n    plt.imshow(rectified)\n    plt.show()\n    all.append(rectified[-1000:, 0:1000])\n\nprint('compare all results:')\nplt.imshow(np.hstack(all), aspect='equal')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-01T11:32:19.475929Z","iopub.execute_input":"2025-11-01T11:32:19.476199Z","iopub.status.idle":"2025-11-01T11:32:31.601323Z","shell.execute_reply.started":"2025-11-01T11:32:19.476176Z","shell.execute_reply":"2025-11-01T11:32:31.600286Z"}},"outputs":[],"execution_count":null}]}