ITK/Release 4/DICOM/GDCM Tcon Notes

From KitwarePublic
Jump to navigationJump to search

Luis Ibanez, Andrew Wasem and Mark Roden were in attendance of this meeting. The meeting was on the current and future progress of GDCM/ITK.


Example of how to run an RT-STRUCT in VTK

  • src/gdcm/Utilities/VTK/Examples/Cxx/GenerateRTSTRUCT.cxx
  • src/gdcm/Utilities/VTK/vtkGDCMPolyDataWriter.h
 void vtkGDCMPolyDataWriter::InitializeRTStructSet(vtkStdString inDirectory,
                                                vtkStdString inStructLabel,
                                                vtkStdString inStructName,
                                                vtkStringArray* inROINames,
                                                vtkStringArray* inROIAlgorithmName,
                                                vtkStringArray* inROIType)

Streaming of DIcom images (from PACS)

Failing streaming test

is using a malformed DICOM image. must change the input image. replace it with one of the brainsets from the Osirix collection of public data. Cerebrix. (apparently this is failing only in an icc build)

Another streaming issue

StreamImageReader

  • ITK/Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat
 gdcmStreamImageReader.h
 gdcmStreamImageReader.cxx
 Limited to uint16:
 mXMin = mYMin = mZMin = std::numeric_limits<uint16_t>::max();
 mXMax = mYMax = mZMax = std::numeric_limits<uint16_t>::min();

then

 uint32_t StreamImageReader::DefineProperBufferLength() const is limited uint32_t, in number of bytes.

Then read the actual buffer:

  bool StreamImageReader::Read(void* inReadBuffer, const std::size_t& inBufferLength)

(before we call ReadImageInformation)

The class: OffsetTable

  • ITK/Modules/ThirdParty/GDCM/src/gdcm/Source/DataStructureAndEncodingDefinition
 gdcmBasicOffsetTable.h

defines sections (regions) of the image that have been compressed. The reading code is implemented for RAW files that do not have an offset table. This could be used to extend GDCM to not have to read a full plane, but instead read a subregion of that plane, and therefore be more compatible with ITK streaming.

 bool StreamImageReader::ReadImageSubregionJpegLS(char* inReadBuffer, const std::size_t& inBufferLength)

This function is used (or was intended for...) to read metadata without reading the pixel data yet.

manages byte swapping

 bool needbyteswap = (ts == TransferSyntax::ImplicitVRBigEndianPrivateGE);

line 244:

  theCodec.Decode(de, inReadBuffer, inBufferLength, mXMin, mXMax, mYMin, mYMax, mZMin, mZMax);

do the decoding by delegates to the JpegLS library.

DICOM files withJPEG

We only have been able to find DICOM files withJPEG, that have a single tile.

(it will be nice to get some with multiple tile).

DICOM Slices

It is also possible to find DICOM Slices that are composed of a group of individual JPEG images. (e.g. like doing tiling by hand).



Tag thePixelDataTag(0x7fe0, 0x0010);//must be LESS than the pixel

information tag, 0x7fe0,0x0010

This is the location where the pixel data buffer.

but in some cases, some images have more tags after this one, and that can confuse the stream reading.


GDCM StreamingWriting

The GDCM StreamingWriting is not working at this point.

(something deep inside of gdcm).


--

gdcmStreamImageWriter.cxx  : 346

bool StreamImageWriter::CanWriteFile() const

bool hasTag818 = mFile.GetDataSet().FindDataElement(Tag(0x08,0x18));
if (!hasTag23 && !hasTag818){

When writing a DICOM image, the Tag818 must be different from the one that it was read from. (unless you are an OEM... e.g. the manufacturer of a CT scanner.)


Then, when it gets to

bool StreamImageWriter::WriteImageInformation(){

it fails..

Line 110 describe how to read the tags.

Reading is done in 116.


The problem relates to the offset table...

and how it addresses space inside of the image.

This is mostly a problem when data is compressed. (streaming raw data will be easier, but it is not working now either...the output file is not readable.).

(its MD5 sum for the pixel buffer doesn't match after writing...)


StreamReader in GDCM

The StreamReader in GDCM is intended to be used by the ITK streaming reader.

(apparently the StreamReader is not used outside of ITK).

There is a passing test for stream reading.

PACS - DICOM Protocol - Networking

Networking

ITK/Modules/ThirdParty/GDCM/src/gdcm/Source/MessageExchangeDefinition/

gdcmCompositeNetworkFunctions.cxx

static bool CEcho( const char *remote, uint16_t portno, const char
  • aetitle = NULL,
  const char *call = NULL );

A server will have "titles" (up to 8).

A title is a "computer name", not an IP address. Every title connects to a specific port in the server

CEcho is the basic test function


Then you construct your query:

/// This function will take a list of strings and tags and fill in a

query that

/// can be used for either CFind or CMove (depending on the input boolean
/// \param inMove).
/// Note that the caller is responsible for deleting the constructed query.
/// This function is used to build both a move and a find query
/// (true for inMove if it's move, false if it's find)
static BaseRootQuery* ConstructQuery(ERootType inRootType,

EQueryLevel inQueryLevel,

  const DataSet& queryds, bool inMove = false );
/// \deprecated
static BaseRootQuery* ConstructQuery(ERootType inRootType,

EQueryLevel inQueryLevel,

  const KeyValuePairArrayType& keys, bool inMove = false );


(it requires a fully form dataset).


BaseRootQuery = abstract class

2 types of queries : find & move

"find" can be using a very generic expression. (can have wildcards).

ITK/Modules/ThirdParty/GDCM/src/gdcm/Source/MessageExchangeDefinition

gdcmFindPatientRootQuery.h


First figure out what tags are available:

std::vector<Tag> GetTagListByLevel(const EQueryLevel& inQueryLevel);


this ties up to the "image interpretation layer" to the point where a juser can get this list of Tags and then select values from them to put them in a query and using to get actual data.


"move" queries are more strict than "find" ones (can't have wildcards).

This search will fail if it is not fully specified.


gdcmCompositeNetworkFunctions.cxx

bool CompositeNetworkFunctions::CStore( const char *remote, uint16_t portno,

const Directory::FilenamesType& filenames,
const char *aetitle, const char *call)

267

CStore command will move images to a server.


gdcmServiceClassUser.h

bool SendMove(const BaseRootQuery* query, const char *outputdir);
bool SendMove(const BaseRootQuery* query, std::vector<DataSet> &retDatasets);

allows to put data directly into memory.

(pending a refactoring in which the concept of

a DICOM file was planned to be abstracted...)

ITK application, creating an itk::Image using data from a remote server.

(list of use cases in another email from Mark...)


In order to define ITK functionalities, we should specify a list of clear use cases....


(Review emails from DICOM Taskforce February 23-24) (post the use cases in a Wiki page).