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.
 
 
 
 
 
 

1388 lines
46 KiB

/*=========================================================================
Program: DICOMParser
Module: $RCSfile: DICOMAppHelper.cxx,v $
Language: C++
Date: $Date: 2006/08/31 17:30:29 $
Version: $Revision: 1.21.20.2 $
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 ( disable : 4702 )
#pragma warning ( push, 3 )
#endif
#include "DICOMConfig.h"
#include "DICOMAppHelper.h"
#include "DICOMCallback.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#if defined(__BORLANDC__)
#include <mem.h> // for memcpy
#endif
//#define DEBUG_DICOM_APP_HELPER
class DICOMAppHelperImplementation
{
public:
// map from series UID to vector of files in the series
dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr> SeriesUIDMap;
// map from filename to intraseries sortable tags
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr> SliceOrderingMap;
typedef dicom_stl::map<dicom_stl::pair<doublebyte, doublebyte>, DICOMTagInfo> TagMapType;
TagMapType TagMap;
};
struct lt_pair_int_string
{
bool operator()(const dicom_stl::pair<int, dicom_stl::string> s1,
const dicom_stl::pair<int, dicom_stl::string> s2) const
{
return s1.first < s2.first;
}
};
struct lt_pair_float_string
{
bool operator()(const dicom_stl::pair<float, dicom_stl::string> s1,
const dicom_stl::pair<float, dicom_stl::string> s2) const
{
return s1.first < s2.first;
}
};
struct gt_pair_int_string
{
bool operator()(const dicom_stl::pair<int, dicom_stl::string> s1,
const dicom_stl::pair<int, dicom_stl::string> s2) const
{
return s1.first > s2.first;
}
};
struct gt_pair_float_string
{
bool operator()(const dicom_stl::pair<float, dicom_stl::string> s1,
const dicom_stl::pair<float, dicom_stl::string> s2) const
{
return s1.first > s2.first;
}
};
DICOMAppHelper::DICOMAppHelper()
{
this->BitsAllocated = 8;
this->ByteSwapData = false;
this->PixelSpacing[0] = this->PixelSpacing[1] = this->PixelSpacing[2] = 1.0;
this->Dimensions[0] = this->Dimensions[1] = 0;
this->PhotometricInterpretation = new dicom_stl::string();
this->TransferSyntaxUID = new dicom_stl::string();
this->RescaleOffset = 0.0;
this->RescaleSlope = 1.0;
this->ImageData = NULL;
this->ImageDataLengthInBytes = 0;
this->PatientName = new dicom_stl::string();
this->StudyUID = new dicom_stl::string();
this->StudyID = new dicom_stl::string();
this->GantryAngle = 0.0;
this->Width = 0;
this->Height = 0;
this->PixelRepresentation = 0;
this->SeriesUIDCB = new DICOMMemberCallback<DICOMAppHelper>;
this->SliceNumberCB = new DICOMMemberCallback<DICOMAppHelper>;
this->SliceLocationCB = new DICOMMemberCallback<DICOMAppHelper>;
this->ImagePositionPatientCB = new DICOMMemberCallback<DICOMAppHelper>;
this->ImageOrientationPatientCB = new DICOMMemberCallback<DICOMAppHelper>;
this->TransferSyntaxCB = new DICOMMemberCallback<DICOMAppHelper>;
this->ToggleSwapBytesCB = new DICOMMemberCallback<DICOMAppHelper>;
this->BitsAllocatedCB = new DICOMMemberCallback<DICOMAppHelper>;
this->PixelSpacingCB = new DICOMMemberCallback<DICOMAppHelper>;
this->HeightCB = new DICOMMemberCallback<DICOMAppHelper>;
this->WidthCB = new DICOMMemberCallback<DICOMAppHelper>;
this->PixelRepresentationCB = new DICOMMemberCallback<DICOMAppHelper>;
this->PhotometricInterpretationCB = new DICOMMemberCallback<DICOMAppHelper>;
this->RescaleOffsetCB = new DICOMMemberCallback<DICOMAppHelper>;
this->RescaleSlopeCB = new DICOMMemberCallback<DICOMAppHelper>;
this->PixelDataCB = new DICOMMemberCallback<DICOMAppHelper>;
this->PatientNameCB = new DICOMMemberCallback<DICOMAppHelper>;
this->StudyUIDCB = new DICOMMemberCallback<DICOMAppHelper>;
this->StudyIDCB = new DICOMMemberCallback<DICOMAppHelper>;
this->GantryAngleCB = new DICOMMemberCallback<DICOMAppHelper>;
this->Implementation = new DICOMAppHelperImplementation;
}
DICOMAppHelper::~DICOMAppHelper()
{
this->Clear();
this->HeaderFile.close();
//
// Fix warning here.
//
if (this->ImageData)
{
delete [] (static_cast<char*> (this->ImageData));
}
if (this->TransferSyntaxUID)
{
delete this->TransferSyntaxUID;
}
if (this->PhotometricInterpretation)
{
delete this->PhotometricInterpretation;
}
if (this->PatientName)
{
delete this->PatientName;
}
if (this->StudyUID)
{
delete this->StudyUID;
}
if (this->StudyID)
{
delete this->StudyID;
}
delete this->SeriesUIDCB;
delete this->SliceNumberCB;
delete this->SliceLocationCB;
delete this->ImagePositionPatientCB;
delete this->ImageOrientationPatientCB;
delete this->TransferSyntaxCB;
delete this->ToggleSwapBytesCB;
delete this->BitsAllocatedCB;
delete this->PixelSpacingCB;
delete this->HeightCB;
delete this->WidthCB;
delete this->PixelRepresentationCB;
delete this->PhotometricInterpretationCB;
delete this->RescaleOffsetCB;
delete this->RescaleSlopeCB;
delete this->PixelDataCB;
delete this->PatientNameCB;
delete this->StudyUIDCB;
delete this->StudyIDCB;
delete this->GantryAngleCB;
delete this->Implementation;
}
void DICOMAppHelper::RegisterCallbacks(DICOMParser* parser)
{
if (!parser)
{
dicom_stream::cerr << "Null parser!" << dicom_stream::endl;
}
SeriesUIDCB->SetCallbackFunction(this, &DICOMAppHelper::SeriesUIDCallback);
parser->AddDICOMTagCallback(0x0020, 0x000e, DICOMParser::VR_UI, SeriesUIDCB);
SliceNumberCB->SetCallbackFunction(this, &DICOMAppHelper::SliceNumberCallback);
parser->AddDICOMTagCallback(0x0020, 0x0013, DICOMParser::VR_IS, SliceNumberCB);
SliceLocationCB->SetCallbackFunction(this, &DICOMAppHelper::SliceLocationCallback);
parser->AddDICOMTagCallback(0x0020, 0x1041, DICOMParser::VR_CS, SliceLocationCB);
ImagePositionPatientCB->SetCallbackFunction(this, &DICOMAppHelper::ImagePositionPatientCallback);
parser->AddDICOMTagCallback(0x0020, 0x0032, DICOMParser::VR_SH, ImagePositionPatientCB);
ImageOrientationPatientCB->SetCallbackFunction(this, &DICOMAppHelper::ImageOrientationPatientCallback);
parser->AddDICOMTagCallback(0x0020, 0x0037, DICOMParser::VR_SH, ImageOrientationPatientCB);
TransferSyntaxCB->SetCallbackFunction(this, &DICOMAppHelper::TransferSyntaxCallback);
parser->AddDICOMTagCallback(0x0002, 0x0010, DICOMParser::VR_UI, TransferSyntaxCB);
ToggleSwapBytesCB->SetCallbackFunction(this, &DICOMAppHelper::ToggleSwapBytesCallback);
BitsAllocatedCB->SetCallbackFunction(this, &DICOMAppHelper::BitsAllocatedCallback);
parser->AddDICOMTagCallback(0x0028, 0x0100, DICOMParser::VR_US, BitsAllocatedCB);
PixelSpacingCB->SetCallbackFunction(this, &DICOMAppHelper::PixelSpacingCallback);
parser->AddDICOMTagCallback(0x0028, 0x0030, DICOMParser::VR_FL, PixelSpacingCB);
parser->AddDICOMTagCallback(0x0018, 0x0050, DICOMParser::VR_FL, PixelSpacingCB);
WidthCB->SetCallbackFunction(this, &DICOMAppHelper::WidthCallback);
parser->AddDICOMTagCallback(0x0028, 0x0011, DICOMParser::VR_US, WidthCB);
HeightCB->SetCallbackFunction(this, &DICOMAppHelper::HeightCallback);
parser->AddDICOMTagCallback(0x0028, 0x0010, DICOMParser::VR_US, HeightCB);
PixelRepresentationCB->SetCallbackFunction(this, &DICOMAppHelper::PixelRepresentationCallback);
parser->AddDICOMTagCallback(0x0028, 0x0103, DICOMParser::VR_US, PixelRepresentationCB);
PhotometricInterpretationCB->SetCallbackFunction(this, &DICOMAppHelper::PhotometricInterpretationCallback);
parser->AddDICOMTagCallback(0x0028, 0x0004, DICOMParser::VR_CS, PhotometricInterpretationCB);
RescaleOffsetCB->SetCallbackFunction(this, &DICOMAppHelper::RescaleOffsetCallback);
parser->AddDICOMTagCallback(0x0028, 0x1052, DICOMParser::VR_CS, RescaleOffsetCB);
RescaleSlopeCB->SetCallbackFunction(this, &DICOMAppHelper::RescaleSlopeCallback);
parser->AddDICOMTagCallback(0x0028, 0x1053, DICOMParser::VR_FL, RescaleSlopeCB);
PatientNameCB->SetCallbackFunction(this, &DICOMAppHelper::PatientNameCallback);
parser->AddDICOMTagCallback(0x0010, 0x0010, DICOMParser::VR_PN, PatientNameCB);
StudyUIDCB->SetCallbackFunction(this, &DICOMAppHelper::StudyUIDCallback);
parser->AddDICOMTagCallback(0x0020, 0x000d, DICOMParser::VR_UI, StudyUIDCB);
StudyIDCB->SetCallbackFunction(this, &DICOMAppHelper::StudyIDCallback);
parser->AddDICOMTagCallback(0x0020, 0x0010, DICOMParser::VR_SH, StudyIDCB);
GantryAngleCB->SetCallbackFunction(this, &DICOMAppHelper::GantryAngleCallback);
parser->AddDICOMTagCallback(0x0018, 0x1120, DICOMParser::VR_FL, GantryAngleCB);
DICOMTagInfo 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"},
{0x0020, 0x1041, DICOMParser::VR_CS, "Slice location"},
{0x0028, 0x0010, DICOMParser::VR_FL, "Num rows"},
{0x0028, 0x0011, DICOMParser::VR_FL, "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"}
};
int num_tags = sizeof(dicom_tags)/sizeof(DICOMTagInfo);
#ifdef DEBUG_DICOM_APP_HELPER
DICOMMemberCallback<DICOMAppHelper>** callbackArray = new DICOMMemberCallback<DICOMAppHelper>*[num_tags];
#endif
for (int j = 0; j < num_tags; j++)
{
//
// Setup internal map.
//
DICOMTagInfo tagStruct = dicom_tags[j];
doublebyte group = tagStruct.group;
doublebyte element = tagStruct.element;
dicom_stl::pair<doublebyte, doublebyte> gePair(group, element);
dicom_stl::pair<const dicom_stl::pair<doublebyte, doublebyte>, DICOMTagInfo> mapPair(gePair, tagStruct);
this->Implementation->TagMap.insert(mapPair);
#ifdef DEBUG_DICOM_APP_HELPER
//
// Make callback
//
callbackArray[j] = new DICOMMemberCallback<DICOMAppHelper>;
callbackArray[j]->SetCallbackFunction(this, &DICOMAppHelper::ArrayCallback);
//
// Set callback on parser.
//
parser->AddDICOMTagCallback(group, element,datatype, callbackArray[j]);
#endif
}
}
void DICOMAppHelper::SeriesUIDCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
char* newString = (char*) val;
dicom_stl::string newStdString(newString);
dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr>::iterator iter = this->Implementation->SeriesUIDMap.find(newStdString);
if ( iter == this->Implementation->SeriesUIDMap.end())
{
dicom_stl::vector<dicom_stl::string> newVector;
newVector.push_back(parser->GetFileName());
this->Implementation->SeriesUIDMap.insert(dicom_stl::pair<const dicom_stl::string, dicom_stl::vector<dicom_stl::string> > (newStdString, newVector));
}
else
{
(*iter).second.push_back(parser->GetFileName());
}
}
void DICOMAppHelper::OutputSeries()
{
dicom_stream::cout << dicom_stream::endl << dicom_stream::endl;
for (dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr >::iterator iter = this->Implementation->SeriesUIDMap.begin();
iter != this->Implementation->SeriesUIDMap.end();
iter++)
{
dicom_stream::cout << "SERIES: " << (*iter).first.c_str() << dicom_stream::endl;
dicom_stl::vector<dicom_stl::string>& v_ref = (*iter).second;
for (dicom_stl::vector<dicom_stl::string>::iterator v_iter = v_ref.begin();
v_iter != v_ref.end();
v_iter++)
{
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator sn_iter = Implementation->SliceOrderingMap.find(*v_iter);
int slice = -1;
if (sn_iter != Implementation->SliceOrderingMap.end())
{
slice = (*sn_iter).second.SliceNumber;
}
dicom_stream::cout << "\t" << (*v_iter).c_str() << " [" << slice << "]" << dicom_stream::endl;
}
}
}
void DICOMAppHelper::ArrayCallback(DICOMParser *parser,
doublebyte group,
doublebyte element,
DICOMParser::VRTypes datatype,
unsigned char* val,
quadbyte len)
{
const char* desc = "No description";
TagMapType::iterator iter = this->Implementation->TagMap.find(dicom_stl::pair<doublebyte, doublebyte> (group, element));
if (iter != this->Implementation->TagMap.end())
{
desc = (*iter).second.description;
}
int t2 = int((0x0000FF00 & datatype) >> 8);
int t1 = int((0x000000FF & datatype));
char ct2(t2);
char ct1(t1);
HeaderFile << "(0x";
HeaderFile.width(4);
char prev = HeaderFile.fill('0');
HeaderFile << dicom_stream::hex << group;
HeaderFile << ",0x";
HeaderFile.width(4);
HeaderFile.fill('0');
HeaderFile << dicom_stream::hex << element;
HeaderFile << ") ";
HeaderFile.fill(prev);
HeaderFile << dicom_stream::dec;
HeaderFile << " " << ct1 << ct2 << " ";
HeaderFile << "[" << len << " bytes] ";
HeaderFile << desc << " : ";
unsigned int uival = 0;
float fval = 0;
double dval = 0;
int ival = 0;
if (val)
{
switch (datatype)
{
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_LO:
case DICOMParser::VR_LT:
case DICOMParser::VR_OB: // ordered bytes
case DICOMParser::VR_OW: // ordered words
case DICOMParser::VR_PN:
case DICOMParser::VR_ST:
case DICOMParser::VR_TM:
case DICOMParser::VR_UN:
case DICOMParser::VR_UT:
case DICOMParser::VR_SQ: // sequence
case DICOMParser::VR_SH: // strings
case DICOMParser::VR_IS:
HeaderFile << val;
break;
case DICOMParser::VR_FL: // float
fval = static_cast<float> (atof((char*) val));
HeaderFile << fval;
break;
case DICOMParser::VR_FD: // float double
fval = static_cast<float> (atof((char*) val));
HeaderFile << dval;
break;
case DICOMParser::VR_UL: // unsigned long
case DICOMParser::VR_SL: // signed long
case DICOMParser::VR_AT:
HeaderFile << uival;
break;
//case DICOMParser::VR_IS:
// ival = DICOMFile::ReturnAsSignedLong(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
// HeaderFile << ival;
// break;
case DICOMParser::VR_SS:
ival = DICOMFile::ReturnAsSignedShort(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
HeaderFile << ival;
break;
case DICOMParser::VR_US: // unsigned short
uival = DICOMFile::ReturnAsUnsignedShort(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
HeaderFile << uival;
break;
default:
HeaderFile << val << dicom_stream::endl;
break;
}
}
else
{
HeaderFile << "NULL";
}
HeaderFile << dicom_stream::dec << dicom_stream::endl;
HeaderFile.fill(prev);
delete [] val;
}
void DICOMAppHelper::SliceNumberCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
// Look for the current file in the map of slice ordering data
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator it;
it = this->Implementation->SliceOrderingMap.find(parser->GetFileName());
if (it == Implementation->SliceOrderingMap.end())
{
// file not found, create a new entry
DICOMOrderingElements ord;
ord.SliceNumber = atoi( (char *) val);
// insert into the map
this->Implementation->SliceOrderingMap.insert(dicom_stl::pair<const dicom_stl::string, DICOMOrderingElements>(parser->GetFileName(), ord));
}
else
{
// file found, add new values
(*it).second.SliceNumber = atoi( (char *)val );
}
// cache the slice number
this->SliceNumber = atoi( (char *) val);
}
void DICOMAppHelper::SliceLocationCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
// Look for the current file in the map of slice ordering data
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator it;
it = this->Implementation->SliceOrderingMap.find(parser->GetFileName());
if (it == Implementation->SliceOrderingMap.end())
{
// file not found, create a new entry
DICOMOrderingElements ord;
ord.SliceLocation = (float)atof( (char *) val);
// insert into the map
this->Implementation->SliceOrderingMap.insert(dicom_stl::pair<const dicom_stl::string,
DICOMOrderingElements>(parser->GetFileName(), ord));
}
else
{
// file found, add new values
(*it).second.SliceLocation = (float)atof( (char *)val );
}
}
void DICOMAppHelper::ImagePositionPatientCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
// Look for the current file in the map of slice ordering data
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator it;
it = this->Implementation->SliceOrderingMap.find(parser->GetFileName());
if (it == Implementation->SliceOrderingMap.end())
{
// file not found, create a new entry
DICOMOrderingElements ord;
if (val)
{
sscanf( (char*)(val), "%f\\%f\\%f",
&ord.ImagePositionPatient[0],
&ord.ImagePositionPatient[1],
&ord.ImagePositionPatient[2] );
}
else
{
// no actual position specified, default to the origin
ord.ImagePositionPatient[0] = 0.0;
ord.ImagePositionPatient[1] = 0.0;
ord.ImagePositionPatient[2] = 0.0;
}
// insert into the map
this->Implementation->SliceOrderingMap.insert(dicom_stl::pair<const dicom_stl::string,
DICOMOrderingElements>(parser->GetFileName(), ord));
// cache the value
memcpy( this->ImagePositionPatient, ord.ImagePositionPatient,
3*sizeof(float) );
}
else
{
if (val)
{
// file found, add new values
sscanf( (char*)(val), "%f\\%f\\%f",
&(*it).second.ImagePositionPatient[0],
&(*it).second.ImagePositionPatient[1],
&(*it).second.ImagePositionPatient[2] );
}
else
{
// no actual position specified, default to the origin
(*it).second.ImagePositionPatient[0] = 0.0;
(*it).second.ImagePositionPatient[1] = 0.0;
(*it).second.ImagePositionPatient[2] = 0.0;
}
// cache the value
memcpy( this->ImagePositionPatient, (*it).second.ImagePositionPatient,
3*sizeof(float) );
}
}
void DICOMAppHelper::ImageOrientationPatientCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
// Look for the current file in the map of slice ordering data
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator it;
it = this->Implementation->SliceOrderingMap.find(parser->GetFileName());
if (it == Implementation->SliceOrderingMap.end())
{
// file not found, create a new entry
DICOMOrderingElements ord;
if (val)
{
sscanf( (char*)(val), "%f\\%f\\%f\\%f\\%f\\%f",
&ord.ImageOrientationPatient[0],
&ord.ImageOrientationPatient[1],
&ord.ImageOrientationPatient[2],
&ord.ImageOrientationPatient[3],
&ord.ImageOrientationPatient[4],
&ord.ImageOrientationPatient[5] );
}
else
{
// no orientation defined, default to an standard axial orientation
ord.ImageOrientationPatient[0] = 1.0;
ord.ImageOrientationPatient[1] = 0.0;
ord.ImageOrientationPatient[2] = 0.0;
ord.ImageOrientationPatient[3] = 0.0;
ord.ImageOrientationPatient[4] = 1.0;
ord.ImageOrientationPatient[5] = 0.0;
}
// insert into the map
this->Implementation->SliceOrderingMap.insert(dicom_stl::pair<const dicom_stl::string,
DICOMOrderingElements>(parser->GetFileName(), ord));
// cache the value
memcpy( this->ImageOrientationPatient, ord.ImageOrientationPatient,
6*sizeof(float) );
}
else
{
// file found, add new values
if (val)
{
sscanf( (char*)(val), "%f\\%f\\%f\\%f\\%f\\%f",
&(*it).second.ImageOrientationPatient[0],
&(*it).second.ImageOrientationPatient[1],
&(*it).second.ImageOrientationPatient[2],
&(*it).second.ImageOrientationPatient[3],
&(*it).second.ImageOrientationPatient[4],
&(*it).second.ImageOrientationPatient[5] );
}
else
{
// no orientation defined, default to an standard axial orientation
(*it).second.ImageOrientationPatient[0] = 1.0;
(*it).second.ImageOrientationPatient[1] = 0.0;
(*it).second.ImageOrientationPatient[2] = 0.0;
(*it).second.ImageOrientationPatient[3] = 0.0;
(*it).second.ImageOrientationPatient[4] = 1.0;
(*it).second.ImageOrientationPatient[5] = 0.0;
}
// cache the value
memcpy( this->ImageOrientationPatient, (*it).second.ImageOrientationPatient,
6*sizeof(float) );
}
}
void DICOMAppHelper::TransferSyntaxCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
#ifdef DEBUG_DICOM_APP_HELPER
#ifdef WIN32
char platformByteOrder = 'L';
#else
char platformByteOrder = 'B';
#endif
dicom_stream::cout << "Platform byte order: " << platformByteOrder << dicom_stream::endl;
#endif
static const char* TRANSFER_UID_EXPLICIT_BIG_ENDIAN = "1.2.840.10008.1.2.2";
// Only add the ToggleSwapBytes callback when we need it.
if (strcmp(TRANSFER_UID_EXPLICIT_BIG_ENDIAN, (char*) val) == 0)
{
this->ByteSwapData = true;
parser->AddDICOMTagCallback(0x0800, 0x0000, DICOMParser::VR_UNKNOWN, ToggleSwapBytesCB);
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cerr <<"Registering callback for swapping bytes." << dicom_stream::endl;
#endif
}
if (this->TransferSyntaxUID)
{
delete this->TransferSyntaxUID;
}
this->TransferSyntaxUID = new dicom_stl::string((char*) val);
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Transfer Syntax UID: " << *this->TransferSyntaxUID;
dicom_stream::cout << " " << this->TransferSyntaxUIDDescription(this->TransferSyntaxUID->c_str()) << dicom_stream::endl;
#endif
}
void DICOMAppHelper::BitsAllocatedCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
this->BitsAllocated = parser->GetDICOMFile()->ReturnAsUnsignedShort(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Bits allocated: " << this->BitsAllocated << dicom_stream::endl;
#endif
}
void DICOMAppHelper::ToggleSwapBytesCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* ,
quadbyte len)
{
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "ToggleSwapBytesCallback" << dicom_stream::endl;
#endif
bool bs = parser->GetDICOMFile()->GetPlatformIsBigEndian();
parser->GetDICOMFile()->SetPlatformIsBigEndian(!bs);
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Set byte swap to: " << parser->GetDICOMFile()->GetPlatformIsBigEndian() << dicom_stream::endl;
#endif
long pos = parser->GetDICOMFile()->Tell();
//
// The +4 is probably a hack, but it's a guess at the length of the previous field.
//
parser->GetDICOMFile()->SkipToPos(pos - len + 4);
}
void DICOMAppHelper::PixelSpacingCallback(DICOMParser *parser,
doublebyte group,
doublebyte element,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
float fval = DICOMFile::ReturnAsFloat(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
if (group == 0x0028 && element == 0x0030)
{
this->PixelSpacing[0] = this->PixelSpacing[1] = fval;
}
else if (group == 0x0018 && element == 0x0050)
{
this->PixelSpacing[2] = fval;
}
}
void DICOMAppHelper::WidthCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
unsigned short uival = DICOMFile::ReturnAsUnsignedShort(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Width: " << uival << dicom_stream::endl;
#endif
this->Width = uival;
this->Dimensions[0] = this->Width;
}
void DICOMAppHelper::HeightCallback(DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
unsigned short uival = DICOMFile::ReturnAsUnsignedShort(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Height: " << uival << dicom_stream::endl;
#endif
this->Height = uival;
this->Dimensions[1] = this->Height;
}
void DICOMAppHelper::PixelRepresentationCallback( DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
unsigned short uival = DICOMFile::ReturnAsUnsignedShort(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Pixel Representation: " << (uival ? "Signed" : "Unsigned") << dicom_stream::endl;
#endif
this->PixelRepresentation = uival;
}
void DICOMAppHelper::PhotometricInterpretationCallback( DICOMParser *,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Photometric Interpretation: " << (char*) val << dicom_stream::endl;
#endif
if (this->PhotometricInterpretation)
{
delete this->PhotometricInterpretation;
}
this->PhotometricInterpretation = new dicom_stl::string((char*) val);
}
void DICOMAppHelper::PixelDataCallback( DICOMParser *,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* data,
quadbyte len)
{
int numPixels = this->Dimensions[0] * this->Dimensions[1] * this->GetNumberOfComponents();
if (len < numPixels)
{
numPixels = len;
}
if (numPixels < 0)
{
numPixels = 0;
}
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "numPixels : " << numPixels << dicom_stream::endl;
#endif
int ptrIncr = int(this->BitsAllocated/8.0);
unsigned short* ushortInputData = reinterpret_cast<unsigned short*>(data);
unsigned char* ucharInputData = data;
short* shortInputData = reinterpret_cast<short*> (data);
float* floatOutputData; // = NULL;
bool isFloat = this->RescaledImageDataIsFloat();
if (isFloat)
{
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Slope and offset are not integer valued : ";
dicom_stream::cout << this->RescaleSlope << ", " << this->RescaleOffset << dicom_stream::endl;
#endif
if (this->ImageData)
{
delete [] (static_cast<char*> (this->ImageData));
}
this->ImageData = new float[numPixels];
floatOutputData = static_cast<float*> (this->ImageData);
this->ImageDataType = DICOMParser::VR_FL;
this->ImageDataLengthInBytes = numPixels * sizeof(float);
float newFloatPixel;
if (ptrIncr == 1)
{
for (int i = 0; i < numPixels; i++)
{
newFloatPixel = float(this->RescaleSlope * ucharInputData[i] + this->RescaleOffset);
floatOutputData[i] = newFloatPixel;
}
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Did rescale, offset to float from char." << dicom_stream::endl;
dicom_stream::cout << numPixels << " pixels." << dicom_stream::endl;
#endif
}
else if (ptrIncr == 2)
{
for (int i = 0; i < numPixels; i++)
{
newFloatPixel = float(this->RescaleSlope * ushortInputData[i] + this->RescaleOffset);
floatOutputData[i] = newFloatPixel;
}
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Did rescale, offset to float from short." << dicom_stream::endl;
dicom_stream::cout << numPixels << " pixels." << dicom_stream::endl;
#endif
}
}
else
{
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Slope and offset are integer valued : ";
dicom_stream::cout << this->RescaleSlope << ", " << this->RescaleOffset << dicom_stream::endl;
#endif
if (ptrIncr == 1)
{
if (this->ImageData)
{
delete [] (static_cast<char*> (this->ImageData));
}
this->ImageData = new char[numPixels];
char* charOutputData = static_cast<char*> (this->ImageData);
this->ImageDataType = DICOMParser::VR_OB;
this->ImageDataLengthInBytes = numPixels * sizeof(char);
char newCharPixel;
for (int i = 0; i < numPixels; i++)
{
newCharPixel = char(this->RescaleSlope * ucharInputData[i] + this->RescaleOffset);
charOutputData[i] = newCharPixel;
}
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Did rescale, offset to char from char." << dicom_stream::endl;
dicom_stream::cout << numPixels << " pixels." << dicom_stream::endl;
#endif
}
else if (ptrIncr == 2)
{
if (this->ImageData)
{
delete [] (static_cast<char*> (this->ImageData));
}
this->ImageData = new short[numPixels];
short* shortOutputData = static_cast<short*> (this->ImageData);
this->ImageDataType = DICOMParser::VR_OW;
this->ImageDataLengthInBytes = numPixels * sizeof(short);
short newShortPixel;
for (int i = 0; i < numPixels; i++)
{
newShortPixel = short(this->RescaleSlope * shortInputData[i] + this->RescaleOffset);
shortOutputData[i] = newShortPixel;
}
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Did rescale, offset to short from short." << dicom_stream::endl;
dicom_stream::cout << numPixels << " pixels." << dicom_stream::endl;
#endif
}
}
}
void DICOMAppHelper::RegisterPixelDataCallback(DICOMParser* parser)
{
this->PixelDataCB->SetCallbackFunction(this, &DICOMAppHelper::PixelDataCallback);
parser->AddDICOMTagCallback(0x7FE0, 0x0010, DICOMParser::VR_OW, this->PixelDataCB);
}
void DICOMAppHelper::RescaleOffsetCallback( DICOMParser *parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
float fval = DICOMFile::ReturnAsFloat(val, parser->GetDICOMFile()->GetPlatformIsBigEndian());
this->RescaleOffset = fval;
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Pixel offset: " << this->RescaleOffset << dicom_stream::endl;
#endif
}
const char* DICOMAppHelper::TransferSyntaxUIDDescription(const char* uid)
{
static const char* DICOM_IMPLICIT_VR_LITTLE_ENDIAN = "1.2.840.10008.1.2";
static const char* DICOM_LOSSLESS_JPEG = "1.2.840.10008.1.2.4.70";
static const char* DICOM_LOSSY_JPEG_8BIT = "1.2.840.10008.1.2.4.50";
static const char* DICOM_LOSSY_JPEG_16BIT = "1.2.840.10008.1.2.4.51";
static const char* DICOM_EXPLICIT_VR_LITTLE_ENDIAN = "1.2.840.10008.1.2.1";
static const char* DICOM_EXPLICIT_VR_BIG_ENDIAN = "1.2.840.10008.1.2.2";
static const char* DICOM_GE_PRIVATE_IMPLICIT_BIG_ENDIAN = "1.2.840.113619.5.2";
if (!strcmp(DICOM_IMPLICIT_VR_LITTLE_ENDIAN, uid))
{
return "Implicit VR, Little Endian";
}
else if (!strcmp(DICOM_LOSSLESS_JPEG, uid))
{
return "Lossless JPEG";
}
else if (!strcmp(DICOM_LOSSY_JPEG_8BIT, uid))
{
return "Lossy JPEG 8 bit";
}
else if (!strcmp(DICOM_LOSSY_JPEG_16BIT, uid))
{
return "Lossy JPEG 16 bit.";
}
else if (!strcmp(DICOM_EXPLICIT_VR_LITTLE_ENDIAN, uid))
{
return "Explicit VR, Little Endian.";
}
else if (!strcmp(DICOM_EXPLICIT_VR_BIG_ENDIAN, uid))
{
return "Explicit VR, Big Endian.";
}
else if (!strcmp(DICOM_GE_PRIVATE_IMPLICIT_BIG_ENDIAN, uid))
{
return "GE Private, Implicit VR, Big Endian Image Data.";
}
else
{
return "Unknown.";
}
}
void DICOMAppHelper::RescaleSlopeCallback(DICOMParser *parser,
doublebyte,
doublebyte ,
DICOMParser::VRTypes ,
unsigned char* val,
quadbyte )
{
float fval = DICOMFile::ReturnAsFloat(val,
parser->GetDICOMFile()->GetPlatformIsBigEndian ());
#ifdef DEBUG_DICOM_APP_HELPER
dicom_stream::cout << "Rescale slope: " << fval << dicom_stream::endl;
#endif
this->RescaleSlope = fval;
}
bool DICOMAppHelper::RescaledImageDataIsFloat()
{
int s = int(this->RescaleSlope);
int o = int(this->RescaleOffset);
float sf = float(s);
float of = float(o);
double d1 = fabs(sf - this->RescaleSlope);
double d2 = fabs(of - this->RescaleOffset);
if (d1 > 0.0 || d2 > 0.0)
{
return true;
}
else
{
return false;
}
}
void DICOMAppHelper::GetImageData(void*& data, DICOMParser::VRTypes& dataType, unsigned long& len)
{
data = this->ImageData;
dataType = this->ImageDataType;
len = this->ImageDataLengthInBytes;
}
bool DICOMAppHelper::RescaledImageDataIsSigned()
{
bool rescaleSigned = (this->RescaleSlope < 0.0);
bool pixelRepSigned = (this->PixelRepresentation == 1);
bool offsetSigned = (this->RescaleOffset < 0.0);
return (rescaleSigned || pixelRepSigned || offsetSigned);
}
void DICOMAppHelper::GetSliceNumberFilenamePairs(const dicom_stl::string &seriesUID,
dicom_stl::vector<dicom_stl::pair<int, dicom_stl::string> >& v, bool ascending)
{
v.clear();
dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr >::iterator miter = this->Implementation->SeriesUIDMap.find(seriesUID);
if (miter == this->Implementation->SeriesUIDMap.end() )
{
return;
}
// grab the filenames for the specified series
dicom_stl::vector<dicom_stl::string> files = (*miter).second;
for (dicom_stl::vector<dicom_stl::string>::iterator fileIter = files.begin();
fileIter != files.end();
fileIter++)
{
dicom_stl::pair<int, dicom_stl::string> p;
p.second = dicom_stl::string(*fileIter);
int slice_number;
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator sn_iter = Implementation->SliceOrderingMap.find(*fileIter);
// Only store files that have a valid slice number
if (sn_iter != Implementation->SliceOrderingMap.end())
{
slice_number = (*sn_iter).second.SliceNumber;
p.first = slice_number;
v.push_back(p);
}
}
if (ascending)
{
dicom_stl::sort(v.begin(), v.end(), lt_pair_int_string());
}
else
{
dicom_stl::sort(v.begin(), v.end(), gt_pair_int_string());
}
}
void DICOMAppHelper::GetSliceNumberFilenamePairs(dicom_stl::vector<dicom_stl::pair<int, dicom_stl::string> >& v, bool ascending)
{
// Default to using the first series
if (this->Implementation->SeriesUIDMap.size() > 0)
{
this->GetSliceNumberFilenamePairs( (*this->Implementation->SeriesUIDMap.begin()).first, v, ascending );
}
else
{
v.clear();
}
}
void DICOMAppHelper::GetSliceLocationFilenamePairs(const dicom_stl::string &seriesUID,
dicom_stl::vector<dicom_stl::pair<float, dicom_stl::string> >& v, bool ascending)
{
v.clear();
dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr >::iterator miter = this->Implementation->SeriesUIDMap.find(seriesUID);
if (miter == this->Implementation->SeriesUIDMap.end() )
{
return;
}
// grab the filenames for the specified series
dicom_stl::vector<dicom_stl::string> files = (*miter).second;
for (dicom_stl::vector<dicom_stl::string>::iterator fileIter = files.begin();
fileIter != files.end();
fileIter++)
{
dicom_stl::pair<float, dicom_stl::string> p;
p.second = dicom_stl::string(*fileIter);
float slice_location;
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator sn_iter = Implementation->SliceOrderingMap.find(*fileIter);
if (sn_iter != Implementation->SliceOrderingMap.end())
{
slice_location = (*sn_iter).second.SliceLocation;
p.first = slice_location;
v.push_back(p);
}
}
if (ascending)
{
dicom_stl::sort(v.begin(), v.end(), lt_pair_float_string());
}
else
{
dicom_stl::sort(v.begin(), v.end(), gt_pair_float_string());
}
}
void DICOMAppHelper::GetSliceLocationFilenamePairs(dicom_stl::vector<dicom_stl::pair<float, dicom_stl::string> >& v, bool ascending)
{
// Default to using the first series
if (this->Implementation->SeriesUIDMap.size() > 0)
{
this->GetSliceLocationFilenamePairs( (*this->Implementation->SeriesUIDMap.begin()).first,
v , ascending);
}
else
{
v.clear();
}
}
void DICOMAppHelper::GetImagePositionPatientFilenamePairs(const dicom_stl::string &seriesUID, dicom_stl::vector<dicom_stl::pair<float, dicom_stl::string> >& v, bool ascending)
{
v.clear();
dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr >::iterator miter = this->Implementation->SeriesUIDMap.find(seriesUID);
if (miter == this->Implementation->SeriesUIDMap.end() )
{
return;
}
// grab the filenames for the specified series
dicom_stl::vector<dicom_stl::string> files = (*miter).second;
for (dicom_stl::vector<dicom_stl::string>::iterator fileIter = files.begin();
fileIter != files.end();
fileIter++)
{
dicom_stl::pair<float, dicom_stl::string> p;
p.second = dicom_stl::string(*fileIter);
float image_position;
float normal[3];
dicom_stl::map<dicom_stl::string, DICOMOrderingElements, ltstdstr>::iterator sn_iter =
Implementation->SliceOrderingMap.find(*fileIter);
if (sn_iter != Implementation->SliceOrderingMap.end())
{
// compute the image patient position wrt to the slice image
// plane normal
normal[0] = ((*sn_iter).second.ImageOrientationPatient[1]
* (*sn_iter).second.ImageOrientationPatient[5])
- ((*sn_iter).second.ImageOrientationPatient[2]
* (*sn_iter).second.ImageOrientationPatient[4]);
normal[1] = ((*sn_iter).second.ImageOrientationPatient[0]
*(*sn_iter).second.ImageOrientationPatient[5])
- ((*sn_iter).second.ImageOrientationPatient[2]
* (*sn_iter).second.ImageOrientationPatient[3]);
normal[2] = ((*sn_iter).second.ImageOrientationPatient[0]
* (*sn_iter).second.ImageOrientationPatient[4])
- ((*sn_iter).second.ImageOrientationPatient[1]
* (*sn_iter).second.ImageOrientationPatient[3]);
image_position = (normal[0]*(*sn_iter).second.ImagePositionPatient[0])
+ (normal[1]*(*sn_iter).second.ImagePositionPatient[1])
+ (normal[2]*(*sn_iter).second.ImagePositionPatient[2]);
p.first = image_position;
v.push_back(p);
}
}
if (ascending)
{
dicom_stl::sort(v.begin(), v.end(), lt_pair_float_string());
}
else
{
dicom_stl::sort(v.begin(), v.end(), gt_pair_float_string());
}
}
void DICOMAppHelper::GetImagePositionPatientFilenamePairs(dicom_stl::vector<dicom_stl::pair<float, dicom_stl::string> >& v, bool ascending)
{
// Default to using the first series
if (this->Implementation->SeriesUIDMap.size() > 0)
{
this->GetImagePositionPatientFilenamePairs(
(*this->Implementation->SeriesUIDMap.begin()).first, v, ascending);
}
else
{
v.clear();
}
}
void DICOMAppHelper::GetSeriesUIDs(dicom_stl::vector<dicom_stl::string> &v)
{
v.clear();
dicom_stl::map<dicom_stl::string, dicom_stl::vector<dicom_stl::string>, ltstdstr >::iterator miter;
for (miter = this->Implementation->SeriesUIDMap.begin(); miter != this->Implementation->SeriesUIDMap.end();
++miter)
{
v.push_back( (*miter).first );
}
}
void DICOMAppHelper::Clear()
{
this->Implementation->SliceOrderingMap.clear();
this->Implementation->SeriesUIDMap.clear();
}
void DICOMAppHelper::PatientNameCallback(DICOMParser *,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
if (this->PatientName)
{
delete this->PatientName;
}
if (val)
{
this->PatientName = new dicom_stl::string((char*) val);
}
else
{
this->PatientName = new dicom_stl::string();
}
}
void DICOMAppHelper::StudyUIDCallback(DICOMParser *,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
if (this->StudyUID)
{
delete this->StudyUID;
}
this->StudyUID = new dicom_stl::string((char*) val);
}
void DICOMAppHelper::StudyIDCallback(DICOMParser *,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
if (this->StudyID)
{
delete this->StudyID;
}
if (val)
{
this->StudyID = new dicom_stl::string((char*) val);
}
else
{
this->StudyID = new dicom_stl::string();
}
}
void DICOMAppHelper::GantryAngleCallback(DICOMParser * parser,
doublebyte,
doublebyte,
DICOMParser::VRTypes,
unsigned char* val,
quadbyte)
{
float fval = DICOMFile::ReturnAsFloat(val,
parser->GetDICOMFile()->GetPlatformIsBigEndian ());
this->GantryAngle = fval;
}
#ifdef _MSC_VER
#pragma warning ( pop )
#endif