Introduction

Having just used Qt to CRUD DICOM files, I have some experience with handing 
metadata. DICOM is a metadata-heavy image format used in medical context. It 
contains patient, image, region, and annotation in addition to the pixel data. 
DICOM also has structured metadata, meaning it contains sequences, and sequence 
items which are used to structure the individual metadata tags. The approach 
described herein was used, with great success.

A few notes on DICOM
DICOM is primarily a 16-bit format. It uses numerical tags (called groups and 
elements), and stores data in various fundamental types. Many times, itis aided 
by an optional dictionary which specifies a ASCII name and data type. There is 
an industry-specified dictionary, though vendors do add their own 'private 
tags'. Also, there are various serializations - some are 'explicit', as 
specifying the data type, others are 'implicit', requiring the use of the 
dictionary. In addition to this, there are big endian and little endian formats.

A sample tag looks like:
|GGGG|EEEE|LENG|DATA|
Where each is group, element, data length (always even) and data (padded to 
even) resspecively.

A sample seqeunce looks like:
|GGGG|EEEE|LENG|     -- Where GGGG|EEEE is a specific sequence tag 
   BoIT|LEN| x N     -- A Begining of item 
      GGGG|EEEE|LENG|DATA| x M -- any number of tags and 
data,                                   (including sequences)
   [EoIT]              -- Optional End of Item (req if LEN==-1)

This metadata can span 10s of KB. 

The DICOM file usually finishes with a special tag for pixel data, which is in 
some format as specified by the metadata. This pixel data averages 10s of 
megabytes, with actual images approaching 600MB, and over gigabyte in the lab. 
Therefore, it is not an acceptable assumption to be able to load the entire 
image into memory.

With Qt, I was able to create new DICOM files, read existing files, modify data 
(update/delete) and serialize it back out to DICOM, a SQL table or XML.

The Key Observations

Observation the First: Structured data 

Structured data can be serialized using 3 operations:
1. Create Node - createNode(QString tag, QVariantMap attrs, QVariant value)
2. Push - push()
3. Pop - pop()

Any format that is not random access can be expressed this way. XML for example 
would look like:
<items>                   createNode("items", {}, QVariant::Qvariant())
                          push()
<item id="1">data1</item> createNode("item", {"id"="1"}, "data1")
<item id="2">data2</item> createNode("item", {"id"="2"}, "data2")             
</items>                  pop()

Now the only thing needed is a data model that understands these commands. We 
need it to hold a variant value, be able to have children, and be randomly 
addressable. Enter QRecursiveMap

QRecursiveMap (proposed name) is nothing mode than a QMAP<T, Variant> that has 
been extended to have its own value property, and return a sub-recursive map 
automatically. createNode() then sets the value. One more small peice is 
missing 
to handle the pushes/pops. I used another class to take care of maintaining the 
current RecursiveMap (the target of createNode()) and provide a stack for 
pop()s.

Once these classes are complete, the above XML can be read addressed as
Qvariant data1 = map["items"]["item"]["0"].value(); //T=QString

[Note: Which is a very easy thing to make look like XPATH: 
"items/item/0.node()" 
(use with .split('/'))]

Observation the Second

Tags are really "tag-found" events, and need to be emitted. This is true of XML 
tags, DICOM tags or any data. Therefore, we need to only write a 
[Format]TagReader class which 'emits' our 3 commands. For DICOM, I used a 
QDataStream. But I also wrote them for XML, RecursiveMap and SQL, pretty 
easily. 
Once we speak this language of the three magic commands, we're set.

Next, we need to write a Writer for these events. What we end up with are 
classes names in the format:

[Media][Format][Operation]
FileDICOMReader
FileDICOMWriter
XMLReader
XMLWriter
MapReader
MapWriter
ScreenWriter - for debug output

To make a program with these classes, we only need select our input class, and 
our output class, then QObject::connect() them
FileDICOMReader in("1.dcm");
RecursiveMap    map;
MapWriter       mw(map);
ScreenWriter    dbg;

in.connectTo(map);
in.connectTo(dbg);

in.read(); // this is where all the signal slots happen.
// map now has the tag tree
map["items"]["item"][0].value="new data"; // change a value


MapReader mr(map);
XMLWriter outXml("1.xml");

mr.read(); // the map is iterated over, producing tags and data that are fed to 
the XML writer

But wait there's more!

I over-simplified when I said '3' commands. Really, because we work with files, 
we need to add begin() and done() so that the writer can initialize, open a 
file, close a file, etc. 


It is also possible to also set your writer not to accept certain tags, say 
pixel data. (For metadata reading this is essential) Also, note for any 
potentially large data, the API needs to support a 'chunked' interface, this 
way 
pixel data can be read and processed in blocks. This keeps memory usage down 
and 
reduces latency. There is GraphicsMagick (ImageMagick competitor) that supports 
a streaming conversion interface. It would be possible to wrap that in a web 
service, chunk-read your file and ship your pixel data to the web service, and 
read the converted pixel data. If you don't stream it, then you have to load, 
send, read. But if you stream it, it will only be milliseconds before you start 
getting data back. Which might not mean a whole lot in wall-clock, but the 
local 
system can service and arbitrary number streams of arbitrary image size, 
whereas, if it isn't chunked, the memory limits of the local system will become 
a factor.

QDom

With QDom "going away" this technique provides an even easier interface for 
documents which need to be modified. Previously QDom was the only way.

The future is limitless!
Exercise to the reader: implement  TAR or ZIP serialization using this 
technique.


      
_______________________________________________
Qt-mobility-feedback mailing list
[email protected]
http://lists.trolltech.com/mailman/listinfo/qt-mobility-feedback

Reply via email to