Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Spotting Implicit VR

41 views
Skip to first unread message

Stephen Kemp

unread,
Mar 2, 2003, 7:05:38 AM3/2/03
to
I wonder if anyone can offer a little guidance, I am trying to write a very
small and specific program in c++ to read certain elements from a dicom dir
and image files. my problem is with the implicit vr fields (detailed in
table 7.1-3 in PS 3.5 2001 document)
There is, no doubt, an obvious way of spotting the implicit VR fields. The
only way I can think of is either to have a long list of implicit tags, and
to read them differently if my software finds one. or perhaps if the first
two bytes after the tag are not equal to a valid ASCII value for a capital
letter.
Can anyone advise me of the best way to do this.

Many thanks
Stephen Kemp


David Clunie

unread,
Mar 2, 2003, 8:15:27 AM3/2/03
to

Hi Stephen

Firstly, if a DICOMDIR is involved, it is always written with
explicit VR little endian transfer syntax, so the issue doesn't
arise. Same with the meta-information that precedes the dataset
of a Part 10 file.

However, a DICOMDIR may point to files that have an implicit
transfer syntax once you have parsed beyond the meta-information,
so you may want to deal with it.

Note however, that there is no random intermingling of implicit
and explicit value representations. The transfer syntax dictates
that either one or the other is used throughout, with the one
exception of the meta-information.

Having said that, if you are reading something encoded in
implicit VR, then you can a) ignore the value by simply
skipping it (using the VL field), or b) interpret the
value if you need to use it for something. In the latter
case you need to know in advance what the VR "should be",
i.e. you need a dictionary or a "long list" as you refer
to it. However, that list only needs to be as long as the
attributes you need to interpret ... all the others can
be skipped passively.

Note that it is NEVER necessary to test on the fly on a per-
attribute basis whether or not the value representation is
implicit or explicit. This is always decided before you start.
Either you know the entire dataset is explicit or implicit
because you read the meta-information, found the transfer
syntax uid specified there and decided to switch to that
transfer syntax after the meta-information (the length of
which is always specified and tells you when to switch),
or there was no meta-information and you used some heuristic
(or command line switch) to decide what the transfer syntax
is.

Heuristics like you describe (checking for upper case letters
in the appropriate place) is one aspect of "guessing the
transfer syntax", but I re-iterate that you only need to do
this once per dataset, not for each attribute.

FYI. Here is the way I "guess", first in C++ and then in Java:

void
DicomInputStream::initializeTransferSyntax(const char *uid,bool meta)
{
TransferSyntaxToReadMetaHeader = 0;
TransferSyntaxToReadDataSet = 0;

// First make use of command line parameters that override guesswork ...

if (uid) {
TransferSyntax *ts = new TransferSyntax(uid);
if (meta) {
TransferSyntaxToReadMetaHeader = ts; // specified UID is transfer syntax to read metaheader
}
else {
TransferSyntaxToReadDataSet = ts; // specified UID is transfer syntax to read dataset (there is no metaheader)
}
}
// else transfer syntax has to be determined by either guesswork or metaheader ...

char b[8];

if (meta) {
// test for metaheader prefix after 128 byte preamble
seekg(128,ios::beg);
if (good() && read(b,4) && strncmp(b,"DICM",4) == 0) {
if (!TransferSyntaxToReadMetaHeader) TransferSyntaxToReadMetaHeader = // guess only if not specified on command line
read(b,6) && isupper(b[4]) && isupper(b[5])
? new TransferSyntax(ExplicitVRLittleEndianTransferSyntaxUID) // standard
: new TransferSyntax(ImplicitVRLittleEndianTransferSyntaxUID); // old draft (e.g. used internally on GE IOS platform)

// leaves positioned at start of metaheader
seekg(128+4,ios::beg);
}
else {
clear(); seekg(0,ios::beg); // reset stream since metaheader was sought but not found
TransferSyntaxToReadDataSet=TransferSyntaxToReadMetaHeader;
TransferSyntaxToReadMetaHeader=0;
}
}

if (!TransferSyntaxToReadDataSet && !TransferSyntaxToReadMetaHeader){ // was not specified on the command line and there is no metaheader
bool bigendian = false;
bool explicitvr = false;
clear();
seekg(0,ios::beg);
if (good() && read(b,8)) {
// examine probable group number ... assume <= 0x00ff
if (b[0] < b[1]) bigendian=true;
else if (b[0] == 0 && b[1] == 0) {
// blech ... group number is zero
// no point in looking at element number
// as it will probably be zero too (group length)
// try the 32 bit value length of implicit vr
if (b[4] < b[7]) bigendian=true;
}
// else littleendian
if (isupper(b[4]) && isupper(b[5])) explicitvr=true;
}
// else unrecognized ... assume default

if (bigendian)
if (explicitvr)
TransferSyntaxToReadDataSet = new TransferSyntax(ExplicitVRBigEndianTransferSyntaxUID);
else
TransferSyntaxToReadDataSet = new TransferSyntax(ImplicitVR,BigEndian);
else
if (explicitvr)
TransferSyntaxToReadDataSet = new TransferSyntax(ExplicitVRLittleEndianTransferSyntaxUID);
else
TransferSyntaxToReadDataSet = new TransferSyntax(ImplicitVRLittleEndianTransferSyntaxUID);

// leaves positioned at start of dataset
clear();
seekg(0,ios::beg);
}

TransferSyntaxInUse = TransferSyntaxToReadMetaHeader ? TransferSyntaxToReadMetaHeader : TransferSyntaxToReadDataSet;
Assert(TransferSyntaxInUse);
setEndian(TransferSyntaxInUse->getEndian());
}


