{
  "id": 614972,
  "title": "Grid artifacts",
  "url": "/competitions/physionet-ecg-image-digitization/discussion/614972",
  "author_name": "Paul Jurczak",
  "post_date": "2025-11-07T19:48:30.359000",
  "votes": 5,
  "comment_count": 22,
  "views": 0,
  "content": "<p>I was surprised to find horizontal grid spacing artifacts on type 0001 images. Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F363811%2F7c6e532ac163c2a9a7071742c7c1c98e%2Fgrid-artifact.png?generation=1762544650331550&amp;alt=media\" alt=\"\"></p>\n<p>Is this an error, or should we assume that the waveform's time and voltage scale is also compressed in this area?</p>\n<hr>\n<p>After looking at the source code of ECG-Image-Kit, which was used to generate type 0001 images, specifically function <code>ecg_plot()</code>, I conclude that grid non-uniformity artifacts are created by Mathplotlib plotting functions and the waveform time base is uniform across the width of the image. This is where the waveform is plotted:</p>\n<pre><code>t1 = ax.plot(.arange(,len(ecg[leadName])*,) + x_offset + dc_offset + x_gap, \n        ecg[leadName] + y_offset,\n        =, \n        =color_line\n        )\n</code></pre>",
  "messages": [
    {
      "id": 3312730,
      "postDate": "2025-11-07T19:48:30.360Z",
      "content": "<p>I was surprised to find horizontal grid spacing artifacts on type 0001 images. Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F363811%2F7c6e532ac163c2a9a7071742c7c1c98e%2Fgrid-artifact.png?generation=1762544650331550&amp;alt=media\" alt=\"\"></p>\n<p>Is this an error, or should we assume that the waveform's time and voltage scale is also compressed in this area?</p>\n<hr>\n<p>After looking at the source code of ECG-Image-Kit, which was used to generate type 0001 images, specifically function <code>ecg_plot()</code>, I conclude that grid non-uniformity artifacts are created by Mathplotlib plotting functions and the waveform time base is uniform across the width of the image. This is where the waveform is plotted:</p>\n<pre><code>t1 = ax.plot(.arange(,len(ecg[leadName])*,) + x_offset + dc_offset + x_gap, \n        ecg[leadName] + y_offset,\n        =, \n        =color_line\n        )\n</code></pre>",
      "rawMarkdown": "I was surprised to find horizontal grid spacing artifacts on type 0001 images. Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F363811%2F7c6e532ac163c2a9a7071742c7c1c98e%2Fgrid-artifact.png?generation=1762544650331550&alt=media)\n\nIs this an error, or should we assume that the waveform's time and voltage scale is also compressed in this area?\n\n---\n\nAfter looking at the source code of ECG-Image-Kit, which was used to generate type 0001 images, specifically function `ecg_plot()`, I conclude that grid non-uniformity artifacts are created by Mathplotlib plotting functions and the waveform time base is uniform across the width of the image. This is where the waveform is plotted:\n```\nt1 = ax.plot(np.arange(0,len(ecg[leadName])*step,step) + x_offset + dc_offset + x_gap, \n        ecg[leadName] + y_offset,\n        linewidth=line_width, \n        color=color_line\n        )\n```\n",
      "votes": 5
    },
    {
      "id": 3313206,
      "postDate": "2025-11-08T22:22:21.587Z",
      "content": "<p>As Gari noted, these are quantization effects of mapping time series to images. The photographed images have additional distortions due to natural imaging artifacts and perspectives. ECG-Image-Kit has a few functions with ideas for estimating grid size in mV and seconds from the images, using histograms and spectral methods. Check out the functions starting with <code>ecg_gridest</code> <a href=\"https://github.com/alphanumericslab/ecg-image-kit/tree/main/codes%2Fecg-image-digitizer\" target=\"_blank\">here</a>.</p>",
      "rawMarkdown": "As Gari noted, these are quantization effects of mapping time series to images. The photographed images have additional distortions due to natural imaging artifacts and perspectives. ECG-Image-Kit has a few functions with ideas for estimating grid size in mV and seconds from the images, using histograms and spectral methods. Check out the functions starting with `ecg_gridest` [here](https://github.com/alphanumericslab/ecg-image-kit/tree/main/codes%2Fecg-image-digitizer).",
      "votes": 1,
      "replies": [
        {
          "id": 3313222,
          "postDate": "2025-11-09T00:00:10.900Z",
          "content": "<p>The essence of my question is whether green and purple arrows represent the same time period of the waveform or is it only a grid mapping artifact to preserve the best visual appearance, i.e. sharp, single pixel wide pink lines, and avoid antialiasing, which would make the grid look less sharp. I'm assuming that the waveform time period per pixel is uniform across the whole width of image type 0001. Is that accurate?</p>\n<p>Skipping pixels is not necessary for waveform quantization in images of 0001 type. With 8 pixel size of the small grid, you have 25*8=200 pixels (1 s of waveform) to represent at the minimum 250 values (fs=250).</p>",
          "rawMarkdown": "The essence of my question is whether green and purple arrows represent the same time period of the waveform or is it only a grid mapping artifact to preserve the best visual appearance, i.e. sharp, single pixel wide pink lines, and avoid antialiasing, which would make the grid look less sharp. I'm assuming that the waveform time period per pixel is uniform across the whole width of image type 0001. Is that accurate?\n\nSkipping pixels is not necessary for waveform quantization in images of 0001 type. With 8 pixel size of the small grid, you have 25*8=200 pixels (1 s of waveform) to represent at the minimum 250 values (fs=250).",
          "votes": 1,
          "replies": [
            {
              "id": 3313311,
              "postDate": "2025-11-09T04:21:03.523Z",
              "content": "<p>As explained in <a href=\"https://doi.org/10.48550/arXiv.2409.16612\" target=\"_blank\">Appendix A of the ECG-Image-Database preprint</a>, after conversion to an image, we should consider the image DPI, not the original signal's sampling frequency. Even in that case, due to inevitable round-off and quantization errors, the distance between successive grid lines will not have exactly the same number of pixels between all grid lines because, as noted <a href=\"https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614972#3312744\" target=\"_blank\">here</a>, the distance between the horizontal and vertical lines when printed as an image is not an integer. But if you count the number of pixels between grid lines that are far apart and divide it by the number of grids in between, you get very close to the expected number of pixels (which is non-integer).</p>",
              "rawMarkdown": "As explained in [Appendix A of the ECG-Image-Database preprint](https://doi.org/10.48550/arXiv.2409.16612), after conversion to an image, we should consider the image DPI, not the original signal's sampling frequency. Even in that case, due to inevitable round-off and quantization errors, the distance between successive grid lines will not have exactly the same number of pixels between all grid lines because, as noted [here](https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614972#3312744), the distance between the horizontal and vertical lines when printed as an image is not an integer. But if you count the number of pixels between grid lines that are far apart and divide it by the number of grids in between, you get very close to the expected number of pixels (which is non-integer).",
              "votes": 2
            }
          ]
        }
      ]
    },
    {
      "id": 3312737,
      "postDate": "2025-11-07T20:27:26.650Z",
      "content": "<p>it is not a perfect square either after you factor in subpixel decimals. It is not an error, but limit of quantisation. \nDespite quantisation error, max snr 28.5 db is achievable.</p>\n<p>if you really want to be very accurate, you need to project this into a new reference with equal grid. you need to have sub pixel coordinate of the current grid lines (e.g. 7.2, 6.8) which you can train a network to do the nudging. Then you can get snr in 60 ranges.</p>",
      "rawMarkdown": "it is not a perfect square either after you factor in subpixel decimals. It is not an error, but limit of quantisation. \nDespite quantisation error, max snr 28.5 db is achievable.\n\nif you really want to be very accurate, you need to project this into a new reference with equal grid. you need to have sub pixel coordinate of the current grid lines (e.g. 7.2, 6.8) which you can train a network to do the nudging. Then you can get snr in 60 ranges.\n\n",
      "votes": 1,
      "replies": [
        {
          "id": 3312750,
          "postDate": "2025-11-07T21:12:04.843Z",
          "content": "<p>Except that it doesn't look like projecting a grid with non-integral dimensions onto a uniform square pixel sensor or printer. If that was a case, instead of a single pixel wide pink lines, we would have, in general, lines overlapping two pixel wide bands. Unless a sloppy reduction to the nearest integral position was used.</p>\n<p>Projection to a uniform size grid is needed, as you noticed. I'm assuming that ignoring the 1 mm grid is the way to go, due to large distortions of 7/6. It seems that projecting the 5 mm grid to a uniform size is the way to go. Also, 1 mm grid is visible only on type 0001 images.</p>",
          "rawMarkdown": "Except that it doesn't look like projecting a grid with non-integral dimensions onto a uniform square pixel sensor or printer. If that was a case, instead of a single pixel wide pink lines, we would have, in general, lines overlapping two pixel wide bands. Unless a sloppy reduction to the nearest integral position was used.\n\nProjection to a uniform size grid is needed, as you noticed. I'm assuming that ignoring the 1 mm grid is the way to go, due to large distortions of 7/6. It seems that projecting the 5 mm grid to a uniform size is the way to go. Also, 1 mm grid is visible only on type 0001 images.",
          "votes": 1
        }
      ]
    },
    {
      "id": 3334424,
      "postDate": "2025-11-17T14:32:21.567Z",
      "content": "<p>no wonder my homography does not work!<br>\n0001 superimposed with 0004, the misalignment only happens \"suddenly\" at the end   </p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87f1ae226af1d0b6a2a078ffffd1a5b1%2FSelection_1011.png?generation=1763389850957833&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F9a9df2b052e8fc243e85c11df1ebe82d%2FSelection_1012.png?generation=1763389861386390&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38f92bd09ed6cdb9382034213bdbe2e3%2FSelection_1013.png?generation=1763389871673536&amp;alt=media\" alt=\"\"></p>\n<p>host making the competition difficult</p>",
      "rawMarkdown": "no wonder my homography does not work!  \n0001 superimposed with 0004, the misalignment only happens \"suddenly\" at the end   \n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87f1ae226af1d0b6a2a078ffffd1a5b1%2FSelection_1011.png?generation=1763389850957833&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F9a9df2b052e8fc243e85c11df1ebe82d%2FSelection_1012.png?generation=1763389861386390&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38f92bd09ed6cdb9382034213bdbe2e3%2FSelection_1013.png?generation=1763389871673536&alt=media)\n\nhost making the competition difficult",
      "votes": 2,
      "replies": [
        {
          "id": 3334456,
          "postDate": "2025-11-17T14:44:00.033Z",
          "content": "<p>Scanners are mechanical, so their motor speeds aren't always stable. This is an example of the type of problem we see in the real world. This can also be generated as a digital artifact when rotating an image - the distortions can be exacerbated far away from the point of rotation (although I don't think that's what is happening here).</p>",
          "rawMarkdown": "Scanners are mechanical, so their motor speeds aren't always stable. This is an example of the type of problem we see in the real world. This can also be generated as a digital artifact when rotating an image - the distortions can be exacerbated far away from the point of rotation (although I don't think that's what is happening here).",
          "votes": 3,
          "replies": [
            {
              "id": 3336066,
              "postDate": "2025-11-18T10:10:30.760Z",
              "content": "<p>The motor speed is stable, but the paper can slip in a sheetfed scanner. This type of distortion will not be present in a flatbed scanner. </p>",
              "rawMarkdown": "The motor speed is stable, but the paper can slip in a sheetfed scanner. This type of distortion will not be present in a flatbed scanner. ",
              "votes": 1
            },
            {
              "id": 3336229,
              "postDate": "2025-11-18T11:59:58.170Z",
              "content": "<p>Sheet feeder slippage seems more likely, but errors do happen on flatbed scanners too, especially older or lower-cost models that may use simpler stepper or DC motors. It also depends on the orientation of the document:  <a href=\"https://pmc.ncbi.nlm.nih.gov/articles/PMC5508593/\" target=\"_blank\">https://pmc.ncbi.nlm.nih.gov/articles/PMC5508593/</a></p>",
              "rawMarkdown": "Sheet feeder slippage seems more likely, but errors do happen on flatbed scanners too, especially older or lower-cost models that may use simpler stepper or DC motors. It also depends on the orientation of the document:  https://pmc.ncbi.nlm.nih.gov/articles/PMC5508593/",
              "votes": 1
            },
            {
              "id": 3338165,
              "postDate": "2025-11-18T22:38:44.320Z",
              "content": "<p>The flatbed scanner errors discussed in this paper are smaller than the pixel size of images used in this competition. They measured the direction of scan error of 0.05/420 = 1/8400, which is less than 1/2 of a pixel for 4K images, the largest provided here. This is just a side note, not relevant to the competition. What is relevant, are the grid distortion for all types of images</p>",
              "rawMarkdown": "The flatbed scanner errors discussed in this paper are smaller than the pixel size of images used in this competition. They measured the direction of scan error of 0.05/420 = 1/8400, which is less than 1/2 of a pixel for 4K images, the largest provided here. This is just a side note, not relevant to the competition. What is relevant, are the grid distortion for all types of images",
              "votes": 1
            }
          ]
        }
      ]
    },
    {
      "id": 3313437,
      "postDate": "2025-11-09T09:08:58.653Z",
      "content": "<p>It is because the DPI -&gt; DPmm conversion. the images were generated in 200 DPI. 1 inch = 25.4mm, so we have 200/25.4 = 7.874px/mm. So roughly every 8th pixel is a line, but sometimes because of the rounding the line is on the 7th pixel. </p>",
      "rawMarkdown": "It is because the DPI -> DPmm conversion. the images were generated in 200 DPI. 1 inch = 25.4mm, so we have 200/25.4 = 7.874px/mm. So roughly every 8th pixel is a line, but sometimes because of the rounding the line is on the 7th pixel. ",
      "votes": 2,
      "replies": [
        {
          "id": 3313461,
          "postDate": "2025-11-09T09:52:44.177Z",
          "content": "<p>Yes, I understand that part. My question was about non-uniformity of the timescale of the waveform itself. I'm working under the assumption that the grid is non-uniform for pretty visualization purposes, but the waveform timescale is uniform across the whole image width. More arguments below.</p>",
          "rawMarkdown": "Yes, I understand that part. My question was about non-uniformity of the timescale of the waveform itself. I'm working under the assumption that the grid is non-uniform for pretty visualization purposes, but the waveform timescale is uniform across the whole image width. More arguments below.",
          "votes": 1
        }
      ]
    },
    {
      "id": 3312744,
      "postDate": "2025-11-07T21:02:57.673Z",
      "content": "<p>As <a href=\"https://www.kaggle.com/hengck23\" target=\"_blank\">@hengck23</a> notes, the scan isn't going to create an exact integer number of pixels in each square (unless you are really lucky), so some squares will be different pixel sizes to others. It's an interesting question whether you should 'warp' the data onto an equal sized grid, or if you should maintain the aspect ratio. (You don't need to train a network to do this - you can just perform a linear time warping.)  It's easy to test which will give you the best answer, though … Note also that you are likely to see the same effect in vertical grid. </p>",
      "rawMarkdown": "As @hengck23 notes, the scan isn't going to create an exact integer number of pixels in each square (unless you are really lucky), so some squares will be different pixel sizes to others. It's an interesting question whether you should 'warp' the data onto an equal sized grid, or if you should maintain the aspect ratio. (You don't need to train a network to do this - you can just perform a linear time warping.)  It's easy to test which will give you the best answer, though ... Note also that you are likely to see the same effect in vertical grid. ",
      "votes": 2,
      "replies": [
        {
          "id": 3312832,
          "postDate": "2025-11-08T04:27:28.897Z",
          "content": "<p>The scan is not a problem. My post was about image type 0001, which was generated by ECG-image-kit, which should be perfectly capable of outputting a uniform 1 mm grid. The question is whether the distortions on the 1 mm scale are real or they are an artifact of image width and height not being a multiple of 8 plus 1, e.g. the width is 2200, but 2241 is needed for 8 pixel size of the millimeter grid.</p>",
          "rawMarkdown": "The scan is not a problem. My post was about image type 0001, which was generated by ECG-image-kit, which should be perfectly capable of outputting a uniform 1 mm grid. The question is whether the distortions on the 1 mm scale are real or they are an artifact of image width and height not being a multiple of 8 plus 1, e.g. the width is 2200, but 2241 is needed for 8 pixel size of the millimeter grid.",
          "votes": 1,
          "replies": [
            {
              "id": 3312869,
              "postDate": "2025-11-08T06:04:00.733Z",
              "content": "<p>You want to generate 1mm per grid spacing but end up with eg 1.005 pixel spacing. There is no aligment issue for mm and pixel for the first few pixels … but as the length of pixels increased, you can see things will get out of sync and some pixel has to be dropped at some point of time ( like clock-shift in daylight saving time )</p>",
              "rawMarkdown": "You want to generate 1mm per grid spacing but end up with eg 1.005 pixel spacing. There is no aligment issue for mm and pixel for the first few pixels … but as the length of pixels increased, you can see things will get out of sync and some pixel has to be dropped at some point of time ( like clock-shift in daylight saving time )",
              "votes": 1
            },
            {
              "id": 3312872,
              "postDate": "2025-11-08T06:07:53.423Z",
              "content": "<p>“Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.“.  </p>\n<p>The actual grid size is eg 6.8934. And it probably start at 0 in x direction and 0.1 in the y direction, leading to things like 6x7 instead of 6x6.</p>\n<p>There is a whole topics on precise measurement in computer vision where there are method for subpixel x,y coordinates in images … sampling, antialiasing, quantisation …</p>",
              "rawMarkdown": "“Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.“.  \n\nThe actual grid size is eg 6.8934. And it probably start at 0 in x direction and 0.1 in the y direction, leading to things like 6x7 instead of 6x6.\n\n\nThere is a whole topics on precise measurement in computer vision where there are method for subpixel x,y coordinates in images … sampling, antialiasing, quantisation …",
              "votes": 1
            },
            {
              "id": 3313191,
              "postDate": "2025-11-08T21:42:15.630Z",
              "content": "<p>Dropping pixels is the crudest technique to accomplish this goal. Anti-aliasing solves the problem without dropping pixels.</p>\n<p>I noticed that in your post <a href=\"https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614067\" target=\"_blank\">https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614067</a> you are rectifying only the 5 mm grid, which ignores much larger local distortions of 1 mm grid. I doubt that they are real, though. Most likely, they are just unintended artifacts. I will make a post later with results of overlaying the test waveforms on test images of type 0001.</p>",
              "rawMarkdown": "Dropping pixels is the crudest technique to accomplish this goal. Anti-aliasing solves the problem without dropping pixels.\n\nI noticed that in your post https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614067 you are rectifying only the 5 mm grid, which ignores much larger local distortions of 1 mm grid. I doubt that they are real, though. Most likely, they are just unintended artifacts. I will make a post later with results of overlaying the test waveforms on test images of type 0001.",
              "votes": 1
            },
            {
              "id": 3313249,
              "postDate": "2025-11-09T02:16:33.523Z",
              "content": "<p>with my 5mm grid annotation (and \"ground truth waveform\" extracted from image), i can recover the signals at 50 db snr using my method . it shows:</p>\n<ul>\n<li>5mm annotation is good enough</li>\n<li>even if there is pixel error, in 5mm annotation, my method (iterative algotithm to refine \"ground truth waveform\" to subpixel and nudging it by subpixel flow) can handle it.</li>\n</ul>\n<p>i.e. i can take csv values and draw ground truth waveform on 0001 image (0003, 0005 … etc) which visually are within pixel error (dift at most 1 px errot)</p>\n<hr>\n<p>for 0001 image, cannot take total length/number of grid cells. they are not uniform. you have two choices<br>\n1) treat 0001 as a distorted image and make another reference with uniform grid (you need to know how to draw the waveform or project from 0001 to this uniform reference)<br>\n2) or your digitilalisation algorithm does not assume uniform cell</p>",
              "rawMarkdown": "with my 5mm grid annotation (and \"ground truth waveform\" extracted from image), i can recover the signals at 50 db snr using my method . it shows:\n- 5mm annotation is good enough\n- even if there is pixel error, in 5mm annotation, my method (iterative algotithm to refine \"ground truth waveform\" to subpixel and nudging it by subpixel flow) can handle it.\n\ni.e. i can take csv values and draw ground truth waveform on 0001 image (0003, 0005 ... etc) which visually are within pixel error (dift at most 1 px errot)\n \n---\n\nfor 0001 image, cannot take total length/number of grid cells. they are not uniform. you have two choices  \n1) treat 0001 as a distorted image and make another reference with uniform grid (you need to know how to draw the waveform or project from 0001 to this uniform reference)  \n2) or your digitilalisation algorithm does not assume uniform cell",
              "votes": 2
            },
            {
              "id": 3313254,
              "postDate": "2025-11-09T02:42:47.853Z",
              "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc503489478be7a3500633e7760292504%2FSelection_937.png?generation=1762656212959177&amp;alt=media\" alt=\"\"></p>\n<p>my wave annotation is not good enough, so i use a deepnet to refine to a better wave annotation that can gives high &gt;50 snr. on 0001 (project to new reference), training data can get 40db  and validation data at 25db. </p>\n<p>my grid annotation for 0003, 004 …  are at 1 to 2 pixel error. now i am experimenting with nugding  the grid point with a refinement net or directly work with inaccurate grid.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38fa10e229fdab1567dfafd33367d804%2FSelection_940.png?generation=1762656328983974&amp;alt=media\" alt=\"\">\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fe8c824fce19d202a8446cbf7b3c06814%2FSelection_941.png?generation=1762656344886144&amp;alt=media\" alt=\"\"></p>",
              "rawMarkdown": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc503489478be7a3500633e7760292504%2FSelection_937.png?generation=1762656212959177&alt=media)\n\nmy wave annotation is not good enough, so i use a deepnet to refine to a better wave annotation that can gives high >50 snr. on 0001 (project to new reference), training data can get 40db  and validation data at 25db. \n\nmy grid annotation for 0003, 004 ...  are at 1 to 2 pixel error. now i am experimenting with nugding  the grid point with a refinement net or directly work with inaccurate grid.\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38fa10e229fdab1567dfafd33367d804%2FSelection_940.png?generation=1762656328983974&alt=media)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fe8c824fce19d202a8446cbf7b3c06814%2FSelection_941.png?generation=1762656344886144&alt=media)\n",
              "votes": 2
            },
            {
              "id": 3313361,
              "postDate": "2025-11-09T06:47:32.643Z",
              "content": "<blockquote>\n  <p>treat 0001 as a distorted image</p>\n</blockquote>\n<p>I disagree. True, the grid is not uniform, but the non-uniformity is only there for better visualization. The time axis has a uniform second/pixel scale. I tested it with a few train images and the train waveform overlap with the printed waveform on 0001 images is perfect. No need for scale distortions.</p>",
              "rawMarkdown": ">treat 0001 as a distorted image\n\nI disagree. True, the grid is not uniform, but the non-uniformity is only there for better visualization. The time axis has a uniform second/pixel scale. I tested it with a few train images and the train waveform overlap with the printed waveform on 0001 images is perfect. No need for scale distortions.",
              "votes": 1
            },
            {
              "id": 3313373,
              "postDate": "2025-11-09T07:22:13.800Z",
              "content": "<p>so long you can find a pipline to recover high snr of 0001 for supervised targets for ml methods, it is ok. this is the upper limit. training results will be worse and validation will further worsen (depending on generalisation of method and data).</p>\n<p>Key is not to recover the pixel mask but the snr values (i.e. regression target). Need to pay attention to loss in alignment of segmentation and regression loss.</p>\n<p>i think there will be bigger issues for non 0001 images because the distortion there is larger. here upper limit of snr will drop again.</p>\n<p>in this competition we can work out the expected best results, an almost shakeup free competition</p>",
              "rawMarkdown": "so long you can find a pipline to recover high snr of 0001 for supervised targets for ml methods, it is ok. this is the upper limit. training results will be worse and validation will further worsen (depending on generalisation of method and data).\n\nKey is not to recover the pixel mask but the snr values (i.e. regression target). Need to pay attention to loss in alignment of segmentation and regression loss.\n\ni think there will be bigger issues for non 0001 images because the distortion there is larger. here upper limit of snr will drop again.\n\nin this competition we can work out the expected best results, an almost shakeup free competition",
              "votes": 2
            }
          ]
        }
      ]
    },
    {
      "id": 3334323,
      "postDate": "2025-11-17T13:50:36.843Z",
      "rawMarkdown": "",
      "isDeleted": true
    }
  ],
  "comments": [
    {
      "id": 3313206,
      "author_name": "Reza Sameni",
      "author_url": "",
      "post_date": "2025-11-08T22:22:21.587000",
      "content": "<p>As Gari noted, these are quantization effects of mapping time series to images. The photographed images have additional distortions due to natural imaging artifacts and perspectives. ECG-Image-Kit has a few functions with ideas for estimating grid size in mV and seconds from the images, using histograms and spectral methods. Check out the functions starting with <code>ecg_gridest</code> <a href=\"https://github.com/alphanumericslab/ecg-image-kit/tree/main/codes%2Fecg-image-digitizer\" target=\"_blank\">here</a>.</p>",
      "votes": 1,
      "replies": [
        {
          "id": 3313222,
          "author_name": "Paul Jurczak",
          "author_url": "",
          "post_date": "2025-11-09T00:00:10.900000",
          "content": "<p>The essence of my question is whether green and purple arrows represent the same time period of the waveform or is it only a grid mapping artifact to preserve the best visual appearance, i.e. sharp, single pixel wide pink lines, and avoid antialiasing, which would make the grid look less sharp. I'm assuming that the waveform time period per pixel is uniform across the whole width of image type 0001. Is that accurate?</p>\n<p>Skipping pixels is not necessary for waveform quantization in images of 0001 type. With 8 pixel size of the small grid, you have 25*8=200 pixels (1 s of waveform) to represent at the minimum 250 values (fs=250).</p>",
          "votes": 1,
          "replies": [
            {
              "id": 3313311,
              "author_name": "Reza Sameni",
              "author_url": "",
              "post_date": "2025-11-09T04:21:03.523000",
              "content": "<p>As explained in <a href=\"https://doi.org/10.48550/arXiv.2409.16612\" target=\"_blank\">Appendix A of the ECG-Image-Database preprint</a>, after conversion to an image, we should consider the image DPI, not the original signal's sampling frequency. Even in that case, due to inevitable round-off and quantization errors, the distance between successive grid lines will not have exactly the same number of pixels between all grid lines because, as noted <a href=\"https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614972#3312744\" target=\"_blank\">here</a>, the distance between the horizontal and vertical lines when printed as an image is not an integer. But if you count the number of pixels between grid lines that are far apart and divide it by the number of grids in between, you get very close to the expected number of pixels (which is non-integer).</p>",
              "votes": 2,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 3312737,
      "author_name": "hengck23",
      "author_url": "",
      "post_date": "2025-11-07T20:27:26.650000",
      "content": "<p>it is not a perfect square either after you factor in subpixel decimals. It is not an error, but limit of quantisation. \nDespite quantisation error, max snr 28.5 db is achievable.</p>\n<p>if you really want to be very accurate, you need to project this into a new reference with equal grid. you need to have sub pixel coordinate of the current grid lines (e.g. 7.2, 6.8) which you can train a network to do the nudging. Then you can get snr in 60 ranges.</p>",
      "votes": 1,
      "replies": [
        {
          "id": 3312750,
          "author_name": "Paul Jurczak",
          "author_url": "",
          "post_date": "2025-11-07T21:12:04.843000",
          "content": "<p>Except that it doesn't look like projecting a grid with non-integral dimensions onto a uniform square pixel sensor or printer. If that was a case, instead of a single pixel wide pink lines, we would have, in general, lines overlapping two pixel wide bands. Unless a sloppy reduction to the nearest integral position was used.</p>\n<p>Projection to a uniform size grid is needed, as you noticed. I'm assuming that ignoring the 1 mm grid is the way to go, due to large distortions of 7/6. It seems that projecting the 5 mm grid to a uniform size is the way to go. Also, 1 mm grid is visible only on type 0001 images.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 3334424,
      "author_name": "hengck23",
      "author_url": "",
      "post_date": "2025-11-17T14:32:21.567000",
      "content": "<p>no wonder my homography does not work!<br>\n0001 superimposed with 0004, the misalignment only happens \"suddenly\" at the end   </p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87f1ae226af1d0b6a2a078ffffd1a5b1%2FSelection_1011.png?generation=1763389850957833&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F9a9df2b052e8fc243e85c11df1ebe82d%2FSelection_1012.png?generation=1763389861386390&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38f92bd09ed6cdb9382034213bdbe2e3%2FSelection_1013.png?generation=1763389871673536&amp;alt=media\" alt=\"\"></p>\n<p>host making the competition difficult</p>",
      "votes": 2,
      "replies": [
        {
          "id": 3334456,
          "author_name": "GDClifford",
          "author_url": "",
          "post_date": "2025-11-17T14:44:00.033000",
          "content": "<p>Scanners are mechanical, so their motor speeds aren't always stable. This is an example of the type of problem we see in the real world. This can also be generated as a digital artifact when rotating an image - the distortions can be exacerbated far away from the point of rotation (although I don't think that's what is happening here).</p>",
          "votes": 3,
          "replies": [
            {
              "id": 3336066,
              "author_name": "Paul Jurczak",
              "author_url": "",
              "post_date": "2025-11-18T10:10:30.760000",
              "content": "<p>The motor speed is stable, but the paper can slip in a sheetfed scanner. This type of distortion will not be present in a flatbed scanner. </p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3336229,
              "author_name": "GDClifford",
              "author_url": "",
              "post_date": "2025-11-18T11:59:58.170000",
              "content": "<p>Sheet feeder slippage seems more likely, but errors do happen on flatbed scanners too, especially older or lower-cost models that may use simpler stepper or DC motors. It also depends on the orientation of the document:  <a href=\"https://pmc.ncbi.nlm.nih.gov/articles/PMC5508593/\" target=\"_blank\">https://pmc.ncbi.nlm.nih.gov/articles/PMC5508593/</a></p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3338165,
              "author_name": "Paul Jurczak",
              "author_url": "",
              "post_date": "2025-11-18T22:38:44.320000",
              "content": "<p>The flatbed scanner errors discussed in this paper are smaller than the pixel size of images used in this competition. They measured the direction of scan error of 0.05/420 = 1/8400, which is less than 1/2 of a pixel for 4K images, the largest provided here. This is just a side note, not relevant to the competition. What is relevant, are the grid distortion for all types of images</p>",
              "votes": 1,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 3313437,
      "author_name": "Peter",
      "author_url": "",
      "post_date": "2025-11-09T09:08:58.653000",
      "content": "<p>It is because the DPI -&gt; DPmm conversion. the images were generated in 200 DPI. 1 inch = 25.4mm, so we have 200/25.4 = 7.874px/mm. So roughly every 8th pixel is a line, but sometimes because of the rounding the line is on the 7th pixel. </p>",
      "votes": 2,
      "replies": [
        {
          "id": 3313461,
          "author_name": "Paul Jurczak",
          "author_url": "",
          "post_date": "2025-11-09T09:52:44.177000",
          "content": "<p>Yes, I understand that part. My question was about non-uniformity of the timescale of the waveform itself. I'm working under the assumption that the grid is non-uniform for pretty visualization purposes, but the waveform timescale is uniform across the whole image width. More arguments below.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 3312744,
      "author_name": "GDClifford",
      "author_url": "",
      "post_date": "2025-11-07T21:02:57.673000",
      "content": "<p>As <a href=\"https://www.kaggle.com/hengck23\" target=\"_blank\">@hengck23</a> notes, the scan isn't going to create an exact integer number of pixels in each square (unless you are really lucky), so some squares will be different pixel sizes to others. It's an interesting question whether you should 'warp' the data onto an equal sized grid, or if you should maintain the aspect ratio. (You don't need to train a network to do this - you can just perform a linear time warping.)  It's easy to test which will give you the best answer, though … Note also that you are likely to see the same effect in vertical grid. </p>",
      "votes": 2,
      "replies": [
        {
          "id": 3312832,
          "author_name": "Paul Jurczak",
          "author_url": "",
          "post_date": "2025-11-08T04:27:28.897000",
          "content": "<p>The scan is not a problem. My post was about image type 0001, which was generated by ECG-image-kit, which should be perfectly capable of outputting a uniform 1 mm grid. The question is whether the distortions on the 1 mm scale are real or they are an artifact of image width and height not being a multiple of 8 plus 1, e.g. the width is 2200, but 2241 is needed for 8 pixel size of the millimeter grid.</p>",
          "votes": 1,
          "replies": [
            {
              "id": 3312869,
              "author_name": "hengck23",
              "author_url": "",
              "post_date": "2025-11-08T06:04:00.733000",
              "content": "<p>You want to generate 1mm per grid spacing but end up with eg 1.005 pixel spacing. There is no aligment issue for mm and pixel for the first few pixels … but as the length of pixels increased, you can see things will get out of sync and some pixel has to be dropped at some point of time ( like clock-shift in daylight saving time )</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3312872,
              "author_name": "hengck23",
              "author_url": "",
              "post_date": "2025-11-08T06:07:53.423000",
              "content": "<p>“Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.“.  </p>\n<p>The actual grid size is eg 6.8934. And it probably start at 0 in x direction and 0.1 in the y direction, leading to things like 6x7 instead of 6x6.</p>\n<p>There is a whole topics on precise measurement in computer vision where there are method for subpixel x,y coordinates in images … sampling, antialiasing, quantisation …</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3313191,
              "author_name": "Paul Jurczak",
              "author_url": "",
              "post_date": "2025-11-08T21:42:15.630000",
              "content": "<p>Dropping pixels is the crudest technique to accomplish this goal. Anti-aliasing solves the problem without dropping pixels.</p>\n<p>I noticed that in your post <a href=\"https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614067\" target=\"_blank\">https://www.kaggle.com/competitions/physionet-ecg-image-digitization/discussion/614067</a> you are rectifying only the 5 mm grid, which ignores much larger local distortions of 1 mm grid. I doubt that they are real, though. Most likely, they are just unintended artifacts. I will make a post later with results of overlaying the test waveforms on test images of type 0001.</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3313249,
              "author_name": "hengck23",
              "author_url": "",
              "post_date": "2025-11-09T02:16:33.523000",
              "content": "<p>with my 5mm grid annotation (and \"ground truth waveform\" extracted from image), i can recover the signals at 50 db snr using my method . it shows:</p>\n<ul>\n<li>5mm annotation is good enough</li>\n<li>even if there is pixel error, in 5mm annotation, my method (iterative algotithm to refine \"ground truth waveform\" to subpixel and nudging it by subpixel flow) can handle it.</li>\n</ul>\n<p>i.e. i can take csv values and draw ground truth waveform on 0001 image (0003, 0005 … etc) which visually are within pixel error (dift at most 1 px errot)</p>\n<hr>\n<p>for 0001 image, cannot take total length/number of grid cells. they are not uniform. you have two choices<br>\n1) treat 0001 as a distorted image and make another reference with uniform grid (you need to know how to draw the waveform or project from 0001 to this uniform reference)<br>\n2) or your digitilalisation algorithm does not assume uniform cell</p>",
              "votes": 2,
              "replies": []
            },
            {
              "id": 3313254,
              "author_name": "hengck23",
              "author_url": "",
              "post_date": "2025-11-09T02:42:47.853000",
              "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc503489478be7a3500633e7760292504%2FSelection_937.png?generation=1762656212959177&amp;alt=media\" alt=\"\"></p>\n<p>my wave annotation is not good enough, so i use a deepnet to refine to a better wave annotation that can gives high &gt;50 snr. on 0001 (project to new reference), training data can get 40db  and validation data at 25db. </p>\n<p>my grid annotation for 0003, 004 …  are at 1 to 2 pixel error. now i am experimenting with nugding  the grid point with a refinement net or directly work with inaccurate grid.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38fa10e229fdab1567dfafd33367d804%2FSelection_940.png?generation=1762656328983974&amp;alt=media\" alt=\"\">\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fe8c824fce19d202a8446cbf7b3c06814%2FSelection_941.png?generation=1762656344886144&amp;alt=media\" alt=\"\"></p>",
              "votes": 2,
              "replies": []
            },
            {
              "id": 3313361,
              "author_name": "Paul Jurczak",
              "author_url": "",
              "post_date": "2025-11-09T06:47:32.643000",
              "content": "<blockquote>\n  <p>treat 0001 as a distorted image</p>\n</blockquote>\n<p>I disagree. True, the grid is not uniform, but the non-uniformity is only there for better visualization. The time axis has a uniform second/pixel scale. I tested it with a few train images and the train waveform overlap with the printed waveform on 0001 images is perfect. No need for scale distortions.</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3313373,
              "author_name": "hengck23",
              "author_url": "",
              "post_date": "2025-11-09T07:22:13.800000",
              "content": "<p>so long you can find a pipline to recover high snr of 0001 for supervised targets for ml methods, it is ok. this is the upper limit. training results will be worse and validation will further worsen (depending on generalisation of method and data).</p>\n<p>Key is not to recover the pixel mask but the snr values (i.e. regression target). Need to pay attention to loss in alignment of segmentation and regression loss.</p>\n<p>i think there will be bigger issues for non 0001 images because the distortion there is larger. here upper limit of snr will drop again.</p>\n<p>in this competition we can work out the expected best results, an almost shakeup free competition</p>",
              "votes": 2,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 3334323,
      "author_name": "",
      "author_url": "",
      "post_date": "2025-11-17T13:50:36.843000",
      "content": "",
      "votes": 0,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "3312730": "I was surprised to find horizontal grid spacing artifacts on type 0001 images. Almost all millimeter grid squares have 7x7 pixel size (purple arrows), but some are compressed to 6x7 size (green arrows) or even 6x6 size.\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F363811%2F7c6e532ac163c2a9a7071742c7c1c98e%2Fgrid-artifact.png?generation=1762544650331550&alt=media)\n\nIs this an error, or should we assume that the waveform's time and voltage scale is also compressed in this area?\n\n---\n\nAfter looking at the source code of ECG-Image-Kit, which was used to generate type 0001 images, specifically function `ecg_plot()`, I conclude that grid non-uniformity artifacts are created by Mathplotlib plotting functions and the waveform time base is uniform across the width of the image. This is where the waveform is plotted:\n```\nt1 = ax.plot(np.arange(0,len(ecg[leadName])*step,step) + x_offset + dc_offset + x_gap, \n        ecg[leadName] + y_offset,\n        linewidth=line_width, \n        color=color_line\n        )\n```\n",
    "3313206": "As Gari noted, these are quantization effects of mapping time series to images. The photographed images have additional distortions due to natural imaging artifacts and perspectives. ECG-Image-Kit has a few functions with ideas for estimating grid size in mV and seconds from the images, using histograms and spectral methods. Check out the functions starting with `ecg_gridest` [here](https://github.com/alphanumericslab/ecg-image-kit/tree/main/codes%2Fecg-image-digitizer).",
    "3312737": "it is not a perfect square either after you factor in subpixel decimals. It is not an error, but limit of quantisation. \nDespite quantisation error, max snr 28.5 db is achievable.\n\nif you really want to be very accurate, you need to project this into a new reference with equal grid. you need to have sub pixel coordinate of the current grid lines (e.g. 7.2, 6.8) which you can train a network to do the nudging. Then you can get snr in 60 ranges.\n\n",
    "3334424": "no wonder my homography does not work!  \n0001 superimposed with 0004, the misalignment only happens \"suddenly\" at the end   \n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87f1ae226af1d0b6a2a078ffffd1a5b1%2FSelection_1011.png?generation=1763389850957833&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F9a9df2b052e8fc243e85c11df1ebe82d%2FSelection_1012.png?generation=1763389861386390&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F38f92bd09ed6cdb9382034213bdbe2e3%2FSelection_1013.png?generation=1763389871673536&alt=media)\n\nhost making the competition difficult",
    "3313437": "It is because the DPI -> DPmm conversion. the images were generated in 200 DPI. 1 inch = 25.4mm, so we have 200/25.4 = 7.874px/mm. So roughly every 8th pixel is a line, but sometimes because of the rounding the line is on the 7th pixel. ",
    "3312744": "As @hengck23 notes, the scan isn't going to create an exact integer number of pixels in each square (unless you are really lucky), so some squares will be different pixel sizes to others. It's an interesting question whether you should 'warp' the data onto an equal sized grid, or if you should maintain the aspect ratio. (You don't need to train a network to do this - you can just perform a linear time warping.)  It's easy to test which will give you the best answer, though ... Note also that you are likely to see the same effect in vertical grid. ",
    "3334323": ""
  }
}