Cloned library of VTK-5.0.0 with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

779 lines
23 KiB

/*=========================================================================
Program: DICOMParser
Module: $RCSfile: DICOMParser.cxx,v $
Language: C++
Date: $Date: 2006/03/24 15:59:50 $
Version: $Revision: 1.14.4.1 $
Copyright (c) 2003 Matt Turek
All rights reserved.
See Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#ifdef _MSC_VER
#pragma warning ( disable : 4514 )
#pragma warning ( disable : 4786 )
#pragma warning ( disable : 4503 )
#pragma warning ( disable : 4710 )
#pragma warning ( push, 3 )
#endif
#include <stdlib.h>
#if !defined(__MWERKS__)
#include <math.h>
#endif
#include <time.h>
#include <assert.h>
#if !defined(__MWERKS__)
#include <sys/types.h>
#endif
#include <string.h>
#include "DICOMConfig.h"
#include "DICOMParser.h"
#include "DICOMCallback.h"
// Define DEBUG_DICOM to get debug messages sent to dicom_stream::cerr
// #define DEBUG_DICOM
#define DICOMPARSER_IGNORE_MAGIC_NUMBER
#ifdef DEBUG_DICOM
#define DICOM_DBG_MSG(x) {dicom_stream::cout x}
#else
#define DICOM_DBG_MSG(x)
#endif
static const char* DICOM_MAGIC = "DICM";
static const int OPTIONAL_SKIP = 128;
class DICOMParserImplementation
{
public:
DICOMParserImplementation() : Groups(), Elements(), Datatypes(), Map(), TypeMap()
{
};
dicom_stl::vector<doublebyte> Groups;
dicom_stl::vector<doublebyte> Elements;
dicom_stl::vector<DICOMParser::VRTypes> Datatypes;
//
// Stores a map from pair<group, element> keys to
// values of pair<vector<DICOMCallback*>, datatype>
//
DICOMParserMap Map;
//
// Stores a map from pair<group, element> keys to
// values of datatype. We use this to store the
// datatypes for implicit keys that we are
// interested in.
//
DICOMImplicitTypeMap TypeMap;
};
DICOMParser::DICOMParser() : ParserOutputFile()
{
this->Implementation = new DICOMParserImplementation();
this->DataFile = NULL;
this->ToggleByteSwapImageData = false;
this->TransferSyntaxCB = new DICOMMemberCallback<DICOMParser>;
this->InitTypeMap();
this->FileName = "";
}
const dicom_stl::string&
DICOMParser::GetFileName()
{
return this->FileName;
}
bool DICOMParser::OpenFile(const dicom_stl::string& filename)
{
if (this->DataFile)
{
// Deleting the DataFile closes the file
delete this->DataFile;
}
this->DataFile = new DICOMFile();
bool val = this->DataFile->Open(filename);
if (val)
{
this->FileName = filename;
}
#ifdef DEBUG_DICOM
if (this->ParserOutputFile.rdbuf()->is_open())
{
this->ParserOutputFile.flush();
this->ParserOutputFile.close();
}
dicom_stl::string fn(filename);
dicom_stl::string append(".parser.txt");
dicom_stl::string parseroutput(fn + append);
// dicom_stl::string parseroutput(dicom_stl::string(dicom_stl::string(filename) + dicom_stl::string(".parser.txt")));
this->ParserOutputFile.open(parseroutput.c_str()); //, dicom_stream::ios::app);
#endif
return val;
}
DICOMParser::~DICOMParser() {
//
// Delete the callbacks.
//
this->ClearAllDICOMTagCallbacks();
if (this->DataFile)
{
delete this->DataFile;
}
delete this->TransferSyntaxCB;
delete this->Implementation;
#ifdef DEBUG_DICOM
this->ParserOutputFile.flush();
this->ParserOutputFile.close();
#endif
}
bool DICOMParser::ReadHeader() {
bool dicom = this->IsDICOMFile(this->DataFile);
if (!dicom)
{
return false;
}
this->TransferSyntaxCB->SetCallbackFunction(this, &DICOMParser::TransferSyntaxCallback);
this->AddDICOMTagCallback(0x0002, 0x0010, DICOMParser::VR_UI, this->TransferSyntaxCB);
this->ToggleByteSwapImageData = false;
doublebyte group = 0;
doublebyte element = 0;
DICOMParser::VRTypes datatype = DICOMParser::VR_UNKNOWN;
this->Implementation->Groups.clear();
this->Implementation->Elements.clear();
this->Implementation->Datatypes.clear();
long fileSize = DataFile->GetSize();
do
{
this->ReadNextRecord(group, element, datatype);
this->Implementation->Groups.push_back(group);
this->Implementation->Elements.push_back(element);
this->Implementation->Datatypes.push_back(datatype);
} while ((DataFile->Tell() >= 0) && (DataFile->Tell() < fileSize));
return true;
}
//
// read magic number from file
// return true if this is your image type, false if it is not
//
bool DICOMParser::IsDICOMFile(DICOMFile* file) {
char magic_number[4];
file->SkipToStart();
file->Read((void*)magic_number,4);
if (CheckMagic(magic_number))
{
return(true);
}
// try with optional skip
else
{
file->Skip(OPTIONAL_SKIP-4);
file->Read((void*)magic_number,4);
if (CheckMagic(magic_number))
{
return true;
}
else
{
#ifndef DICOMPARSER_IGNORE_MAGIC_NUMBER
return false;
#else
//
// Try it anyways...
//
file->SkipToStart();
doublebyte group = file->ReadDoubleByte();
bool dicom;
if (group == 0x0002 || group == 0x0008)
{
dicom_stream::cerr << "No DICOM magic number found, but file appears to be DICOM." << dicom_stream::endl;
dicom_stream::cerr << "Proceeding without caution." << dicom_stream::endl;
dicom = true;
}
else
{
dicom = false;
}
file->SkipToStart();
return dicom;
#endif // DICOMPARSER_IGNORE_MAGIC_NUMBER
}
}
}
bool DICOMParser::IsValidRepresentation(doublebyte rep, quadbyte& len, VRTypes &mytype)
{
switch (rep)
{
case DICOMParser::VR_AW:
case DICOMParser::VR_AE:
case DICOMParser::VR_AS:
case DICOMParser::VR_CS:
case DICOMParser::VR_UI:
case DICOMParser::VR_DA:
case DICOMParser::VR_DS:
case DICOMParser::VR_DT:
case DICOMParser::VR_IS:
case DICOMParser::VR_LO:
case DICOMParser::VR_LT:
case DICOMParser::VR_PN:
case DICOMParser::VR_ST:
case DICOMParser::VR_TM:
case DICOMParser::VR_UT: // new
case DICOMParser::VR_SH:
case DICOMParser::VR_FL:
case DICOMParser::VR_SL:
case DICOMParser::VR_AT:
case DICOMParser::VR_UL:
case DICOMParser::VR_US:
case DICOMParser::VR_SS:
case DICOMParser::VR_FD:
len = DataFile->ReadDoubleByte();
mytype = VRTypes(rep);
return true;
case DICOMParser::VR_OB: // OB - LE
case DICOMParser::VR_OW:
case DICOMParser::VR_UN:
case DICOMParser::VR_SQ:
DataFile->ReadDoubleByte();
len = DataFile->ReadQuadByte();
mytype = VRTypes(rep);
return true;
default:
//
//
// Need to comment out in new paradigm.
//
DataFile->Skip(-2);
len = DataFile->ReadQuadByte();
mytype = DICOMParser::VR_UNKNOWN;
return false;
}
}
void DICOMParser::ReadNextRecord(doublebyte& group, doublebyte& element, DICOMParser::VRTypes& mytype)
{
//
// WE SHOULD IMPLEMENT THIS ALGORITHM.
//
// FIND A WAY TO STOP IF THERE ARE NO MORE CALLBACKS.
//
// DO WE NEED TO ENSURE THAT WHEN A CALLBACK IS ADDED THAT
// THE IMPLICIT TYPE MAP IS UPDATED? ONLY IF THERE ISN'T
// A VALUE IN THE IMPLICIT TYPE MAP.
//
// New algorithm:
//
// 1. Read group & element
// 2. ParseExplicitRecord
// a. Check to see if the next doublebyte is a valid datatype
// b. If the type is valid, lookup type to find the size of
// the length field.
// 3. If ParseExplicitRecord fails, ParseImplicitRecord
// a. Lookup implicit datatype
// 4. Check to see if there is a registered callback for the group,element.
// 5. If there are callbacks, read the data and call them, otherwise
// skip ahead to the next record.
//
//
group = DataFile->ReadDoubleByte();
element = DataFile->ReadDoubleByte();
doublebyte representation = DataFile->ReadDoubleByteAsLittleEndian();
quadbyte length = 0;
mytype = DICOMParser::VR_UNKNOWN;
this->IsValidRepresentation(representation, length, mytype);
DICOMParserMap::iterator iter =
Implementation->Map.find(DICOMMapKey(group,element));
VRTypes callbackType;
if (iter != Implementation->Map.end())
{
//
// Only read the data if there's a registered callback.
//
unsigned char* tempdata = (unsigned char*) DataFile->ReadAsciiCharArray(length);
DICOMMapKey ge = (*iter).first;
callbackType = VRTypes(((*iter).second.first));
if (callbackType != mytype &&
mytype != VR_UNKNOWN)
{
//
// mytype is not VR_UNKNOWN if the file is in Explicit format.
//
callbackType = mytype;
}
#ifdef DEBUG_DICOM
this->DumpTag(this->ParserOutputFile, group, element, callbackType, tempdata, length);
#endif
dicom_stl::pair<const DICOMMapKey,DICOMMapValue> p = *iter;
DICOMMapValue mv = p.second;
bool doSwap = (this->ToggleByteSwapImageData ^ this->DataFile->GetPlatformIsBigEndian()) && callbackType == VR_OW;
if (group == 0x7FE0 &&
element == 0x0010 )
{
if (doSwap)
{
#ifdef DEBUG_DICOM
dicom_stream::cout << "==============================" << dicom_stream::endl;
dicom_stream::cout << "TOGGLE BS FOR IMAGE" << dicom_stream::endl;
dicom_stream::cout << " ToggleByteSwapImageData : " << this->ToggleByteSwapImageData << dicom_stream::endl;
dicom_stream::cout << " DataFile Byte Swap : " << this->DataFile->GetPlatformIsBigEndian() << dicom_stream::endl;
dicom_stream::cout << "==============================" << dicom_stream::endl;
#endif
DICOMFile::swap2((ushort*) tempdata, (ushort*) tempdata, length/sizeof(ushort));
}
else
{
#ifdef DEBUG_DICOM
dicom_stream::cout << "==============================" << dicom_stream::endl;
dicom_stream::cout << " AT IMAGE DATA " << dicom_stream::endl;
dicom_stream::cout << " ToggleByteSwapImageData : " << this->ToggleByteSwapImageData << dicom_stream::endl;
dicom_stream::cout << " DataFile Byte Swap : " << this->DataFile->GetPlatformIsBigEndian() << dicom_stream::endl;
int t2 = int((0x0000FF00 & callbackType) >> 8);
int t1 = int((0x000000FF & callbackType));
if (t1 == 0 && t2 == 0)
{
t1 = '?';
t2 = '?';
}
char ct2(t2);
char ct1(t1);
dicom_stream::cout << " Callback type : " << ct1 << ct2 << dicom_stream::endl;
dicom_stream::cout << "==============================" << dicom_stream::endl;
#endif
}
}
else
{
if (this->DataFile->GetPlatformIsBigEndian() == true)
{
switch (callbackType)
{
case DICOMParser::VR_OW:
case DICOMParser::VR_US:
case DICOMParser::VR_SS:
DICOMFile::swap2((ushort*) tempdata, (ushort*) tempdata, length/sizeof(ushort));
// dicom_stream::cout << "16 bit byte swap needed!" << dicom_stream::endl;
break;
case DICOMParser::VR_FL:
case DICOMParser::VR_FD:
DICOMFile::swap4((uint*) tempdata, (uint*) tempdata, length/sizeof(uint));
// dicom_stream::cout << "Float byte swap needed!" << dicom_stream::endl;
break;
case DICOMParser::VR_SL:
case DICOMParser::VR_UL:
DICOMFile::swap4((uint*) tempdata, (uint*) tempdata, length/sizeof(uint));
// dicom_stream::cout << "32 bit byte swap needed!" << dicom_stream::endl;
break;
case DICOMParser::VR_AT:
// dicom_stream::cout << "ATTRIBUTE Byte swap needed!" << dicom_stream::endl;
break;
default:
break;
}
}
}
dicom_stl::vector<DICOMCallback*> * cbVector = mv.second;
for (dicom_stl::vector<DICOMCallback*>::iterator cbiter = cbVector->begin();
cbiter != cbVector->end();
cbiter++)
{
(*cbiter)->Execute(this, // parser
ge.first, // group
ge.second, // element
callbackType, // type
tempdata, // data
length); // length
}
delete [] tempdata;
}
else
{
//
// Some lengths are negative, but we don't
// want to back up the file pointer.
//
if (length > 0)
{
DataFile->Skip(length);
}
#ifdef DEBUG_DICOM
this->DumpTag(this->ParserOutputFile, group, element, mytype, (unsigned char*) "Unread.", length);
#endif
}
}
void DICOMParser::InitTypeMap()
{
DICOMRecord dicom_tags[] = {{0x0002, 0x0002, DICOMParser::VR_UI}, // Media storage SOP class uid
{0x0002, 0x0003, DICOMParser::VR_UI}, // Media storage SOP inst uid
{0x0002, 0x0010, DICOMParser::VR_UI}, // Transfer syntax uid
{0x0002, 0x0012, DICOMParser::VR_UI}, // Implementation class uid
{0x0008, 0x0018, DICOMParser::VR_UI}, // Image UID
{0x0008, 0x0020, DICOMParser::VR_DA}, // Series date
{0x0008, 0x0030, DICOMParser::VR_TM}, // Series time
{0x0008, 0x0060, DICOMParser::VR_SH}, // Modality
{0x0008, 0x0070, DICOMParser::VR_SH}, // Manufacturer
{0x0008, 0x1060, DICOMParser::VR_SH}, // Physician
{0x0018, 0x0050, DICOMParser::VR_FL}, // slice thickness
{0x0018, 0x0060, DICOMParser::VR_FL}, // kV
{0x0018, 0x0088, DICOMParser::VR_FL}, // slice spacing
{0x0018, 0x1100, DICOMParser::VR_SH}, // Recon diameter
{0x0018, 0x1151, DICOMParser::VR_FL}, // mA
{0x0018, 0x1210, DICOMParser::VR_SH}, // Recon kernel
{0x0020, 0x000d, DICOMParser::VR_UI}, // Study UID
{0x0020, 0x000e, DICOMParser::VR_UI}, // Series UID
{0x0020, 0x0013, DICOMParser::VR_IS}, // Image number
{0x0020, 0x0032, DICOMParser::VR_SH}, // Patient position
{0x0020, 0x0037, DICOMParser::VR_SH}, // Patient position cosines
{0x0028, 0x0010, DICOMParser::VR_US}, // Num rows
{0x0028, 0x0011, DICOMParser::VR_US}, // Num cols
{0x0028, 0x0030, DICOMParser::VR_FL}, // pixel spacing
{0x0028, 0x0100, DICOMParser::VR_US}, // Bits allocated
{0x0028, 0x0120, DICOMParser::VR_UL}, // pixel padding
{0x0028, 0x1052, DICOMParser::VR_FL}, // pixel offset
{0x7FE0, 0x0010, DICOMParser::VR_OW} // pixel data
};
int num_tags = sizeof(dicom_tags)/sizeof(DICOMRecord);
doublebyte group;
doublebyte element;
VRTypes datatype;
for (int i = 0; i < num_tags; i++)
{
group = dicom_tags[i].group;
element = dicom_tags[i].element;
datatype = (VRTypes) dicom_tags[i].datatype;
Implementation->TypeMap.insert(dicom_stl::pair<const DICOMMapKey, DICOMTypeValue>(DICOMMapKey(group, element), datatype));
}
}
void DICOMParser::SetDICOMTagCallbacks(doublebyte group, doublebyte element, VRTypes datatype, dicom_stl::vector<DICOMCallback*>* cbVector)
{
Implementation->Map.insert(dicom_stl::pair<const DICOMMapKey, DICOMMapValue>(DICOMMapKey(group, element), DICOMMapValue((int)datatype, cbVector)));
}
bool DICOMParser::CheckMagic(char* magic_number)
{
return (
(magic_number[0] == DICOM_MAGIC[0]) &&
(magic_number[1] == DICOM_MAGIC[1]) &&
(magic_number[2] == DICOM_MAGIC[2]) &&
(magic_number[3] == DICOM_MAGIC[3])
);
}
void DICOMParser::DumpTag(dicom_stream::ostream& out, doublebyte group, doublebyte element, VRTypes vrtype, unsigned char* tempdata, quadbyte length)
{
int t2 = int((0x0000FF00 & vrtype) >> 8);
int t1 = int((0x000000FF & vrtype));
if (t1 == 0 && t2 == 0)
{
t1 = '?';
t2 = '?';
}
char ct2(t2);
char ct1(t1);
out << "(0x";
out.width(4);
char prev = out.fill('0');
out << dicom_stream::hex << group;
out << ",0x";
out.width(4);
out.fill('0');
out << dicom_stream::hex << element;
out << ") ";
out.fill(prev);
out << dicom_stream::dec;
out << " " << ct1 << ct2 << " ";
out << "[" << length << " bytes] ";
if (group == 0x7FE0 && element == 0x0010)
{
out << "Image data not printed." ;
}
else
{
out << (tempdata ? (char*) tempdata : "NULL");
}
out << dicom_stream::dec << dicom_stream::endl;
out.fill(prev);
out << dicom_stream::dec;
return;
}
void DICOMParser::ModalityTag(doublebyte, doublebyte, VRTypes, unsigned char* tempdata, quadbyte)
{
if (!strcmp( (char*)tempdata, "MR"))
{
// this->AddMRTags();
}
else if (!strcmp((char*) tempdata, "CT"))
{
}
else if (!strcmp((char*) tempdata, "US"))
{
}
}
void DICOMParser::AddDICOMTagCallbacks(doublebyte group, doublebyte element, VRTypes datatype, dicom_stl::vector<DICOMCallback*>* cbVector)
{
DICOMParserMap::iterator miter = Implementation->Map.find(DICOMMapKey(group,element));
if (miter != Implementation->Map.end())
{
for (dicom_stl::vector<DICOMCallback*>::iterator iter = cbVector->begin();
iter != cbVector->end();
iter++)
{
dicom_stl::vector<DICOMCallback*>* callbacks = (*miter).second.second;
callbacks->push_back(*iter);
}
}
else
{
this->SetDICOMTagCallbacks(group, element, datatype, cbVector);
}
}
void DICOMParser::AddDICOMTagCallback(doublebyte group, doublebyte element, VRTypes datatype, DICOMCallback* cb)
{
DICOMParserMap::iterator miter = Implementation->Map.find(DICOMMapKey(group,element));
if (miter != Implementation->Map.end())
{
dicom_stl::vector<DICOMCallback*>* callbacks = (*miter).second.second;
callbacks->push_back(cb);
}
else
{
dicom_stl::vector<DICOMCallback*>* callback = new dicom_stl::vector<DICOMCallback*>;
callback->push_back(cb);
this->SetDICOMTagCallbacks(group, element, datatype, callback);
}
}
void DICOMParser::AddDICOMTagCallbackToAllTags(DICOMCallback* cb)
{
DICOMParserMap::iterator miter;
for (miter = Implementation->Map.begin();
miter != Implementation->Map.end();
miter++);
{
dicom_stl::vector<DICOMCallback*>* callbacks = (*miter).second.second;
callbacks->push_back(cb);
}
}
bool DICOMParser::ParseExplicitRecord(doublebyte, doublebyte,
quadbyte& length,
VRTypes& represent)
{
doublebyte representation = DataFile->ReadDoubleByte();
bool valid = this->IsValidRepresentation(representation, length, represent);
if (valid)
{
return true;
}
else
{
represent = VR_UNKNOWN;
length = 0;
return false;
}
}
bool DICOMParser::ParseImplicitRecord(doublebyte group, doublebyte element,
quadbyte& length,
VRTypes& represent)
{
DICOMImplicitTypeMap::iterator iter =
Implementation->TypeMap.find(DICOMMapKey(group,element));
represent = VRTypes((*iter).second);
//
// length?
//
length = DataFile->ReadQuadByte();
return false;
}
void DICOMParser::TransferSyntaxCallback(DICOMParser *,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
#ifdef DEBUG_DICOM
dicom_stream::cout << "DICOMParser::TransferSyntaxCallback" << dicom_stream::endl;
#endif
const char* TRANSFER_UID_EXPLICIT_BIG_ENDIAN = "1.2.840.10008.1.2.2";
const char* TRANSFER_UID_GE_PRIVATE_IMPLICIT_BIG_ENDIAN = "1.2.840.113619.5.2";
// char* fileEndian = "LittleEndian";
// char* dataEndian = "LittleEndian";
this->ToggleByteSwapImageData = false;
if (strcmp(TRANSFER_UID_EXPLICIT_BIG_ENDIAN, (char*) val) == 0)
{
#ifdef DEBUG_DICOM
dicom_stream::cout << "EXPLICIT BIG ENDIAN" << dicom_stream::endl;
#endif
this->ToggleByteSwapImageData = true;
//
// Data byte order is big endian
//
// We're always reading little endian in the beginning,
// so now we need to swap.
}
else if (strcmp(TRANSFER_UID_GE_PRIVATE_IMPLICIT_BIG_ENDIAN, (char*) val) == 0)
{
this->ToggleByteSwapImageData = true;
#ifdef DEBUG_DICOM
dicom_stream::cout << "GE PRIVATE TRANSFER SYNTAX" << dicom_stream::endl;
dicom_stream::cout << "ToggleByteSwapImageData : " << this->ToggleByteSwapImageData << dicom_stream::endl;
#endif
}
else
{
}
}
void DICOMParser::GetGroupsElementsDatatypes(dicom_stl::vector<doublebyte>& groups,
dicom_stl::vector<doublebyte>& elements,
dicom_stl::vector<DICOMParser::VRTypes>& datatypes)
{
groups.clear();
elements.clear();
datatypes.clear();
dicom_stl::vector<doublebyte>::iterator giter; // = this->Groups.begin();
dicom_stl::vector<doublebyte>::iterator eiter; // = this->Elements.begin();
dicom_stl::vector<DICOMParser::VRTypes>::iterator diter; // = this->Datatypes.begin();
for (giter = this->Implementation->Groups.begin(), eiter = this->Implementation->Elements.begin(), diter = this->Implementation->Datatypes.begin();
giter != this->Implementation->Groups.end(), eiter != this->Implementation->Elements.end(), diter != this->Implementation->Datatypes.end();
giter++, eiter++, diter++)
{
groups.push_back(*giter);
elements.push_back(*eiter);
datatypes.push_back(*diter);
}
}
void DICOMParser::ClearAllDICOMTagCallbacks()
{
DICOMParserMap::iterator mapIter;
for (mapIter = this->Implementation->Map.begin();
mapIter != this->Implementation->Map.end();
mapIter++)
{
dicom_stl::pair<const DICOMMapKey, DICOMMapValue> mapPair = *mapIter;
DICOMMapValue mapVal = mapPair.second;
dicom_stl::vector<DICOMCallback*>* cbVector = mapVal.second;
delete cbVector;
}
this->Implementation->Map.clear();
}
DICOMParser::DICOMParser(const DICOMParser&)
{
dicom_stream::cerr << "DICOMParser copy constructor should not be called!" << dicom_stream::endl;
}
void DICOMParser::operator=(const DICOMParser&)
{
dicom_stream::cerr << "DICOMParser assignment operator should not be called!" << dicom_stream::endl;
}
#ifdef _MSC_VER
#pragma warning ( pop )
#endif