# Nifti (RPI orientation) file exported to DICOM - wrong orientation

**URL:** https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612
**Category:** Uncategorized
**Created:** [February 19, 2019, 9:01pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612 "2019-02-19T21:01:25Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [February 19, 2019, 9:01pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/1 "2019-02-19T21:01:25Z")

</div>

Hello,

Just FYI.

experiment:  
take this Nifti file  
[https://nifti.nimh.nih.gov/nifti-1/data/avg152T1\_LR\_nifti.nii.gz/file\_view](https://nifti.nimh.nih.gov/nifti-1/data/avg152T1_LR_nifti.nii.gz/file_view)

and export to DICOM

result is horrible wrong.

It happens because GDCM uses _Multi Frame Grayscale Byte SC Image IOD Modules_.  
Internally GDCM takes origin and adds positions one by one, calculating **right handed** cross product, so far [GDCM does nothing wrong](https://github.com/malaterre/GDCM/blob/074872fbe04afd5a77e33cfa58caa44f04f73ef5/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx#L1869), but RPI orientation is not compatible with this approach. IMHO, IO could catch incompatible orientations and apply “change orientation filter” (RPI to RPS in particular case) to re-slice such images to compatible orientation.

Code to reproduce (trivial, read nifti, save dcm, nothing more):  
[Test](https://drive.google.com/file/d/127uxlOFxrFdhuRCcQajKyvdnZjKuV95v/view?usp=sharing)

In physical space it looks like:

 ![20190219-214441](https://discourse.itk.org/uploads/default/original/1X/442ba3aec401f37bd16fcea272ebcdb125355bf2.png)

There are already enough messy multi-frame files, were good not produce more.

Sincerely

P.S. It is not related to discussion about sorting frames in multi-frame images several months ago. Here sorting will not help, wrong positions are written in the image.

Sorry i don have this problem in my app and will not work on the issue.

Edit:  
another screenshot (exported image), from Slicer  
 ![Slicer](https://discourse.itk.org/uploads/default/original/1X/4b1f7cb0199b4177ba89475c1fc8f08c8c29d4bc.png)

---

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [February 20, 2019, 11:44am UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/2 "2019-02-20T11:44:34Z")

</div>

Don’t understand me wrong, ITK handles image orientation ideally, it is not a problem with ITK’s orientation design, it is just required step before exporting to multi-frame DICOM.

If someone cares - link to demo program with required re-orientation step before exporting to multi-frame DICOM . All 48 possible orientations test images (in MHA format with R/L marks) are also inside.

[Demo](https://drive.google.com/file/d/11Nq-HeO3nsiLyCUafawPzi3F4ruk8hrI/view?usp=sharing)

```
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkSpatialOrientation.h>
#include <itkOrientImageFilter.h>
#include <iostream>
#include <string>

int main(int argc, char ** argv)
{
  if (argc < 2)
  {
    std::cout << "File name is required" << std::endl;
    return 0;
  }
  typedef itk::Image<unsigned char, 3> ImageTypeUC;
  typedef itk::ImageFileReader<ImageTypeUC> ReaderType;
  typedef itk::ImageFileWriter<ImageTypeUC> WriterType;
  ReaderType::Pointer reader = ReaderType::New();
  try
  {
    reader->SetFileName(argv[1]);
    reader->Update();
  }
  catch (itk::ExceptionObject & ex)
  {
    std::cout << ex.GetDescription() << std::endl;
    return 1;
  }
  ImageTypeUC::Pointer image = reader->GetOutput();
  itk::SpatialOrientationAdapter adapter;
  unsigned int x = 0;
  const unsigned int orientation = static_cast<unsigned int>(
     adapter.FromDirectionCosines(image->GetDirection()));
  switch (orientation)
  {
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIL:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALS:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALI;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARI:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARS;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASR:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAR:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAL;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILA:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILP;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPL:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPR;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRP:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRA;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAS;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIP:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIA;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPI;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSA:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSP;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIL;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLI:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLS;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRS:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRI;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSL:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSR;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIA:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPS;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAL:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAR;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLP:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLA;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPR:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPL;
    break;
  case itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRA:
    x = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRP;
    break;
  default:
   break;
  }
  const std::string f = std::string(argv[1]) + std::string(".dcm");
  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName(f.c_str());
  if (x != 0)
  {
    typedef itk::OrientImageFilter<ImageTypeUC,ImageTypeUC>
      OrientImageFilterType;
    OrientImageFilterType::Pointer filter =
      OrientImageFilterType::New();
    try
    {
      filter->SetInput(image);
      filter->UseImageDirectionOn();
      filter->SetDesiredCoordinateOrientation(
        static_cast
          <itk::SpatialOrientation::ValidCoordinateOrientationFlags>
            (x));
      filter->Update();
      writer->SetInput(filter->GetOutput());
    }
    catch (itk::ExceptionObject & ex)
    {
      std::cout << ex.GetDescription() << std::endl;
      return 1;
    }
  }
  else
  {
    writer->SetInput(image);
  }
  try
  {
    writer->Update();
  }
  catch (itk::ExceptionObject & ex)
  {
    std::cout << ex.GetDescription() << std::endl;
    return 1;
  }
  return 0;
}
```

---

<div class="post-metadata">

### Author: ![matt.mccormick](https://discourse.itk.org/user_avatar/discourse.itk.org/matt.mccormick/32/7_2.png) [@matt.mccormick](https://discourse.itk.org/u/matt.mccormick)
#### Post date: [February 22, 2019, 8:44pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/3 "2019-02-22T20:44:17Z")

</div>

Hello @mihail.isakov,

Thanks for looking into this and the nice example data and demo code 👍 🍬 .

Do you think we should add the proposed pipeline to `itk::GDCMImageIO` here?:

> <https://github.com/InsightSoftwareConsortium/ITK/blob/73188f4e7b1bb558ed0f1871ac119c926950e49d/Modules/IO/GDCM/src/itkGDCMImageIO.cxx#L742>

---

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [February 22, 2019, 9:14pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/4 "2019-02-22T21:14:20Z")

</div>

Honestly, i didn’t know where to do the step - in itk::GDCMImageIO  
`void GDCMImageIO::Write(const void *buffer)`  
works already with _void \* buffer_  
and direction is taken [from metadata](https://github.com/InsightSoftwareConsortium/ITK/blob/73188f4e7b1bb558ed0f1871ac119c926950e49d/Modules/IO/GDCM/src/itkGDCMImageIO.cxx#L901)  
`ExposeMetaData< DoubleMatrixType >( dict, key, directionMatrix );`  
or already set, probably here is “too late”, i have to learn how IO factory works exactly 🤓 🤔

Thank you for reply

---

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [February 23, 2019, 8:25pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/5 "2019-02-23T20:25:22Z")

</div>

Looked at image IO.  
IMHO, filter could be applied here  
[itkImageFileWriter.hxx](https://github.com/InsightSoftwareConsortium/ITK/blob/73188f4e7b1bb558ed0f1871ac119c926950e49d/Modules/IO/ImageBase/include/itkImageFileWriter.hxx#L174)  
_if IO is GDCM image IO_ (DCMTK too? never tried) and _if image dimension is 3_.  
Changing of metadata is not required.

For justification (another one):  
For DICOM file 6 values from direction matrix will be taken (x and y direction), because 3rd is defined in DICOM as right handed cross product. From GDCM IO:

```
  // Do the direction now:
  // if the meta dictionary contains the tag "0020 0037", use it
  const bool hasIOP = ExposeMetaData<std::string>(dict, "0020|0037",tempString);
  if (hasIOP)
  {
    double directions[6];
    sscanf(tempString.c_str(), "%lf\\%lf\\%lf\\%lf\\%lf\\%lf", &(directions[0]), &(directions[1]), &(directions[2]),&(directions[3]),&(directions[4]),&(directions[5]));
    image.SetDirectionCosines(0, directions[0]);
    image.SetDirectionCosines(1, directions[1]);
    image.SetDirectionCosines(2, directions[2]);
    image.SetDirectionCosines(3, directions[3]);
    image.SetDirectionCosines(4, directions[4]);
    image.SetDirectionCosines(5, directions[5]);
  }
  else
  {
    image.SetDirectionCosines(0, m_Direction[0][0]);
    image.SetDirectionCosines(1, m_Direction[0][1]);
    if ( m_Direction.size() == 3 )
      {
      image.SetDirectionCosines(2, m_Direction[0][2]);
      }
    else
      {
      image.SetDirectionCosines(2, 0);
      }
    image.SetDirectionCosines(3, m_Direction[1][0]);
    image.SetDirectionCosines(4, m_Direction[1][1]);
    if ( m_Direction.size() == 3 )
      {
      image.SetDirectionCosines(5, m_Direction[1][2]);
      }
    else
      {
      image.SetDirectionCosines(5, 0);
      }
  }

```

E.g. RAI and RAS:

1 0 0  
0 1 0  
0 0 1

and

1 0 0  
0 1 0  
0 0 -1

For both orientations, the same _Image Orientation Patient_ “1, 0, 0, 0, 1, 0” will be assumed and for multi-frame image GDCM will calculate wrong slice origins for RAS image.

Sincerely

---

<div class="post-metadata">

### Author: ![matt.mccormick](https://discourse.itk.org/user_avatar/discourse.itk.org/matt.mccormick/32/7_2.png) [@matt.mccormick](https://discourse.itk.org/u/matt.mccormick)
#### Post date: [February 24, 2019, 1:54am UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/6 "2019-02-24T01:54:43Z")

</div>

> [@mihail.isakov](#):
>
> _if IO is GDCM image IO_ (DCMTK too? never tried) and _if image dimension is 3_ .

It is tricky since _itkImageFileWriter.hxx_ cannot depend on _itkGDCMImageIO.h_ – _itkGDCMImageIO.h_ may not be available if the _ITKIOGDCM_ module is not enabled, and the _ITKIOImageBase_ module cannot depend on on the _ITKIOGDCM_ module (the reverse dependency exists).

In one approach, if DCMTK behaves the same, we could apply the fix for output files with the _.dcm_.

Or, we could apply the fix in _itkGDCMImageIO.cxx_, even though it is more difficult since the image has been decomposed.

---

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [February 24, 2019, 2:14am UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/7 "2019-02-24T02:14:03Z")

</div>

Oh, yes. Important to know, thank you.  
I’ve just tried to add

std::cout \<\< m\_ImageIO-\>GetNameOfClass() \<\< std::endl;

at this point, line 174, before line with  
`auto * nonConstInput = const_cast< InputImageType * >( input );`

seems _m\_ImageIO_ knows name of class, without including new headers.  
Probably it is enough, i’ll try and report.

Sincerely

---

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [February 24, 2019, 8:12pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/8 "2019-02-24T20:12:02Z")

</div>

@matt.mccormick you are right, of course. ImageFileWriter is bad place, the problem is - some things happen in ::Write(), some in ::GenerateData(), always accessing image like  
`const InputImageType *input = this->GetInput();`  
and buffered region, streaming, pipeline, many possible issues. It is possible, but will require too many changes, not worth… Probably possible ways are either do the job in GDCM IO class, as you have suggested, also tricky, or just give a warning (or error). I shall play again little later… Thank you

---

<div class="post-metadata">

### Author: ![jond01](https://discourse.itk.org/user_avatar/discourse.itk.org/jond01/32/1367_2.png) [@jond01](https://discourse.itk.org/u/jond01)
#### Post date: [April 16, 2020, 8:25pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/9 "2020-04-16T20:25:01Z")

</div>

Hi, is there any update with this?

Apparently, every dicom (single 3d image or dicom series) with a left-handed orientation (24 out of 48, e.g. RAS) is saved in a wrong way. I think that it is a serious bug, but couldn’t find one in the open issues.

I can provide a short python code which demonstrates the issue.  
Maybe a solution will be to add a (-1) factor to the “_Spacing Between Slices_” tag for left-handed orientations?

---

<div class="post-metadata">

### Author: ![mihail.isakov](https://discourse.itk.org/letter_avatar_proxy/v4/letter/m/c4cdca/32.png) [@mihail.isakov](https://discourse.itk.org/u/mihail.isakov)
#### Post date: [April 16, 2020, 10:02pm UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/10 "2020-04-16T22:02:17Z")

</div>

i also think it is critical issue, actually best workaround, IMHO, is to apply change orientation filter for incompatible orientations (e.g. RAS to RAI) before exporting to multi-frame DICOM.

---

<div class="post-metadata">

### Author: ![jond01](https://discourse.itk.org/user_avatar/discourse.itk.org/jond01/32/1367_2.png) [@jond01](https://discourse.itk.org/u/jond01)
#### Post date: [April 17, 2020, 11:40am UTC](https://discourse.itk.org/t/nifti-rpi-orientation-file-exported-to-dicom-wrong-orientation/1612/11 "2020-04-17T11:40:30Z")

</div>

Ok thank you. I just opened an issue in github:

> <https://github.com/InsightSoftwareConsortium/ITK/issues/1771>
>
> Description
> Out of 48 possible orientations for a 3d image, the 24 left-handed orientations are treated incorrectly when saving a multi-frame dicom...

  
You’re welcome to add your ideas 🙂
