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.
780 lines
23 KiB
780 lines
23 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
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
|