/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* .
**/
#include "../PrecompiledHeaders.h"
#include "DicomMap.h"
#include
#include
#include
#include "../Compatibility.h"
#include "../Endianness.h"
#include "../OrthancException.h"
#include "../Toolbox.h"
#include "DicomArray.h"
#include "DicomImageInformation.h"
#if ORTHANC_ENABLE_DCMTK == 1
#include "../DicomParsing/FromDcmtkBridge.h"
#endif
#if !defined(__EMSCRIPTEN__)
// Multithreading is not supported in WebAssembly
# include
# include // For boost::unique_lock<> and boost::shared_lock<>
#endif
namespace Orthanc
{
// WARNING: the DEFAULT list of main dicom tags below are the list as they
// were in Orthanc 1.10 before we introduced the dynamic main dicom tags.
// This list has not changed since Orthanc 1.4.2 and had a single change since
// Orthanc 0.9.5.
// These lists have a specific signature. When a resource does not have
// the metadata "MainDicomTagsSignature", we'll assume that they were stored
// with an Orthanc prior to 1.11. It is therefore very important that you never
// change these lists ! Update ResetDefaultMainDicomTags instead.
static const DicomTag DEFAULT_1_11_PATIENT_MAIN_DICOM_TAGS[] =
{
// { DicomTag(0x0010, 0x1010), "PatientAge" },
// { DicomTag(0x0010, 0x1040), "PatientAddress" },
DICOM_TAG_PATIENT_NAME,
DICOM_TAG_PATIENT_BIRTH_DATE,
DICOM_TAG_PATIENT_SEX,
DICOM_TAG_OTHER_PATIENT_IDS,
DICOM_TAG_PATIENT_ID
// don't add tags here, check ResetDefaultMainDicomTags instead
};
static const DicomTag DEFAULT_1_11_STUDY_MAIN_DICOM_TAGS[] =
{
// { DicomTag(0x0010, 0x1020), "PatientSize" },
// { DicomTag(0x0010, 0x1030), "PatientWeight" },
DICOM_TAG_STUDY_DATE,
DICOM_TAG_STUDY_TIME,
DICOM_TAG_STUDY_ID,
DICOM_TAG_STUDY_DESCRIPTION,
DICOM_TAG_ACCESSION_NUMBER,
DICOM_TAG_STUDY_INSTANCE_UID,
// New in db v6 (Orthanc 0.9.5)
DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION,
DICOM_TAG_INSTITUTION_NAME,
DICOM_TAG_REQUESTING_PHYSICIAN,
DICOM_TAG_REFERRING_PHYSICIAN_NAME
// don't add tags here, check ResetDefaultMainDicomTags instead
};
static const DicomTag DEFAULT_1_11_SERIES_MAIN_DICOM_TAGS[] =
{
// { DicomTag(0x0010, 0x1080), "MilitaryRank" },
DICOM_TAG_SERIES_DATE,
DICOM_TAG_SERIES_TIME,
DICOM_TAG_MODALITY,
DICOM_TAG_MANUFACTURER,
DICOM_TAG_STATION_NAME,
DICOM_TAG_SERIES_DESCRIPTION,
DICOM_TAG_BODY_PART_EXAMINED,
DICOM_TAG_SEQUENCE_NAME,
DICOM_TAG_PROTOCOL_NAME,
DICOM_TAG_SERIES_NUMBER,
DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES,
DICOM_TAG_IMAGES_IN_ACQUISITION,
DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS,
DICOM_TAG_NUMBER_OF_SLICES,
DICOM_TAG_NUMBER_OF_TIME_SLICES,
DICOM_TAG_SERIES_INSTANCE_UID,
// New in db v6 (Orthanc 0.9.5)
DICOM_TAG_IMAGE_ORIENTATION_PATIENT,
DICOM_TAG_SERIES_TYPE,
DICOM_TAG_OPERATOR_NAME,
DICOM_TAG_PERFORMED_PROCEDURE_STEP_DESCRIPTION,
DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION,
DICOM_TAG_CONTRAST_BOLUS_AGENT
// don't add tags here, check ResetDefaultMainDicomTags instead
};
static const DicomTag DEFAULT_1_11_INSTANCE_MAIN_DICOM_TAGS[] =
{
DICOM_TAG_INSTANCE_CREATION_DATE,
DICOM_TAG_INSTANCE_CREATION_TIME,
DICOM_TAG_ACQUISITION_NUMBER,
DICOM_TAG_IMAGE_INDEX,
DICOM_TAG_INSTANCE_NUMBER,
DICOM_TAG_NUMBER_OF_FRAMES,
DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER,
DICOM_TAG_SOP_INSTANCE_UID,
// New in db v6 (Orthanc 0.9.5)
DICOM_TAG_IMAGE_POSITION_PATIENT,
DICOM_TAG_IMAGE_COMMENTS,
/**
* Main DICOM tags that are not part of any release of the
* database schema yet, and that will be part of future db v7. In
* the meantime, the user must call "/tools/reconstruct" once to
* access these tags if the corresponding DICOM files where
* indexed in the database by an older version of Orthanc.
**/
DICOM_TAG_IMAGE_ORIENTATION_PATIENT // New in Orthanc 1.4.2
// don't add tags here, check ResetDefaultMainDicomTags instead
};
class DicomMap::MainDicomTagsConfiguration : public boost::noncopyable
{
private:
#if !defined(__EMSCRIPTEN__)
typedef boost::unique_lock WriterLock;
typedef boost::shared_lock ReaderLock;
boost::shared_mutex mutex_;
#endif
std::set patientsMainDicomTagsByLevel_;
std::set studiesMainDicomTagsByLevel_;
std::set seriesMainDicomTagsByLevel_;
std::set instancesMainDicomTagsByLevel_;
std::set allMainDicomTags_;
std::map signatures_;
std::map defaultSignatures_;
MainDicomTagsConfiguration()
{
ResetDefaultMainDicomTags();
}
std::string ComputeSignature(const std::set& tags)
{
// std::set are sorted by default (which is important for us !)
std::set tagsIds;
for (std::set::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
tagsIds.insert(it->Format());
}
std::string signatureText = boost::algorithm::join(tagsIds, ";");
return signatureText;
}
void LoadDefaultMainDicomTags(ResourceType level)
{
const DicomTag* tags = NULL;
size_t size;
switch (level)
{
case ResourceType_Patient:
tags = DEFAULT_1_11_PATIENT_MAIN_DICOM_TAGS;
size = sizeof(DEFAULT_1_11_PATIENT_MAIN_DICOM_TAGS) / sizeof(DicomTag);
break;
case ResourceType_Study:
tags = DEFAULT_1_11_STUDY_MAIN_DICOM_TAGS;
size = sizeof(DEFAULT_1_11_STUDY_MAIN_DICOM_TAGS) / sizeof(DicomTag);
break;
case ResourceType_Series:
tags = DEFAULT_1_11_SERIES_MAIN_DICOM_TAGS;
size = sizeof(DEFAULT_1_11_SERIES_MAIN_DICOM_TAGS) / sizeof(DicomTag);
break;
case ResourceType_Instance:
tags = DEFAULT_1_11_INSTANCE_MAIN_DICOM_TAGS;
size = sizeof(DEFAULT_1_11_INSTANCE_MAIN_DICOM_TAGS) / sizeof(DicomTag);
break;
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
assert(tags != NULL &&
size != 0);
for (size_t i = 0; i < size; i++)
{
AddMainDicomTagInternal(tags[i], level);
}
}
std::set& GetMainDicomTagsByLevelInternal(ResourceType level)
{
switch (level)
{
case ResourceType_Patient:
return patientsMainDicomTagsByLevel_;
case ResourceType_Study:
return studiesMainDicomTagsByLevel_;
case ResourceType_Series:
return seriesMainDicomTagsByLevel_;
case ResourceType_Instance:
return instancesMainDicomTagsByLevel_;
default:
throw OrthancException(ErrorCode_InternalError);
}
}
void AddMainDicomTagInternal(const DicomTag& tag,
ResourceType level)
{
std::set& existingLevelTags = GetMainDicomTagsByLevelInternal(level);
if (existingLevelTags.find(tag) != existingLevelTags.end())
{
throw OrthancException(ErrorCode_MainDicomTagsMultiplyDefined, tag.Format() + " is already defined", false);
}
existingLevelTags.insert(tag);
allMainDicomTags_.insert(tag);
signatures_[level] = ComputeSignature(GetMainDicomTagsByLevelInternal(level));
}
public:
// Singleton pattern
static MainDicomTagsConfiguration& GetInstance()
{
static MainDicomTagsConfiguration parameters;
return parameters;
}
void ResetDefaultMainDicomTags()
{
#if !defined(__EMSCRIPTEN__)
WriterLock lock(mutex_);
#endif
patientsMainDicomTagsByLevel_.clear();
studiesMainDicomTagsByLevel_.clear();
seriesMainDicomTagsByLevel_.clear();
instancesMainDicomTagsByLevel_.clear();
allMainDicomTags_.clear();
// by default, initialize with the previous static list (up to 1.10.0)
LoadDefaultMainDicomTags(ResourceType_Patient);
LoadDefaultMainDicomTags(ResourceType_Study);
LoadDefaultMainDicomTags(ResourceType_Series);
LoadDefaultMainDicomTags(ResourceType_Instance);
defaultSignatures_[ResourceType_Patient] = signatures_[ResourceType_Patient];
defaultSignatures_[ResourceType_Study] = signatures_[ResourceType_Study];
defaultSignatures_[ResourceType_Series] = signatures_[ResourceType_Series];
defaultSignatures_[ResourceType_Instance] = signatures_[ResourceType_Instance];
// only add new tags here !
// introduced in v 1.12.5
AddMainDicomTagInternal(DICOM_TAG_TIMEZONE_OFFSET_FROM_UTC, ResourceType_Study); // used in default QIDO-RS queries
AddMainDicomTagInternal(DICOM_TAG_TIMEZONE_OFFSET_FROM_UTC, ResourceType_Series); // used in default QIDO-RS queries
AddMainDicomTagInternal(DICOM_TAG_PERFORMED_PROCEDURE_STEP_START_DATE, ResourceType_Series); // used in default QIDO-RS queries
AddMainDicomTagInternal(DICOM_TAG_PERFORMED_PROCEDURE_STEP_START_TIME, ResourceType_Series); // used in default QIDO-RS queries
AddMainDicomTagInternal(DICOM_TAG_REQUEST_ATTRIBUTES_SEQUENCE, ResourceType_Series); // used in default QIDO-RS queries
// TODO-FIND: remove it from metadata when adding it ! AddMainDicomTagInternal(DICOM_TAG_SOP_CLASS_UID, ResourceType_Instance); // previously saved in a metadata; makes more sense to store it in a DICOM tag
}
void AddMainDicomTag(const DicomTag& tag,
ResourceType level)
{
#if !defined(__EMSCRIPTEN__)
WriterLock lock(mutex_);
#endif
AddMainDicomTagInternal(tag, level);
}
void GetAllMainDicomTags(std::set& target)
{
#if !defined(__EMSCRIPTEN__)
ReaderLock lock(mutex_);
#endif
target = allMainDicomTags_;
}
void GetMainDicomTagsByLevel(std::set& target,
ResourceType level)
{
#if !defined(__EMSCRIPTEN__)
ReaderLock lock(mutex_);
#endif
target = GetMainDicomTagsByLevelInternal(level);
}
std::string GetMainDicomTagsSignature(ResourceType level)
{
#if !defined(__EMSCRIPTEN__)
ReaderLock lock(mutex_);
#endif
assert(signatures_.find(level) != signatures_.end());
return signatures_[level];
}
std::string GetDefaultMainDicomTagsSignatureFrom1_11(ResourceType level)
{
#if !defined(__EMSCRIPTEN__)
ReaderLock lock(mutex_);
#endif
assert(defaultSignatures_.find(level) != defaultSignatures_.end());
return defaultSignatures_[level];
}
bool IsMainDicomTag(const DicomTag& tag)
{
#if !defined(__EMSCRIPTEN__)
ReaderLock lock(mutex_);
#endif
return allMainDicomTags_.find(tag) != allMainDicomTags_.end();
}
bool IsMainDicomTag(const DicomTag& tag,
ResourceType level)
{
#if !defined(__EMSCRIPTEN__)
ReaderLock lock(mutex_);
#endif
const std::set& mainDicomTags = GetMainDicomTagsByLevelInternal(level);
return mainDicomTags.find(tag) != mainDicomTags.end();
}
};
void DicomMap::SetValueInternal(uint16_t group,
uint16_t element,
DicomValue* value)
{
DicomTag tag(group, element);
Content::iterator it = content_.find(tag);
if (it != content_.end())
{
delete it->second;
it->second = value;
}
else
{
content_.insert(std::make_pair(tag, value));
}
}
void DicomMap::Clear()
{
for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(it->second != NULL);
delete it->second;
}
content_.clear();
}
void DicomMap::SetNullValue(uint16_t group, uint16_t element)
{
SetValueInternal(group, element, new DicomValue);
}
void DicomMap::SetNullValue(const DicomTag &tag)
{
SetValueInternal(tag.GetGroup(), tag.GetElement(), new DicomValue);
}
void DicomMap::SetValue(uint16_t group, uint16_t element, const DicomValue &value)
{
SetValueInternal(group, element, value.Clone());
}
void DicomMap::SetValue(const DicomTag &tag, const DicomValue &value)
{
SetValueInternal(tag.GetGroup(), tag.GetElement(), value.Clone());
}
void DicomMap::SetValue(const DicomTag &tag, const std::string &str, bool isBinary)
{
SetValueInternal(tag.GetGroup(), tag.GetElement(), new DicomValue(str, isBinary));
}
void DicomMap::SetValue(uint16_t group, uint16_t element, const std::string &str, bool isBinary)
{
SetValueInternal(group, element, new DicomValue(str, isBinary));
}
void DicomMap::SetSequenceValue(const DicomTag& tag, const Json::Value& value)
{
SetValueInternal(tag.GetGroup(), tag.GetElement(), new DicomValue(value));
}
bool DicomMap::HasTag(uint16_t group, uint16_t element) const
{
return HasTag(DicomTag(group, element));
}
bool DicomMap::HasTag(const DicomTag &tag) const
{
return content_.find(tag) != content_.end();
}
const DicomValue &DicomMap::GetValue(uint16_t group, uint16_t element) const
{
return GetValue(DicomTag(group, element));
}
static void ExtractTagsInternal(DicomMap& result,
const DicomMap::Content& source,
const std::set& mainDicomTags)
{
result.Clear();
for (std::set::const_iterator itmt = mainDicomTags.begin();
itmt != mainDicomTags.end(); ++itmt)
{
DicomMap::Content::const_iterator it = source.find(*itmt);
if (it != source.end())
{
result.SetValue(it->first, *it->second /* value will be cloned */);
}
}
}
void DicomMap::ExtractTags(DicomMap& result, const std::set& tags) const
{
result.Clear();
for (std::set::const_iterator itmt = tags.begin();
itmt != tags.end(); ++itmt)
{
DicomMap::Content::const_iterator it = content_.find(*itmt);
if (it != content_.end())
{
result.SetValue(it->first, *it->second /* value will be cloned */);
}
}
}
void DicomMap::ExtractResourceInformation(DicomMap& result, ResourceType level) const
{
std::set mainDicomTags;
DicomMap::MainDicomTagsConfiguration::GetInstance().GetMainDicomTagsByLevel(mainDicomTags, level);
ExtractTagsInternal(result, content_, mainDicomTags);
}
void DicomMap::ExtractPatientInformation(DicomMap& result) const
{
ExtractResourceInformation(result, ResourceType_Patient);
}
void DicomMap::ExtractStudyInformation(DicomMap& result) const
{
ExtractResourceInformation(result, ResourceType_Study);
}
void DicomMap::ExtractSeriesInformation(DicomMap& result) const
{
ExtractResourceInformation(result, ResourceType_Series);
}
void DicomMap::ExtractInstanceInformation(DicomMap& result) const
{
ExtractResourceInformation(result, ResourceType_Instance);
}
DicomMap::~DicomMap()
{
Clear();
}
size_t DicomMap::GetSize() const
{
return content_.size();
}
DicomMap* DicomMap::Clone() const
{
std::unique_ptr result(new DicomMap);
for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
{
result->content_.insert(std::make_pair(it->first, it->second->Clone()));
}
return result.release();
}
void DicomMap::Assign(const DicomMap& other)
{
Clear();
for (Content::const_iterator it = other.content_.begin(); it != other.content_.end(); ++it)
{
content_.insert(std::make_pair(it->first, it->second->Clone()));
}
}
const DicomValue& DicomMap::GetValue(const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value)
{
return *value;
}
else
{
throw OrthancException(ErrorCode_InexistentTag);
}
}
const DicomValue *DicomMap::TestAndGetValue(uint16_t group, uint16_t element) const
{
return TestAndGetValue(DicomTag(group, element));
}
const DicomValue* DicomMap::TestAndGetValue(const DicomTag& tag) const
{
Content::const_iterator it = content_.find(tag);
if (it == content_.end())
{
return NULL;
}
else
{
return it->second;
}
}
void DicomMap::Remove(const DicomTag& tag)
{
Content::iterator it = content_.find(tag);
if (it != content_.end())
{
delete it->second;
content_.erase(it);
}
}
void DicomMap::RemoveTags(const std::set& tags)
{
for (std::set::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
Remove(*it);
}
}
void DicomMap::SetupFindPatientTemplate(DicomMap& result)
{
result.Clear();
// Identifying tags
result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
// Other tags in the "Patient" module
result.SetValue(DICOM_TAG_OTHER_PATIENT_IDS, "", false);
result.SetValue(DICOM_TAG_PATIENT_BIRTH_DATE, "", false);
result.SetValue(DICOM_TAG_PATIENT_NAME, "", false);
result.SetValue(DICOM_TAG_PATIENT_SEX, "", false);
}
void DicomMap::SetupFindStudyTemplate(DicomMap& result)
{
result.Clear();
// Identifying tags
result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
// Other tags in the "General Study" module
result.SetValue(DICOM_TAG_REFERRING_PHYSICIAN_NAME, "", false);
result.SetValue(DICOM_TAG_STUDY_DATE, "", false);
result.SetValue(DICOM_TAG_STUDY_DESCRIPTION, "", false);
result.SetValue(DICOM_TAG_STUDY_ID, "", false);
result.SetValue(DICOM_TAG_STUDY_TIME, "", false);
}
void DicomMap::SetupFindSeriesTemplate(DicomMap& result)
{
result.Clear();
// Identifying tags
result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false);
// Other tags in the "General Series" module
result.SetValue(DICOM_TAG_BODY_PART_EXAMINED, "", false);
result.SetValue(DICOM_TAG_MODALITY, "", false);
result.SetValue(DICOM_TAG_OPERATOR_NAME, "", false);
result.SetValue(DICOM_TAG_PERFORMED_PROCEDURE_STEP_DESCRIPTION, "", false);
result.SetValue(DICOM_TAG_PROTOCOL_NAME, "", false);
result.SetValue(DICOM_TAG_SERIES_DATE, "", false);
result.SetValue(DICOM_TAG_SERIES_DESCRIPTION, "", false);
result.SetValue(DICOM_TAG_SERIES_NUMBER, "", false);
result.SetValue(DICOM_TAG_SERIES_TIME, "", false);
}
void DicomMap::SetupFindInstanceTemplate(DicomMap& result)
{
result.Clear();
// Identifying tags
result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false);
result.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "", false);
// Other tags in the "SOP Common" module
result.SetValue(DICOM_TAG_ACQUISITION_NUMBER, "", false);
result.SetValue(DICOM_TAG_IMAGE_COMMENTS, "", false);
result.SetValue(DICOM_TAG_IMAGE_INDEX, "", false);
result.SetValue(DICOM_TAG_IMAGE_ORIENTATION_PATIENT, "", false);
result.SetValue(DICOM_TAG_IMAGE_POSITION_PATIENT, "", false);
result.SetValue(DICOM_TAG_INSTANCE_CREATION_DATE, "", false);
result.SetValue(DICOM_TAG_INSTANCE_CREATION_TIME, "", false);
result.SetValue(DICOM_TAG_INSTANCE_NUMBER, "", false);
result.SetValue(DICOM_TAG_NUMBER_OF_FRAMES, "", false);
result.SetValue(DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER, "", false);
}
bool DicomMap::CopyTagIfExists(const DicomMap& source,
const DicomTag& tag)
{
if (source.HasTag(tag))
{
SetValue(tag, source.GetValue(tag));
return true;
}
return false;
}
bool DicomMap::IsMainDicomTag(const DicomTag& tag, ResourceType level)
{
return DicomMap::MainDicomTagsConfiguration::GetInstance().IsMainDicomTag(tag, level);
}
bool DicomMap::IsMainDicomTag(const DicomTag& tag)
{
return (IsMainDicomTag(tag, ResourceType_Patient) ||
IsMainDicomTag(tag, ResourceType_Study) ||
IsMainDicomTag(tag, ResourceType_Series) ||
IsMainDicomTag(tag, ResourceType_Instance));
}
static bool IsGenericComputedTag(const DicomTag& tag)
{
return tag == DICOM_TAG_RETRIEVE_URL ||
tag == DICOM_TAG_RETRIEVE_AE_TITLE;
}
bool DicomMap::IsComputedTag(const DicomTag& tag)
{
return (IsComputedTag(tag, ResourceType_Patient) ||
IsComputedTag(tag, ResourceType_Study) ||
IsComputedTag(tag, ResourceType_Series) ||
IsComputedTag(tag, ResourceType_Instance) ||
IsGenericComputedTag(tag));
}
bool DicomMap::IsComputedTag(const DicomTag& tag, ResourceType level)
{
switch (level)
{
case ResourceType_Patient:
return (
tag == DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES ||
tag == DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES ||
tag == DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES
);
case ResourceType_Study:
return (
tag == DICOM_TAG_MODALITIES_IN_STUDY ||
tag == DICOM_TAG_SOP_CLASSES_IN_STUDY ||
tag == DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES ||
tag == DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES
);
case ResourceType_Series:
return (
tag == DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES
);
case ResourceType_Instance:
return (
tag == DICOM_TAG_INSTANCE_AVAILABILITY
);
default:
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
}
bool DicomMap::HasOnlyComputedTags(const std::set& tags)
{
if (tags.size() == 0)
{
return false;
}
for (std::set::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
if (!IsComputedTag(*it))
{
return false;
}
}
return true;
}
bool DicomMap::HasComputedTags(const std::set& tags)
{
for (std::set::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
if (IsComputedTag(*it))
{
return true;
}
}
return false;
}
bool DicomMap::HasComputedTags(const std::set& tags, ResourceType level)
{
for (std::set::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
if (IsComputedTag(*it, level))
{
return true;
}
}
return false;
}
bool DicomMap::HasMetaInformationTags(const std::set& tags)
{
for (std::set::const_iterator it = tags.begin(); it != tags.end(); ++it)
{
if (it->GetGroup() == 0x0002)
{
return true;
}
}
return false;
}
void DicomMap::GetMainDicomTags(std::set& target,
ResourceType level)
{
DicomMap::MainDicomTagsConfiguration::GetInstance().GetMainDicomTagsByLevel(target, level);
}
void DicomMap::GetAllMainDicomTags(std::set& target)
{
DicomMap::MainDicomTagsConfiguration::GetInstance().GetAllMainDicomTags(target);
}
void DicomMap::AddMainDicomTag(const DicomTag& tag, ResourceType level)
{
DicomMap::MainDicomTagsConfiguration::GetInstance().AddMainDicomTag(tag, level);
}
void DicomMap::ResetDefaultMainDicomTags()
{
DicomMap::MainDicomTagsConfiguration::GetInstance().ResetDefaultMainDicomTags();
}
std::string DicomMap::GetMainDicomTagsSignature(ResourceType level)
{
return DicomMap::MainDicomTagsConfiguration::GetInstance().GetMainDicomTagsSignature(level);
}
std::string DicomMap::GetDefaultMainDicomTagsSignatureFrom1_11(ResourceType level)
{
return DicomMap::MainDicomTagsConfiguration::GetInstance().GetDefaultMainDicomTagsSignatureFrom1_11(level);
}
void DicomMap::GetTags(std::set& tags) const
{
tags.clear();
for (Content::const_iterator it = content_.begin();
it != content_.end(); ++it)
{
tags.insert(it->first);
}
}
static uint16_t ReadLittleEndianUint16(const char* dicom)
{
const uint8_t* p = reinterpret_cast(dicom);
return (static_cast(p[0]) |
(static_cast(p[1]) << 8));
}
static uint32_t ReadLittleEndianUint32(const char* dicom)
{
const uint8_t* p = reinterpret_cast(dicom);
return (static_cast(p[0]) |
(static_cast(p[1]) << 8) |
(static_cast(p[2]) << 16) |
(static_cast(p[3]) << 24));
}
static bool ValidateTag(const ValueRepresentation& vr,
const std::string& value)
{
switch (vr)
{
case ValueRepresentation_ApplicationEntity:
return value.size() <= 16;
case ValueRepresentation_AgeString:
return (value.size() == 4 &&
isdigit(value[0]) &&
isdigit(value[1]) &&
isdigit(value[2]) &&
(value[3] == 'D' || value[3] == 'W' || value[3] == 'M' || value[3] == 'Y'));
case ValueRepresentation_AttributeTag:
return value.size() == 4;
case ValueRepresentation_CodeString:
return value.size() <= 16;
case ValueRepresentation_Date:
return value.size() <= 18;
case ValueRepresentation_DecimalString:
return value.size() <= 16;
case ValueRepresentation_DateTime:
return value.size() <= 54;
case ValueRepresentation_FloatingPointSingle:
return value.size() == 4;
case ValueRepresentation_FloatingPointDouble:
return value.size() == 8;
case ValueRepresentation_IntegerString:
return value.size() <= 12;
case ValueRepresentation_LongString:
return value.size() <= 64;
case ValueRepresentation_LongText:
return value.size() <= 10240;
case ValueRepresentation_OtherByte:
return true;
case ValueRepresentation_OtherDouble:
return value.size() <= (static_cast(1) << 32) - 8;
case ValueRepresentation_OtherFloat:
return value.size() <= (static_cast(1) << 32) - 4;
case ValueRepresentation_OtherLong:
return true;
case ValueRepresentation_OtherWord:
return true;
case ValueRepresentation_PersonName:
return true;
case ValueRepresentation_ShortString:
return value.size() <= 16;
case ValueRepresentation_SignedLong:
return value.size() == 4;
case ValueRepresentation_Sequence:
return true;
case ValueRepresentation_SignedShort:
return value.size() == 2;
case ValueRepresentation_ShortText:
return value.size() <= 1024;
case ValueRepresentation_Time:
return value.size() <= 28;
case ValueRepresentation_UnlimitedCharacters:
return value.size() <= (static_cast(1) << 32) - 2;
case ValueRepresentation_UniqueIdentifier:
return value.size() <= 64;
case ValueRepresentation_UnsignedLong:
return value.size() == 4;
case ValueRepresentation_Unknown:
return true;
case ValueRepresentation_UniversalResource:
return value.size() <= (static_cast(1) << 32) - 2;
case ValueRepresentation_UnsignedShort:
return value.size() == 2;
case ValueRepresentation_UnlimitedText:
return value.size() <= (static_cast(1) << 32) - 2;
default:
// Assume unsupported tags are OK
return true;
}
}
static void RemoveTagPadding(std::string& value,
const ValueRepresentation& vr)
{
/**
* Remove padding from character strings, if need be. For the time
* being, only the UI VR is supported.
* http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
**/
switch (vr)
{
case ValueRepresentation_UniqueIdentifier:
{
/**
* "Values with a VR of UI shall be padded with a single
* trailing NULL (00H) character when necessary to achieve even
* length."
**/
if (!value.empty() &&
value[value.size() - 1] == '\0')
{
value.resize(value.size() - 1);
}
break;
}
/**
* TODO implement other VR
**/
default:
// No padding is applicable to this VR
break;
}
}
static bool ReadNextTag(DicomTag& tag,
ValueRepresentation& vr,
std::string& value,
const char* dicom,
size_t size,
size_t& position)
{
/**
* http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2
* This function reads a data element with Explicit VR encoded using Little-Endian.
**/
if (position + 6 > size)
{
return false;
}
tag = DicomTag(ReadLittleEndianUint16(dicom + position),
ReadLittleEndianUint16(dicom + position + 2));
vr = StringToValueRepresentation(std::string(dicom + position + 4, 2), true);
if (vr == ValueRepresentation_NotSupported)
{
return false;
}
// http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2
if (vr == ValueRepresentation_ApplicationEntity /* AE */ ||
vr == ValueRepresentation_AgeString /* AS */ ||
vr == ValueRepresentation_AttributeTag /* AT */ ||
vr == ValueRepresentation_CodeString /* CS */ ||
vr == ValueRepresentation_Date /* DA */ ||
vr == ValueRepresentation_DecimalString /* DS */ ||
vr == ValueRepresentation_DateTime /* DT */ ||
vr == ValueRepresentation_FloatingPointSingle /* FL */ ||
vr == ValueRepresentation_FloatingPointDouble /* FD */ ||
vr == ValueRepresentation_IntegerString /* IS */ ||
vr == ValueRepresentation_LongString /* LO */ ||
vr == ValueRepresentation_LongText /* LT */ ||
vr == ValueRepresentation_PersonName /* PN */ ||
vr == ValueRepresentation_ShortString /* SH */ ||
vr == ValueRepresentation_SignedLong /* SL */ ||
vr == ValueRepresentation_SignedShort /* SS */ ||
vr == ValueRepresentation_ShortText /* ST */ ||
vr == ValueRepresentation_Time /* TM */ ||
vr == ValueRepresentation_UniqueIdentifier /* UI */ ||
vr == ValueRepresentation_UnsignedLong /* UL */ ||
vr == ValueRepresentation_UnsignedShort /* US */)
{
/**
* This is Table 7.1-2. "Data Element with Explicit VR of AE,
* AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, LT, PN, SH, SL, SS,
* ST, TM, UI, UL and US"
**/
if (position + 8 > size)
{
return false;
}
uint16_t length = ReadLittleEndianUint16(dicom + position + 6);
if (position + 8 + length > size)
{
return false;
}
value.assign(dicom + position + 8, length);
position += (8 + length);
}
else
{
/**
* This is Table 7.1-1. "Data Element with Explicit VR other
* than as shown in Table 7.1-2"
**/
if (position + 12 > size)
{
return false;
}
uint16_t reserved = ReadLittleEndianUint16(dicom + position + 6);
if (reserved != 0)
{
return false;
}
uint32_t length = ReadLittleEndianUint32(dicom + position + 8);
if (position + 12 + length > size)
{
return false;
}
value.assign(dicom + position + 12, length);
position += (12 + length);
}
if (!ValidateTag(vr, value))
{
return false;
}
RemoveTagPadding(value, vr);
return true;
}
bool DicomMap::IsDicomFile(const void* dicom,
size_t size)
{
/**
* http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html
* According to Table 7.1-1, besides the "DICM" DICOM prefix, the
* file preamble (i.e. dicom[0..127]) should not be taken into
* account to determine whether the file is or is not a DICOM file.
**/
const uint8_t* p = reinterpret_cast(dicom);
return (size >= 132 &&
p[128] == 'D' &&
p[129] == 'I' &&
p[130] == 'C' &&
p[131] == 'M');
}
bool DicomMap::ParseDicomMetaInformation(DicomMap& result,
const void* dicom,
size_t size)
{
if (!IsDicomFile(dicom, size))
{
return false;
}
/**
* The DICOM File Meta Information must be encoded using the
* Explicit VR Little Endian Transfer Syntax
* (UID=1.2.840.10008.1.2.1).
**/
result.Clear();
// First, we read the "File Meta Information Group Length" tag
// (0002,0000) to know where to stop reading the meta header
size_t position = 132;
DicomTag tag(0x0000, 0x0000); // Dummy initialization
ValueRepresentation vr;
std::string value;
if (!ReadNextTag(tag, vr, value, reinterpret_cast(dicom), size, position) ||
tag.GetGroup() != 0x0002 ||
tag.GetElement() != 0x0000 ||
vr != ValueRepresentation_UnsignedLong ||
value.size() != 4)
{
return false;
}
size_t stopPosition = position + ReadLittleEndianUint32(value.c_str());
if (stopPosition > size)
{
return false;
}
while (position < stopPosition)
{
if (ReadNextTag(tag, vr, value, reinterpret_cast(dicom), size, position))
{
result.SetValue(tag, value, IsBinaryValueRepresentation(vr));
}
else
{
return false;
}
}
return true;
}
static std::string ValueAsString(const DicomMap& summary,
const DicomTag& tag)
{
const DicomValue& value = summary.GetValue(tag);
if (value.IsNull())
{
return "(null)";
}
else
{
return value.GetContent();
}
}
std::string DicomMap::FormatMissingTagsForStore() const
{
std::string patientId, studyInstanceUid, seriesInstanceUid, sopInstanceUid;
if (HasTag(DICOM_TAG_PATIENT_ID))
{
patientId = ValueAsString(*this, DICOM_TAG_PATIENT_ID);
}
if (HasTag(DICOM_TAG_STUDY_INSTANCE_UID))
{
studyInstanceUid = ValueAsString(*this, DICOM_TAG_STUDY_INSTANCE_UID);
}
if (HasTag(DICOM_TAG_SERIES_INSTANCE_UID))
{
seriesInstanceUid = ValueAsString(*this, DICOM_TAG_SERIES_INSTANCE_UID);
}
if (HasTag(DICOM_TAG_SOP_INSTANCE_UID))
{
sopInstanceUid = ValueAsString(*this, DICOM_TAG_SOP_INSTANCE_UID);
}
return FormatMissingTagsForStore(patientId, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
}
std::string DicomMap::FormatMissingTagsForStore(const std::string& patientId,
const std::string& studyInstanceUid,
const std::string& seriesInstanceUid,
const std::string& sopInstanceUid)
{
std::string s, t;
if (!patientId.empty())
{
if (t.size() > 0)
t += ", ";
t += "PatientID=" + patientId;
}
else
{
if (s.size() > 0)
s += ", ";
s += "PatientID";
}
if (!studyInstanceUid.empty())
{
if (t.size() > 0)
t += ", ";
t += "StudyInstanceUID=" + studyInstanceUid;
}
else
{
if (s.size() > 0)
s += ", ";
s += "StudyInstanceUID";
}
if (!seriesInstanceUid.empty())
{
if (t.size() > 0)
t += ", ";
t += "SeriesInstanceUID=" + seriesInstanceUid;
}
else
{
if (s.size() > 0)
s += ", ";
s += "SeriesInstanceUID";
}
if (!sopInstanceUid.empty())
{
if (t.size() > 0)
t += ", ";
t += "SOPInstanceUID=" + sopInstanceUid;
}
else
{
if (s.size() > 0)
s += ", ";
s += "SOPInstanceUID";
}
if (t.size() == 0)
{
return "Store has failed because all the required tags (" + s + ") are missing (is it a DICOMDIR file?)";
}
else
{
return "Store has failed because required tags (" + s + ") are missing for the following instance: " + t;
}
}
bool DicomMap::LookupStringValue(std::string& result,
const DicomTag& tag,
bool allowBinary) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->CopyToString(result, allowBinary);
}
}
bool DicomMap::LookupStringValues(std::set& results,
const DicomTag& tag,
bool allowBinary) const
{
std::string tmp;
if (LookupStringValue(tmp, tag, allowBinary))
{
Toolbox::SplitString(results, tmp, '\\');
return true;
}
return false;
}
bool DicomMap::ParseInteger32(int32_t& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseInteger32(result);
}
}
bool DicomMap::ParseInteger64(int64_t& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseInteger64(result);
}
}
bool DicomMap::ParseUnsignedInteger32(uint32_t& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseUnsignedInteger32(result);
}
}
bool DicomMap::ParseUnsignedInteger64(uint64_t& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseUnsignedInteger64(result);
}
}
bool DicomMap::ParseFloat(float& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseFloat(result);
}
}
bool DicomMap::ParseFirstFloat(float& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseFirstFloat(result);
}
}
bool DicomMap::ParseDouble(double& result,
const DicomTag& tag) const
{
const DicomValue* value = TestAndGetValue(tag);
if (value == NULL)
{
return false;
}
else
{
return value->ParseDouble(result);
}
}
void DicomMap::FromDicomAsJson(const Json::Value& dicomAsJson, bool append, bool parseSequences)
{
if (dicomAsJson.type() != Json::objectValue)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
if (!append)
{
Clear();
}
Json::Value::Members tags = dicomAsJson.getMemberNames();
for (Json::Value::Members::const_iterator
it = tags.begin(); it != tags.end(); ++it)
{
DicomTag tag(0, 0);
if (!DicomTag::ParseHexadecimal(tag, it->c_str()))
{
throw OrthancException(ErrorCode_CorruptedFile);
}
const Json::Value& value = dicomAsJson[*it];
if (value.type() != Json::objectValue ||
!value.isMember("Type") ||
!value.isMember("Value") ||
value["Type"].type() != Json::stringValue)
{
throw OrthancException(ErrorCode_CorruptedFile);
}
if (value["Type"] == "String")
{
if (value["Value"].type() != Json::stringValue)
{
throw OrthancException(ErrorCode_CorruptedFile);
}
else
{
SetValue(tag, value["Value"].asString(), false /* not binary */);
}
}
else if (value["Type"] == "Sequence" && parseSequences)
{
if (value["Value"].type() != Json::arrayValue)
{
throw OrthancException(ErrorCode_CorruptedFile);
}
else
{
SetSequenceValue(tag, value["Value"]);
}
}
}
}
void DicomMap::Merge(const DicomMap& other)
{
for (Content::const_iterator it = other.content_.begin();
it != other.content_.end(); ++it)
{
assert(it->second != NULL);
if (content_.find(it->first) == content_.end())
{
content_[it->first] = it->second->Clone();
}
}
}
void DicomMap::MergeMainDicomTags(const DicomMap& other,
ResourceType level)
{
std::set mainDicomTags;
DicomMap::MainDicomTagsConfiguration::GetInstance().GetMainDicomTagsByLevel(mainDicomTags, level);
for (std::set::const_iterator itmt = mainDicomTags.begin();
itmt != mainDicomTags.end(); ++itmt)
{
Content::const_iterator found = other.content_.find(*itmt);
if (found != other.content_.end() &&
content_.find(*itmt) == content_.end())
{
assert(found->second != NULL);
content_[*itmt] = found->second->Clone();
}
}
}
void DicomMap::ExtractMainDicomTags(const DicomMap& other)
{
Clear();
MergeMainDicomTags(other, ResourceType_Patient);
MergeMainDicomTags(other, ResourceType_Study);
MergeMainDicomTags(other, ResourceType_Series);
MergeMainDicomTags(other, ResourceType_Instance);
}
bool DicomMap::HasOnlyMainDicomTags() const
{
for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
{
if (!DicomMap::MainDicomTagsConfiguration::GetInstance().IsMainDicomTag(it->first))
{
return false;
}
}
return true;
}
void DicomMap::ExtractSequences(DicomMap& result) const
{
result.Clear();
for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
{
if (it->second->IsSequence())
{
result.SetSequenceValue(it->first, it->second->GetSequenceContent());
}
}
}
void DicomMap::Serialize(Json::Value& target) const
{
target = Json::objectValue;
for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(it->second != NULL);
std::string tag = it->first.Format();
Json::Value value;
it->second->Serialize(value);
target[tag] = value;
}
}
void DicomMap::Unserialize(const Json::Value& source)
{
Clear();
if (source.type() != Json::objectValue)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
Json::Value::Members tags = source.getMemberNames();
for (size_t i = 0; i < tags.size(); i++)
{
DicomTag tag(0, 0);
if (!DicomTag::ParseHexadecimal(tag, tags[i].c_str()) ||
content_.find(tag) != content_.end())
{
throw OrthancException(ErrorCode_BadFileFormat);
}
std::unique_ptr value(new DicomValue);
value->Unserialize(source[tags[i]]);
content_[tag] = value.release();
}
}
void DicomMap::FromDicomWeb(const Json::Value& source)
{
static const char* const ALPHABETIC = "Alphabetic";
static const char* const IDEOGRAPHIC = "Ideographic";
static const char* const INLINE_BINARY = "InlineBinary";
static const char* const PHONETIC = "Phonetic";
static const char* const VALUE = "Value";
static const char* const VR = "vr";
Clear();
if (source.type() != Json::objectValue)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
Json::Value::Members tags = source.getMemberNames();
for (size_t i = 0; i < tags.size(); i++)
{
const Json::Value& item = source[tags[i]];
DicomTag tag(0, 0);
if (item.type() != Json::objectValue ||
!item.isMember(VR) ||
item[VR].type() != Json::stringValue ||
!DicomTag::ParseHexadecimal(tag, tags[i].c_str()))
{
throw OrthancException(ErrorCode_BadFileFormat);
}
ValueRepresentation vr = StringToValueRepresentation(item[VR].asString(), false);
if (item.isMember(INLINE_BINARY))
{
const Json::Value& value = item[INLINE_BINARY];
if (value.type() == Json::stringValue)
{
std::string decoded;
Toolbox::DecodeBase64(decoded, value.asString());
SetValue(tag, decoded, true /* binary data */);
}
}
else if (!item.isMember(VALUE))
{
// Tag is present, but it has a null value
SetValue(tag, "", false /* not binary */);
}
else
{
const Json::Value& value = item[VALUE];
if (value.type() == Json::arrayValue)
{
bool supported = true;
std::string s;
for (Json::Value::ArrayIndex j = 0; j < value.size() && supported; j++)
{
if (!s.empty())
{
s += '\\';
}
switch (value[j].type())
{
case Json::objectValue:
if (vr == ValueRepresentation_PersonName &&
value[j].type() == Json::objectValue)
{
if (value[j].isMember(ALPHABETIC) &&
value[j][ALPHABETIC].type() == Json::stringValue)
{
s += value[j][ALPHABETIC].asString();
}
bool hasIdeographic = false;
if (value[j].isMember(IDEOGRAPHIC) &&
value[j][IDEOGRAPHIC].type() == Json::stringValue)
{
s += '=' + value[j][IDEOGRAPHIC].asString();
hasIdeographic = true;
}
if (value[j].isMember(PHONETIC) &&
value[j][PHONETIC].type() == Json::stringValue)
{
if (!hasIdeographic)
{
s += '=';
}
s += '=' + value[j][PHONETIC].asString();
}
}
else
{
// This is the case of sequences
supported = false;
}
break;
case Json::stringValue:
s += value[j].asString();
break;
case Json::intValue:
s += boost::lexical_cast(value[j].asInt64());
break;
case Json::uintValue:
s += boost::lexical_cast(value[j].asUInt64());
break;
case Json::realValue:
s += boost::lexical_cast(value[j].asDouble());
break;
default:
break;
}
}
if (supported)
{
SetValue(tag, s, false /* not binary */);
}
}
}
}
}
std::string DicomMap::GetStringValue(const DicomTag& tag,
const std::string& defaultValue,
bool allowBinary) const
{
std::string s;
if (LookupStringValue(s, tag, allowBinary))
{
return s;
}
else
{
return defaultValue;
}
}
void DicomMap::RemoveBinaryTags()
{
Content kept;
for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(it->second != NULL);
if (!it->second->IsBinary() &&
!it->second->IsNull())
{
kept[it->first] = it->second;
}
else
{
delete it->second;
}
}
content_ = kept;
}
void DicomMap::RemoveSequences()
{
Content kept;
for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(it->second != NULL);
if (!it->second->IsSequence())
{
kept[it->first] = it->second;
}
else
{
delete it->second;
}
}
content_ = kept;
}
void DicomMap::DumpMainDicomTags(Json::Value& target,
ResourceType level) const
{
std::set mainDicomTags;
DicomMap::MainDicomTagsConfiguration::GetInstance().GetMainDicomTagsByLevel(mainDicomTags, level);
target = Json::objectValue;
for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(it->second != NULL);
if (!it->second->IsBinary() &&
!it->second->IsNull())
{
std::set::const_iterator found = mainDicomTags.find(it->first);
if (found != mainDicomTags.end())
{
#if ORTHANC_ENABLE_DCMTK == 1
target[FromDcmtkBridge::GetTagName(*found, "")] = it->second->GetContent();
#else
target[found->Format()] = it->second->GetContent();
#endif
}
}
}
}
ValueRepresentation DicomMap::GuessPixelDataValueRepresentation(DicomTransferSyntax transferSyntax) const
{
const DicomValue* value = TestAndGetValue(DICOM_TAG_BITS_ALLOCATED);
uint32_t bitsAllocated;
if (value == NULL ||
!value->ParseUnsignedInteger32(bitsAllocated))
{
bitsAllocated = 8;
}
return DicomImageInformation::GuessPixelDataValueRepresentation(transferSyntax, bitsAllocated);
}
void DicomMap::Print(FILE* fp) const
{
DicomArray a(*this);
a.Print(fp);
}
}