{
  "id": 430662,
  "title": "Slice thickness vs. Image Position (Patient)",
  "url": "/competitions/rsna-2023-abdominal-trauma-detection/discussion/430662",
  "author_name": "Victor Shlepov",
  "post_date": "2023-08-10T16:48:12.432000",
  "votes": 2,
  "comment_count": 10,
  "views": 0,
  "content": "<p>Hi everyone,</p>\n<p>I would appreciate if someone could explain how the \"Slice Thickness\" attribute in DICOM files relates to the numbers in \"Image Position (Patient)\" field? Logically, for any given series the difference of Image Position between two ajustent images along the z-axis should equal to \"Slice Thickness\", but this is not always the the case here.</p>\n<p>Just a little code snippet to illustrate what is just being said:</p>\n<pre><code>import pydicom\nimport numpy as np\nimport os\n\ndirectory = \nimg1 = pydicom(os(directory, ))\nimg2 = pydicom(os(directory, ))\nattribute = img1\nestimate = (img1 - img2) / (-)\n)\n</code></pre>\n<p>Should you execute the code above, the \"attribute\" would equal 1.0 mm, and estimate - 0.5 mm. I've checked the the DICOM standard, but everything it says about Slice Thickness is that it's a \"Nominal slice thickness, in mm.\" Clearly, I must be missing something obvious here… </p>",
  "messages": [
    {
      "id": 2384034,
      "postDate": "2023-08-10T18:48:56.383Z",
      "content": "<p>SliceThickness isn't necessarily related to ImagePositionPatient. It might be, but doesn't need to be. </p>\n<p>All CT images are reconstructed from raw voxel data into the \"slices\" we see. Most CT scanners can acquire data detailed enough to produces sub-millimeter slice thicknesses. But, if we scan an abdomen/pelvis on a scanner capable of acquiring at .5mm, and then display every slice .. we'd have an enormous amount of images.</p>\n<p>The solution is to average together (Volume Averaging) data into the viewable slices we get .. which creates the SliceThickness value. Slices might have gaps between them, or they might overlap. In some cases SliceThickness is equal to the distance between slices and everyone rejoices!</p>\n<p>There are clinical reasons for differences in SliceThickness/Spacing as well as technical reasons built into the protocols.</p>",
      "rawMarkdown": "SliceThickness isn't necessarily related to ImagePositionPatient. It might be, but doesn't need to be. \n\nAll CT images are reconstructed from raw voxel data into the \"slices\" we see. Most CT scanners can acquire data detailed enough to produces sub-millimeter slice thicknesses. But, if we scan an abdomen/pelvis on a scanner capable of acquiring at .5mm, and then display every slice .. we'd have an enormous amount of images.\n\nThe solution is to average together (Volume Averaging) data into the viewable slices we get .. which creates the SliceThickness value. Slices might have gaps between them, or they might overlap. In some cases SliceThickness is equal to the distance between slices and everyone rejoices!\n\nThere are clinical reasons for differences in SliceThickness/Spacing as well as technical reasons built into the protocols.",
      "votes": 3,
      "replies": [
        {
          "id": 2384069,
          "postDate": "2023-08-10T19:21:05.490Z",
          "content": "<p>Hi David,</p>\n<p>Thank you for your answer! May re-ask the question in a slightly different manner: we have segmentations for some of the images here. I tried to calculate the \"organ volume\" for those - to make sure that one of my transformations worked well - so I took number of voxels per each organ and multiplied it by the voxel volume. Now the question comes:</p>\n<p>Should I use the \"Pixel Spacing\" for x- and y-axis and \"Slice Thickness\" for z-axis size of the voxel, OR calculate z-axis voxel size based on the \"Image Position Patient\" data instead?</p>\n<p>The first approach gives the liver volume estimate of around 6000 cm3 for the patient #10004, which seems kind of \"inhuman\" to me - somewhere around to \"bear-to-whale\" scale :)</p>",
          "rawMarkdown": "Hi David,\n\nThank you for your answer! May re-ask the question in a slightly different manner: we have segmentations for some of the images here. I tried to calculate the \"organ volume\" for those - to make sure that one of my transformations worked well - so I took number of voxels per each organ and multiplied it by the voxel volume. Now the question comes:\n\nShould I use the \"Pixel Spacing\" for x- and y-axis and \"Slice Thickness\" for z-axis size of the voxel, OR calculate z-axis voxel size based on the \"Image Position Patient\" data instead?\n\nThe first approach gives the liver volume estimate of around 6000 cm3 for the patient #10004, which seems kind of \"inhuman\" to me - somewhere around to \"bear-to-whale\" scale :)",
          "replies": [
            {
              "id": 2384098,
              "postDate": "2023-08-10T19:42:10.027Z",
              "content": "<p>I would use the ImagePositionPatient tag to get the Z axis location in mm. Then use the pixel spacing values to convert X and Y axis to mm.</p>\n<p>The challenge is that some series have slices at .5mm intervals and some at 3mm. So there may be 150 slices with Liver in one study, and only 25 in another. Theoretically, we should stack (average) slices to make the counts more similar.</p>",
              "rawMarkdown": "I would use the ImagePositionPatient tag to get the Z axis location in mm. Then use the pixel spacing values to convert X and Y axis to mm.\n\nThe challenge is that some series have slices at .5mm intervals and some at 3mm. So there may be 150 slices with Liver in one study, and only 25 in another. Theoretically, we should stack (average) slices to make the counts more similar.",
              "votes": 2
            },
            {
              "id": 2384111,
              "postDate": "2023-08-10T20:04:34.143Z",
              "content": "<p>Thank you, that was exactly the answer I was looking for. I still struggle to completely grasp the technical details, but that's alright. Ultimately, my natural expertise is in corporate finance, not medical imaging; it's simply more prudent to keep me away from real-life hospital settings :)</p>",
              "rawMarkdown": "Thank you, that was exactly the answer I was looking for. I still struggle to completely grasp the technical details, but that's alright. Ultimately, my natural expertise is in corporate finance, not medical imaging; it's simply more prudent to keep me away from real-life hospital settings :)"
            }
          ]
        }
      ]
    },
    {
      "id": 2384783,
      "postDate": "2023-08-11T04:39:49.533Z",
      "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F16224967%2F0a5f6e252705f4432458343ebdfc4060%2Funnamed.png?generation=1691728643268662&amp;alt=media\" alt=\"\"></p>\n<p>we get z-spacing in nifti file by average of positions interval. slice-thickness is slightly different from positions interval.<br>\nHere's a picture to help you understand.</p>",
      "rawMarkdown": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F16224967%2F0a5f6e252705f4432458343ebdfc4060%2Funnamed.png?generation=1691728643268662&alt=media)\n\nwe get z-spacing in nifti file by average of positions interval. slice-thickness is slightly different from positions interval.\nHere's a picture to help you understand.",
      "votes": 4
    },
    {
      "id": 2383933,
      "postDate": "2023-08-10T16:48:12.433Z",
      "content": "<p>Hi everyone,</p>\n<p>I would appreciate if someone could explain how the \"Slice Thickness\" attribute in DICOM files relates to the numbers in \"Image Position (Patient)\" field? Logically, for any given series the difference of Image Position between two ajustent images along the z-axis should equal to \"Slice Thickness\", but this is not always the the case here.</p>\n<p>Just a little code snippet to illustrate what is just being said:</p>\n<pre><code>import pydicom\nimport numpy as np\nimport os\n\ndirectory = \nimg1 = pydicom(os(directory, ))\nimg2 = pydicom(os(directory, ))\nattribute = img1\nestimate = (img1 - img2) / (-)\n)\n</code></pre>\n<p>Should you execute the code above, the \"attribute\" would equal 1.0 mm, and estimate - 0.5 mm. I've checked the the DICOM standard, but everything it says about Slice Thickness is that it's a \"Nominal slice thickness, in mm.\" Clearly, I must be missing something obvious here… </p>",
      "rawMarkdown": "Hi everyone,\n\nI would appreciate if someone could explain how the \"Slice Thickness\" attribute in DICOM files relates to the numbers in \"Image Position (Patient)\" field? Logically, for any given series the difference of Image Position between two ajustent images along the z-axis should equal to \"Slice Thickness\", but this is not always the the case here.\n\nJust a little code snippet to illustrate what is just being said:\n```\nimport pydicom\nimport numpy as np\nimport os\n\ndirectory = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/10004/21057'\nimg1 = pydicom.dcmread(os.path.join(directory, '171.dcm'))\nimg2 = pydicom.dcmread(os.path.join(directory, '1179.dcm'))\nattribute = img1.SliceThickness\nestimate = (img1.ImagePositionPatient[2] - img2.ImagePositionPatient[2]) / (1179-171)\nprint('Slice Thickness Attribute: {}, Slice Thickness Estimate: {}'.format(attribute, estimate))\n```\nShould you execute the code above, the \"attribute\" would equal 1.0 mm, and estimate - 0.5 mm. I've checked the the DICOM standard, but everything it says about Slice Thickness is that it's a \"Nominal slice thickness, in mm.\" Clearly, I must be missing something obvious here... ",
      "votes": 2
    },
    {
      "id": 2389131,
      "postDate": "2023-08-13T19:59:55.673Z",
      "content": "<p>Hi Victor,</p>\n<p>I just wrote a code snippet to find out whether the slices match across Nii and Dcm files. As shown below, I would say we don't need to worry about slice numbers, thickness, position, etc. since they are already matched in the original dataset. </p>\n<p>The only thing we need to address is to use the correct order of slides in Dcm given the arbitrary file labels.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6476260%2Fb893ac90bba3c471f198684be046ef8f%2FScreenshot%202023-08-13%20145818.jpg?generation=1691956830416834&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "Hi Victor,\n\nI just wrote a code snippet to find out whether the slices match across Nii and Dcm files. As shown below, I would say we don't need to worry about slice numbers, thickness, position, etc. since they are already matched in the original dataset. \n\nThe only thing we need to address is to use the correct order of slides in Dcm given the arbitrary file labels.\n  \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6476260%2Fb893ac90bba3c471f198684be046ef8f%2FScreenshot%202023-08-13%20145818.jpg?generation=1691956830416834&alt=media)",
      "replies": [
        {
          "id": 2389141,
          "postDate": "2023-08-13T20:09:03.923Z",
          "content": "<p>Agree 100%. I was just curious why the seemingly interchangeable numbers differ. But the illustration above showed where the difference comes form :)</p>\n<p>Are you sure about arbitrary file names? They seem to match the \"Instance Number\" attribute in DICOM header, I have not seen any inconsistencies so far. Here's a simple snippet to check (it should output the empty pd.DataFrame):</p>\n<pre><code>path = \nmeta = pd(path)\nmeta = meta(lambda x:   x == (x()())  , axis=)\n\n</code></pre>\n<p>Also, both Nibabel and SimpleITK (and most other libraries, I bet) seem to use DICOM header to stack files into 3D array. Or did I misunderstood your concern? </p>",
          "rawMarkdown": "Agree 100%. I was just curious why the seemingly interchangeable numbers differ. But the illustration above showed where the difference comes form :)\n\nAre you sure about arbitrary file names? They seem to match the \"Instance Number\" attribute in DICOM header, I have not seen any inconsistencies so far. Here's a simple snippet to check (it should output the empty pd.DataFrame):\n\n```\npath = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_dicom_tags.parquet'\nmeta = pd.read_parquet(path)\nmeta['Match'] = meta.apply(lambda x: 1 if x['InstanceNumber'] == int(x['path'].split('.')[0].split('/')[-1]) else 0, axis=1)\nprint(meta[meta['Match'] == 0])\n```\n\nAlso, both Nibabel and SimpleITK (and most other libraries, I bet) seem to use DICOM header to stack files into 3D array. Or did I misunderstood your concern? ",
          "replies": [
            {
              "id": 2389198,
              "postDate": "2023-08-13T21:23:42.937Z",
              "content": "<p>Oh by arbitrary I meant that the labels numbers wouldn't correspond to slice number in a 3D array of loaded Nii file. Other than that the ordered slice labels do correspond to Nii slices. :) </p>",
              "rawMarkdown": "Oh by arbitrary I meant that the labels numbers wouldn't correspond to slice number in a 3D array of loaded Nii file. Other than that the ordered slice labels do correspond to Nii slices. :) "
            },
            {
              "id": 2389202,
              "postDate": "2023-08-13T21:28:43.913Z",
              "rawMarkdown": "",
              "isDeleted": true
            },
            {
              "id": 2389204,
              "postDate": "2023-08-13T21:29:22.277Z",
              "content": "<p>Just inverse the order of labels - like LIFO…</p>",
              "rawMarkdown": "Just inverse the order of labels - like LIFO..."
            }
          ]
        }
      ]
    }
  ],
  "comments": [
    {
      "id": 2384034,
      "author_name": "David Roberts",
      "author_url": "",
      "post_date": "2023-08-10T18:48:56.383000",
      "content": "<p>SliceThickness isn't necessarily related to ImagePositionPatient. It might be, but doesn't need to be. </p>\n<p>All CT images are reconstructed from raw voxel data into the \"slices\" we see. Most CT scanners can acquire data detailed enough to produces sub-millimeter slice thicknesses. But, if we scan an abdomen/pelvis on a scanner capable of acquiring at .5mm, and then display every slice .. we'd have an enormous amount of images.</p>\n<p>The solution is to average together (Volume Averaging) data into the viewable slices we get .. which creates the SliceThickness value. Slices might have gaps between them, or they might overlap. In some cases SliceThickness is equal to the distance between slices and everyone rejoices!</p>\n<p>There are clinical reasons for differences in SliceThickness/Spacing as well as technical reasons built into the protocols.</p>",
      "votes": 3,
      "replies": [
        {
          "id": 2384069,
          "author_name": "Victor Shlepov",
          "author_url": "",
          "post_date": "2023-08-10T19:21:05.490000",
          "content": "<p>Hi David,</p>\n<p>Thank you for your answer! May re-ask the question in a slightly different manner: we have segmentations for some of the images here. I tried to calculate the \"organ volume\" for those - to make sure that one of my transformations worked well - so I took number of voxels per each organ and multiplied it by the voxel volume. Now the question comes:</p>\n<p>Should I use the \"Pixel Spacing\" for x- and y-axis and \"Slice Thickness\" for z-axis size of the voxel, OR calculate z-axis voxel size based on the \"Image Position Patient\" data instead?</p>\n<p>The first approach gives the liver volume estimate of around 6000 cm3 for the patient #10004, which seems kind of \"inhuman\" to me - somewhere around to \"bear-to-whale\" scale :)</p>",
          "votes": 0,
          "replies": [
            {
              "id": 2384098,
              "author_name": "David Roberts",
              "author_url": "",
              "post_date": "2023-08-10T19:42:10.027000",
              "content": "<p>I would use the ImagePositionPatient tag to get the Z axis location in mm. Then use the pixel spacing values to convert X and Y axis to mm.</p>\n<p>The challenge is that some series have slices at .5mm intervals and some at 3mm. So there may be 150 slices with Liver in one study, and only 25 in another. Theoretically, we should stack (average) slices to make the counts more similar.</p>",
              "votes": 2,
              "replies": []
            },
            {
              "id": 2384111,
              "author_name": "Victor Shlepov",
              "author_url": "",
              "post_date": "2023-08-10T20:04:34.143000",
              "content": "<p>Thank you, that was exactly the answer I was looking for. I still struggle to completely grasp the technical details, but that's alright. Ultimately, my natural expertise is in corporate finance, not medical imaging; it's simply more prudent to keep me away from real-life hospital settings :)</p>",
              "votes": 0,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 2384783,
      "author_name": "kuchoco97",
      "author_url": "",
      "post_date": "2023-08-11T04:39:49.533000",
      "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F16224967%2F0a5f6e252705f4432458343ebdfc4060%2Funnamed.png?generation=1691728643268662&amp;alt=media\" alt=\"\"></p>\n<p>we get z-spacing in nifti file by average of positions interval. slice-thickness is slightly different from positions interval.<br>\nHere's a picture to help you understand.</p>",
      "votes": 4,
      "replies": []
    },
    {
      "id": 2389131,
      "author_name": "Parham Mostame",
      "author_url": "",
      "post_date": "2023-08-13T19:59:55.673000",
      "content": "<p>Hi Victor,</p>\n<p>I just wrote a code snippet to find out whether the slices match across Nii and Dcm files. As shown below, I would say we don't need to worry about slice numbers, thickness, position, etc. since they are already matched in the original dataset. </p>\n<p>The only thing we need to address is to use the correct order of slides in Dcm given the arbitrary file labels.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6476260%2Fb893ac90bba3c471f198684be046ef8f%2FScreenshot%202023-08-13%20145818.jpg?generation=1691956830416834&amp;alt=media\" alt=\"\"></p>",
      "votes": 0,
      "replies": [
        {
          "id": 2389141,
          "author_name": "Victor Shlepov",
          "author_url": "",
          "post_date": "2023-08-13T20:09:03.923000",
          "content": "<p>Agree 100%. I was just curious why the seemingly interchangeable numbers differ. But the illustration above showed where the difference comes form :)</p>\n<p>Are you sure about arbitrary file names? They seem to match the \"Instance Number\" attribute in DICOM header, I have not seen any inconsistencies so far. Here's a simple snippet to check (it should output the empty pd.DataFrame):</p>\n<pre><code>path = \nmeta = pd(path)\nmeta = meta(lambda x:   x == (x()())  , axis=)\n\n</code></pre>\n<p>Also, both Nibabel and SimpleITK (and most other libraries, I bet) seem to use DICOM header to stack files into 3D array. Or did I misunderstood your concern? </p>",
          "votes": 0,
          "replies": [
            {
              "id": 2389198,
              "author_name": "Parham Mostame",
              "author_url": "",
              "post_date": "2023-08-13T21:23:42.937000",
              "content": "<p>Oh by arbitrary I meant that the labels numbers wouldn't correspond to slice number in a 3D array of loaded Nii file. Other than that the ordered slice labels do correspond to Nii slices. :) </p>",
              "votes": 0,
              "replies": []
            },
            {
              "id": 2389202,
              "author_name": "",
              "author_url": "",
              "post_date": "2023-08-13T21:28:43.913000",
              "content": "",
              "votes": 0,
              "replies": []
            },
            {
              "id": 2389204,
              "author_name": "Victor Shlepov",
              "author_url": "",
              "post_date": "2023-08-13T21:29:22.277000",
              "content": "<p>Just inverse the order of labels - like LIFO…</p>",
              "votes": 0,
              "replies": []
            }
          ]
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "2384034": "SliceThickness isn't necessarily related to ImagePositionPatient. It might be, but doesn't need to be. \n\nAll CT images are reconstructed from raw voxel data into the \"slices\" we see. Most CT scanners can acquire data detailed enough to produces sub-millimeter slice thicknesses. But, if we scan an abdomen/pelvis on a scanner capable of acquiring at .5mm, and then display every slice .. we'd have an enormous amount of images.\n\nThe solution is to average together (Volume Averaging) data into the viewable slices we get .. which creates the SliceThickness value. Slices might have gaps between them, or they might overlap. In some cases SliceThickness is equal to the distance between slices and everyone rejoices!\n\nThere are clinical reasons for differences in SliceThickness/Spacing as well as technical reasons built into the protocols.",
    "2384783": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F16224967%2F0a5f6e252705f4432458343ebdfc4060%2Funnamed.png?generation=1691728643268662&alt=media)\n\nwe get z-spacing in nifti file by average of positions interval. slice-thickness is slightly different from positions interval.\nHere's a picture to help you understand.",
    "2383933": "Hi everyone,\n\nI would appreciate if someone could explain how the \"Slice Thickness\" attribute in DICOM files relates to the numbers in \"Image Position (Patient)\" field? Logically, for any given series the difference of Image Position between two ajustent images along the z-axis should equal to \"Slice Thickness\", but this is not always the the case here.\n\nJust a little code snippet to illustrate what is just being said:\n```\nimport pydicom\nimport numpy as np\nimport os\n\ndirectory = '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/10004/21057'\nimg1 = pydicom.dcmread(os.path.join(directory, '171.dcm'))\nimg2 = pydicom.dcmread(os.path.join(directory, '1179.dcm'))\nattribute = img1.SliceThickness\nestimate = (img1.ImagePositionPatient[2] - img2.ImagePositionPatient[2]) / (1179-171)\nprint('Slice Thickness Attribute: {}, Slice Thickness Estimate: {}'.format(attribute, estimate))\n```\nShould you execute the code above, the \"attribute\" would equal 1.0 mm, and estimate - 0.5 mm. I've checked the the DICOM standard, but everything it says about Slice Thickness is that it's a \"Nominal slice thickness, in mm.\" Clearly, I must be missing something obvious here... ",
    "2389131": "Hi Victor,\n\nI just wrote a code snippet to find out whether the slices match across Nii and Dcm files. As shown below, I would say we don't need to worry about slice numbers, thickness, position, etc. since they are already matched in the original dataset. \n\nThe only thing we need to address is to use the correct order of slides in Dcm given the arbitrary file labels.\n  \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F6476260%2Fb893ac90bba3c471f198684be046ef8f%2FScreenshot%202023-08-13%20145818.jpg?generation=1691956830416834&alt=media)"
  }
}