Wait until vtkDicomImageReader output data is available

Hi,

I’m using vtkDicomImageReader to read a DICOM volume, and then accessing properties of the DICOM volume, like this:

reader->SetDirectoryName("/path");
reader->Update();
reader->GetOutput()->GetSpacing(spacing);

It seems that the call to Update() is not sufficient to guarantee that the scalar range values are up-to-date by the time I call GetSpacing(), because depending on how I run my program, I get either the correct result or not. When I run my program (debug build) normally, the values are 0.0. When I run it via the debugger, the values are correctly corresponding with the volume being read. So there must be a timing-related issue here.

Any suggestion on how to make sure that I don’t access data before the volume has been fully read ?

Thanks

In VTK, Update() is synchronous. When it returns, it is guaranteed that either

  1. the update is complete, or
  2. an error has occurred

After calling Update(), be sure to call GetErrorCode() to check whether the Update was successful. The error codes are listed in vtkErrorCode.

Thank you.

Strangely, the error code is NoError in both cases (running with and without debugger). I observe the issue on Ubuntu, but not on MacOS. Using VTK 8.2.0.

I should probably make a minimal example that generates the issue.

By calling
reader->SetDebug(true);
I observed that something goes wrong while determining the slice positions.

Successful run:

vtkDICOMImageReader (0x5614b5807bb0): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00599 at slice : -235.4
vtkDICOMImageReader (0x5614b5807bb0): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00598 at slice : -235.6
vtkDICOMImageReader (0x5614b5807bb0): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00597 at slice : -235.8

vtkDICOMImageReader (0x5614b5807bb0): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00000 at slice : -355.2

Failed run:

vtkDICOMImageReader (0x55c537e09530): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00153 at slice : 0
vtkDICOMImageReader (0x55c537e09530): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00146 at slice : 0
vtkDICOMImageReader (0x55c537e09530): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00454 at slice : 0

vtkDICOMImageReader (0x55c537e09530): Adding file /home/gert/data/20200526_01_ct/DCM000/IMG00503 at slice : 0

So the success and failure are for the same input data?

It’s interesting that in the failed run, the reader was unable to sort the slices and was instead reading them in random order.

Yes, same input data, same code. The only difference is whether I run the executable with or without the debugger (from Qt Creator IDE). Really puzzling.

It turns out that sscanf gives unexpected results in this code in DICOMAppHelper.cxx

sscanf( reinterpret_cast<char*>(val), “%f\%f\%f”,
&(*it).second.ImagePositionPatient[0],
&(*it).second.ImagePositionPatient[1],
&(*it).second.ImagePositionPatient[2] );

When running successfully, this string
-38.000000\-38.800001\-369.999996
is parsed correctly to produce values -38, -38.8, and -370

When it fails, the same string is parsed to produce the values
-38, 0, and 0

This set me on the track to the root cause:

Adding
setlocale(LC_NUMERIC, “C”);
just after
QApplication app(argc, argv);
in my main() function makes the problem go away.

Aha, good find. Several of the other VTK readers avoid this problem by internally setting the locale:

this->CurrentLocale = std::locale::global(std::locale::classic());
// read the file
// …
std::locale::global(this->CurrentLocale);

Similar code will be needed in vtkDICOMImageReader.

This is just one of the many issues with the DICOM reader bundled with VTK by default. I would strongly recommend to use @dgobbi’s excellent vtkDICOM library instead.

To reduce confusion among users, it would be great if the legacy DICOM parser could be removed from VTK and vtkDICOM could be included as an optional VTK remote module. @ben.boeckel I know that you are not a fan of VTK remote modules, but this is another good example where it would be very useful to be able to build VTK with proper DICOM support with the flip of a CMake flag. VTK Python wheels should be also built with vtkDICOM added (instead of the old flawed DICOM parser).

On a general note: if it is not obvious to everyone yet, sscanf should not be used in a library for converting text to double (as a library cannot dictate what locale the application must use). Instead C++ streams and imbue(std::locale::classic()) can be used - as it is done already in vtkDICOM and at a number of places in VTK. Unfortunately, there are still a lot of legacy sscanf usages in VTK IO classes.

Nothing that isn’t in the VTK repository is going into the wheels. It’s not code that is under VTK’s software process. Get it into VTK.

My intent is to get vtkDICOM into the VTK repository, and I’m around 75% of the way there. Ben has been supporting the effort via code reviews. However, I cannot say when I’ll be finished.

1 Like

I’m just worried because I see VTK losing ground against hundreds of new, immature, low-quality Python visualization packages. Having a small VTK core and a network of independently maintained packages would go a long way in preserving VTK’s relevance and keeping its development sustainable. For example, you can merge vtkDICOM, but you don’t want to merge and take the ownership of all the other potentially interesting modules.

The remote module concept has proven to work well for ITK (they have now more than 40 remote modules), so it would be an obvious solution to port it to VTK as well, but of course there could be other approaches, too. I understand if nothing is done because there other priorities, no funding, etc. but there should be at least a desire to build a healthy ecosystem of contributed libraries around VTK.

Sure, I want this too. Rendering, IO, Filters, etc. should all be separate projects (if not repositories) as far as I’m concerned, but these are political issues that are hard to progress given the momentum today. ParaView embedding VTK is a blocker to any real progress of splitting VTK anyways, so that’s my main concern right now on this front.

The main problem I see is that if I have remote modules A, B, and C and you have A, B, and X. We need a brand new, from scratch VTK build to combine them at all. I can’t “just add X” (nor can you “just add B”) if remote modules are how they work. If, instead, they use VTK as an external dependency (just like any other dependency they may have), we could just do VTK+A+B+C+X as separate builds and be done with it. This kind of thing basically makes pip install vtk a crapshoot of what is actually included and why I don’t want to include any of them in the official wheels. There is support for building your own as you see fit with whatever options you want though.

It is unfortunate that Python has a poor (no?) solution for non-Python dependency links between Python packages, but I don’t know how one would expect us to solve that for them. Anaconda is probably better there since it acknowledges things like C++ libraries and linking between packages and offers real install prefixes (rather than a directory extracted somewhere nothing else looks at by default).