private void initializeTransferSyntax(String uid,boolean tryMeta) throws IOException {
TransferSyntaxToReadMetaHeader = null;
TransferSyntaxToReadDataSet = null;

byte b[] = new byte[8];

// First make use of argument that overrides guesswork at transfer syntax ...

if (uid != null) {
TransferSyntax ts = new TransferSyntax(uid);
if (tryMeta) {
TransferSyntaxToReadMetaHeader = ts; // specified UID is transfer syntax to read metaheader
}
else {
TransferSyntaxToReadDataSet = ts; // specified UID is transfer syntax to read dataset (there is no metaheader)
}
}
// else transfer syntax has to be determined by either guesswork or metaheader ...

if (tryMeta) {
// test for metaheader prefix after 128 byte preamble
if (markSupported()) mark(140);
if (skip(128) == 128 && read(b,0,4) == 4 && new String(b,0,4).equals("DICM")) {
if (TransferSyntaxToReadMetaHeader == null) { // guess only if not specified as an argument
if (markSupported()) {
mark(8);
if (read(b,0,6) == 6) { // the first 6 bytes of the first attribute tag in the metaheader
TransferSyntaxToReadMetaHeader =
Character.isUpperCase((char)(b[4])) && Character.isUpperCase((char)(b[5]))
? new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian) // standard
: new TransferSyntax(TransferSyntax.ImplicitVRLittleEndian); // old draft (e.g. used internally on GE IOS platform)
}
else {
TransferSyntaxToReadMetaHeader = new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian);
}
reset();
}
else {
// can't guess since can't rewind ... insist on standard transfer syntax
TransferSyntaxToReadMetaHeader = new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian);
}
}
byteOffsetOfStartOfData=132;
}
else {
// no preamble, so rewind and try using the specified transfer syntax (if any) for the dataset instead
if (markSupported()) {
reset();
TransferSyntaxToReadDataSet = TransferSyntaxToReadMetaHeader; // may be null anyway if no uid argument specified
byteOffsetOfStartOfData=0;
}
else {
throw new IOException("Not a DICOM PS 3.10 file - no DICM after preamble in metaheader, and can't rewind input");
}
}
}

// at this point either we have succeeded or failed at finding a metaheader, or we didn't look
// so we either have a detected or specified transfer syntax for the metaheader, or the dataset, or nothing at all

if (TransferSyntaxToReadDataSet == null && TransferSyntaxToReadMetaHeader == null) { // was not specified as an argument and there is no metaheader
boolean bigendian = false;
boolean explicitvr = false;
if (markSupported()) {
mark(10);
if (read(b,0,8) == 8) {
// examine probable group number ... assume <= 0x00ff
if (b[0] < b[1]) bigendian=true;
else if (b[0] == 0 && b[1] == 0) {
// blech ... group number is zero
// no point in looking at element number
// as it will probably be zero too (group length)
// try the 32 bit value length of implicit vr
if (b[4] < b[7]) bigendian=true;
}
// else little endian
if (Character.isUpperCase((char)(b[4])) && Character.isUpperCase((char)(b[5]))) explicitvr=true;
}
// go back to start of dataset
reset();
}
// else can't guess or unrecognized ... assume default ImplicitVRLittleEndian (most common without metaheader due to Mallinckrodt CTN default)

if (bigendian)
if (explicitvr)
TransferSyntaxToReadDataSet = new TransferSyntax(TransferSyntax.ExplicitVRBigEndian);
else
throw new IOException("Not a DICOM file (masquerades as explicit VR big endian)");
else
if (explicitvr)
TransferSyntaxToReadDataSet = new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian);
else
TransferSyntaxToReadDataSet = new TransferSyntax(TransferSyntax.ImplicitVRLittleEndian);
}

TransferSyntaxInUse = TransferSyntaxToReadMetaHeader != null ? TransferSyntaxToReadMetaHeader : TransferSyntaxToReadDataSet;
if (TransferSyntaxInUse == null) throw new IOException("Not a DICOM file (or can't detect Transfer Syntax)");
setEndian(TransferSyntaxInUse.isBigEndian());

// leaves us positioned at start of group and element tags (for either metaheader or dataset)
}


Stephen Kemp

unread,
Mar 2, 2003, 9:11:07 AM3/2/03
to
thank you very much, that makes it a whole lot easier.

"David Clunie" <dcl...@dclunie.com> wrote in message
news:3E6203E...@dclunie.com...

0 new messages