Skip to content
Commits on Source (5)
repo: 3959d33612ccaadc0d4d707227fbed09ac35e5fe
node: 78e5414d57f8874cfe88997726b97bd3b25e48df
branch: Orthanc-1.3.2
node: a2536c0d72deda960f2908e7bdcc56929837a0bf
branch: Orthanc-1.4.0
latesttag: dcmtk-3.6.1
latesttagdistance: 205
changessincelatesttag: 213
latesttagdistance: 354
changessincelatesttag: 396
syntax: glob
ThirdPartyDownloads/
\ No newline at end of file
ThirdPartyDownloads/
CMakeLists.txt.user
*.cpp.orig
*.h.orig
This diff is collapsed.
......@@ -80,9 +80,12 @@ namespace Orthanc
void MemoryCache::Invalidate(const std::string& id)
{
if (index_.Contains(id))
Page* p = NULL;
if (index_.Contains(id, p))
{
VLOG(1) << "Invalidating a cache page";
assert(p != NULL);
delete p;
index_.Invalidate(id);
}
}
......
......@@ -39,9 +39,32 @@
#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <string.h>
namespace Orthanc
{
{
static inline uint16_t GetCharValue(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else
return 0;
}
static inline uint16_t GetTagValue(const char* c)
{
return ((GetCharValue(c[0]) << 12) +
(GetCharValue(c[1]) << 8) +
(GetCharValue(c[2]) << 4) +
GetCharValue(c[3]));
}
bool DicomTag::operator< (const DicomTag& other) const
{
if (group_ < other.group_)
......@@ -74,6 +97,49 @@ namespace Orthanc
}
bool DicomTag::ParseHexadecimal(DicomTag& tag,
const char* value)
{
size_t length = strlen(value);
if (length == 9 &&
isxdigit(value[0]) &&
isxdigit(value[1]) &&
isxdigit(value[2]) &&
isxdigit(value[3]) &&
(value[4] == '-' || value[4] == ',') &&
isxdigit(value[5]) &&
isxdigit(value[6]) &&
isxdigit(value[7]) &&
isxdigit(value[8]))
{
uint16_t group = GetTagValue(value);
uint16_t element = GetTagValue(value + 5);
tag = DicomTag(group, element);
return true;
}
else if (length == 8 &&
isxdigit(value[0]) &&
isxdigit(value[1]) &&
isxdigit(value[2]) &&
isxdigit(value[3]) &&
isxdigit(value[4]) &&
isxdigit(value[5]) &&
isxdigit(value[6]) &&
isxdigit(value[7]))
{
uint16_t group = GetTagValue(value);
uint16_t element = GetTagValue(value + 4);
tag = DicomTag(group, element);
return true;
}
else
{
return false;
}
}
const char* DicomTag::GetMainTagsName() const
{
if (*this == DICOM_TAG_ACCESSION_NUMBER)
......
......@@ -88,6 +88,9 @@ namespace Orthanc
std::string Format() const;
static bool ParseHexadecimal(DicomTag& tag,
const char* value);
friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag);
static void AddTagsForModule(std::set<DicomTag>& target,
......
......@@ -264,8 +264,6 @@ namespace Orthanc
const std::string& moveOriginatorAET,
uint16_t moveOriginatorID)
{
CheckIsOpen();
DcmFileFormat dcmff;
Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength));
......@@ -284,23 +282,36 @@ namespace Orthanc
bool isGeneric = IsGenericTransferSyntax(syntax);
bool renegotiate;
if (isGeneric)
if (!IsOpen())
{
renegotiate = true;
}
else if (isGeneric)
{
// Are we making a generic-to-specific or specific-to-generic change of
// the transfer syntax? If this is the case, renegotiate the connection.
renegotiate = !IsGenericTransferSyntax(connection.GetPreferredTransferSyntax());
if (renegotiate)
{
LOG(INFO) << "Use of non-generic transfer syntax: the C-Store associated must be renegotiated";
}
}
else
{
// We are using a specific transfer syntax. Renegotiate if the
// current connection does not match this transfer syntax.
renegotiate = (syntax != connection.GetPreferredTransferSyntax());
if (renegotiate)
{
LOG(INFO) << "Change in the transfer syntax: the C-Store associated must be renegotiated";
}
}
if (renegotiate)
{
LOG(INFO) << "Change in the transfer syntax: the C-Store associated must be renegotiated";
if (isGeneric)
{
connection.ResetPreferredTransferSyntax();
......@@ -313,7 +324,6 @@ namespace Orthanc
if (!connection.IsOpen())
{
LOG(INFO) << "Renegotiating a C-Store association due to a change in the parameters";
connection.Open();
}
......@@ -785,13 +795,12 @@ namespace Orthanc
}
DicomUserConnection::DicomUserConnection() :
pimpl_(new PImpl),
preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX),
localAet_("STORESCU"),
remoteAet_("ANY-SCP"),
remoteHost_("127.0.0.1")
void DicomUserConnection::DefaultSetup()
{
preferredTransferSyntax_ = DEFAULT_PREFERRED_TRANSFER_SYNTAX;
localAet_ = "STORESCU";
remoteAet_ = "ANY-SCP";
remoteHost_ = "127.0.0.1";
remotePort_ = 104;
manufacturer_ = ModalityManufacturer_Generic;
......@@ -809,6 +818,24 @@ namespace Orthanc
ResetStorageSOPClasses();
}
DicomUserConnection::DicomUserConnection() :
pimpl_(new PImpl)
{
DefaultSetup();
}
DicomUserConnection::DicomUserConnection(const std::string& localAet,
const RemoteModalityParameters& remote) :
pimpl_(new PImpl)
{
DefaultSetup();
SetLocalApplicationEntityTitle(localAet);
SetRemoteModality(remote);
}
DicomUserConnection::~DicomUserConnection()
{
......@@ -820,7 +847,7 @@ namespace Orthanc
{
SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle());
SetRemoteHost(parameters.GetHost());
SetRemotePort(parameters.GetPort());
SetRemotePort(parameters.GetPortNumber());
SetRemoteManufacturer(parameters.GetManufacturer());
}
......@@ -1217,4 +1244,15 @@ namespace Orthanc
<< seconds << " seconds (0 = no timeout)";
defaultTimeout_ = seconds;
}
bool DicomUserConnection::IsSameAssociation(const std::string& localAet,
const RemoteModalityParameters& remote) const
{
return (localAet_ == localAet &&
remoteAet_ == remote.GetApplicationEntityTitle() &&
remoteHost_ == remote.GetHost() &&
remotePort_ == remote.GetPortNumber() &&
manufacturer_ == remote.GetManufacturer());
}
}
......@@ -77,11 +77,18 @@ namespace Orthanc
void CheckStorageSOPClassesInvariant() const;
void DefaultSetup();
public:
DicomUserConnection();
~DicomUserConnection();
// This constructor corresponds to behavior of the old class
// "ReusableDicomUserConnection", without the call to "Open()"
DicomUserConnection(const std::string& localAet,
const RemoteModalityParameters& remote);
void SetRemoteModality(const RemoteModalityParameters& parameters);
void SetLocalApplicationEntityTitle(const std::string& aet);
......@@ -201,5 +208,8 @@ namespace Orthanc
ParsedDicomFile& query);
static void SetDefaultTimeout(uint32_t seconds);
bool IsSameAssociation(const std::string& localAet,
const RemoteModalityParameters& remote) const;
};
}
/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2018 Osimis S.A., Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* In addition, as a special exception, the copyright holders of this
* program give permission to link the code of its release with the
* OpenSSL project's "OpenSSL" library (or with modified versions of it
* that use the same license as the "OpenSSL" library), and distribute
* the linked executables. You must obey the GNU General Public License
* in all respects for all of the code used other than "OpenSSL". If you
* modify file(s) with this exception, you may extend this exception to
* your version of the file(s), but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files
* in the program, then also delete it here.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#pragma once
#if !defined(ORTHANC_ENABLE_DCMTK_NETWORKING)
# error The macro ORTHANC_ENABLE_DCMTK_NETWORKING must be defined
#endif
#if ORTHANC_ENABLE_DCMTK_NETWORKING == 0
namespace Orthanc
{
// DICOM networking is disabled, this is just a void class
class IDicomConnectionManager : public boost::noncopyable
{
public:
virtual ~IDicomConnectionManager()
{
}
};
}
#else
#include "DicomUserConnection.h"
namespace Orthanc
{
class IDicomConnectionManager : public boost::noncopyable
{
public:
virtual ~IDicomConnectionManager()
{
}
class IResource : public boost::noncopyable
{
public:
virtual ~IResource()
{
}
virtual DicomUserConnection& GetConnection() = 0;
};
virtual IResource* AcquireConnection(const std::string& localAet,
const RemoteModalityParameters& remote) = 0;
};
}
#endif
......@@ -93,6 +93,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <boost/lexical_cast.hpp>
/**
* Macro specifying whether to apply the patch suggested in issue 66:
* "Orthanc responses C-MOVE with zero Move Originator Message ID"
* https://bitbucket.org/sjodogne/orthanc/issues/66/
**/
#define APPLY_FIX_ISSUE_66 1
namespace Orthanc
{
namespace
......@@ -112,7 +121,7 @@ namespace Orthanc
};
#if APPLY_FIX_ISSUE_66 != 1
static uint16_t GetMessageId(const DicomMap& message)
{
/**
......@@ -145,7 +154,7 @@ namespace Orthanc
return 0;
}
#endif
void MoveScpCallback(
......@@ -172,8 +181,15 @@ namespace Orthanc
try
{
#if APPLY_FIX_ISSUE_66 == 1
uint16_t messageId = request->MessageID;
#else
// The line below was the implementation for Orthanc <= 1.3.2
uint16_t messageId = GetMessageId(input);
#endif
data.iterator_.reset(data.handler_->Handle(data.target_, input, *data.remoteIp_, *data.remoteAet_,
*data.calledAet_, GetMessageId(input)));
*data.calledAet_, messageId));
if (data.iterator_.get() == NULL)
{
......
......@@ -36,6 +36,7 @@
#include "../Logging.h"
#include "../OrthancException.h"
#include "../SerializationToolbox.h"
#include <boost/lexical_cast.hpp>
#include <stdexcept>
......@@ -57,7 +58,7 @@ namespace Orthanc
{
SetApplicationEntityTitle(aet);
SetHost(host);
SetPort(port);
SetPortNumber(port);
SetManufacturer(manufacturer);
}
......@@ -83,13 +84,13 @@ namespace Orthanc
throw OrthancException(ErrorCode_ParameterOutOfRange);
}
SetPort(static_cast<uint16_t>(tmp));
SetPortNumber(static_cast<uint16_t>(tmp));
}
catch (std::runtime_error /* error inside JsonCpp */)
{
try
{
SetPort(boost::lexical_cast<uint16_t>(portValue.asString()));
SetPortNumber(boost::lexical_cast<uint16_t>(portValue.asString()));
}
catch (boost::bad_lexical_cast)
{
......@@ -117,12 +118,34 @@ namespace Orthanc
}
}
void RemoteModalityParameters::ToJson(Json::Value& value) const
{
value = Json::arrayValue;
value.append(GetApplicationEntityTitle());
value.append(GetHost());
value.append(GetPort());
value.append(GetPortNumber());
value.append(EnumerationToString(GetManufacturer()));
}
void RemoteModalityParameters::Serialize(Json::Value& target) const
{
target = Json::objectValue;
target["AET"] = aet_;
target["Host"] = host_;
target["Port"] = port_;
target["Manufacturer"] = EnumerationToString(manufacturer_);
}
RemoteModalityParameters::RemoteModalityParameters(const Json::Value& serialized)
{
aet_ = SerializationToolbox::ReadString(serialized, "AET");
host_ = SerializationToolbox::ReadString(serialized, "Host");
port_ = static_cast<uint16_t>
(SerializationToolbox::ReadUnsignedInteger(serialized, "Port"));
manufacturer_ = StringToModalityManufacturer
(SerializationToolbox::ReadString(serialized, "Manufacturer"));
}
}
......@@ -52,6 +52,8 @@ namespace Orthanc
public:
RemoteModalityParameters();
RemoteModalityParameters(const Json::Value& serialized);
RemoteModalityParameters(const std::string& aet,
const std::string& host,
uint16_t port,
......@@ -77,12 +79,12 @@ namespace Orthanc
host_ = host;
}
uint16_t GetPort() const
uint16_t GetPortNumber() const
{
return port_;
}
void SetPort(uint16_t port)
void SetPortNumber(uint16_t port)
{
port_ = port;
}
......@@ -105,5 +107,7 @@ namespace Orthanc
void FromJson(const Json::Value& modality);
void ToJson(Json::Value& value) const;
void Serialize(Json::Value& target) const;
};
}
/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2018 Osimis S.A., Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* In addition, as a special exception, the copyright holders of this
* program give permission to link the code of its release with the
* OpenSSL project's "OpenSSL" library (or with modified versions of it
* that use the same license as the "OpenSSL" library), and distribute
* the linked executables. You must obey the GNU General Public License
* in all respects for all of the code used other than "OpenSSL". If you
* modify file(s) with this exception, you may extend this exception to
* your version of the file(s), but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files
* in the program, then also delete it here.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#include "../PrecompiledHeaders.h"
#include "TimeoutDicomConnectionManager.h"
#include "../Logging.h"
#include "../OrthancException.h"
namespace Orthanc
{
static boost::posix_time::ptime GetNow()
{
return boost::posix_time::microsec_clock::universal_time();
}
class TimeoutDicomConnectionManager::Resource : public IDicomConnectionManager::IResource
{
private:
TimeoutDicomConnectionManager& that_;
public:
Resource(TimeoutDicomConnectionManager& that) :
that_(that)
{
if (that_.connection_.get() == NULL)
{
throw OrthancException(ErrorCode_InternalError);
}
}
~Resource()
{
that_.Touch();
}
DicomUserConnection& GetConnection()
{
assert(that_.connection_.get() != NULL);
return *that_.connection_;
}
};
void TimeoutDicomConnectionManager::Touch()
{
lastUse_ = GetNow();
}
void TimeoutDicomConnectionManager::CheckTimeoutInternal()
{
if (connection_.get() != NULL &&
(GetNow() - lastUse_) >= timeout_)
{
Close();
}
}
void TimeoutDicomConnectionManager::SetTimeout(unsigned int timeout)
{
timeout_ = boost::posix_time::milliseconds(timeout);
CheckTimeoutInternal();
}
unsigned int TimeoutDicomConnectionManager::GetTimeout()
{
return timeout_.total_milliseconds();
}
void TimeoutDicomConnectionManager::Close()
{
if (connection_.get() != NULL)
{
LOG(INFO) << "Closing inactive DICOM association with modality: "
<< connection_->GetRemoteApplicationEntityTitle();
connection_.reset(NULL);
}
}
void TimeoutDicomConnectionManager::CheckTimeout()
{
CheckTimeoutInternal();
}
IDicomConnectionManager::IResource*
TimeoutDicomConnectionManager::AcquireConnection(const std::string& localAet,
const RemoteModalityParameters& remote)
{
if (connection_.get() == NULL ||
!connection_->IsSameAssociation(localAet, remote))
{
connection_.reset(new DicomUserConnection(localAet, remote));
}
return new Resource(*this);
}
}
/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2018 Osimis S.A., Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* In addition, as a special exception, the copyright holders of this
* program give permission to link the code of its release with the
* OpenSSL project's "OpenSSL" library (or with modified versions of it
* that use the same license as the "OpenSSL" library), and distribute
* the linked executables. You must obey the GNU General Public License
* in all respects for all of the code used other than "OpenSSL". If you
* modify file(s) with this exception, you may extend this exception to
* your version of the file(s), but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files
* in the program, then also delete it here.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#pragma once
#include "IDicomConnectionManager.h"
#if ORTHANC_ENABLE_DCMTK_NETWORKING == 0
namespace Orthanc
{
class TimeoutDicomConnectionManager : public IDicomConnectionManager
{
public:
void SetTimeout(unsigned int timeout)
{
}
unsigned int GetTimeout()
{
return 0;
}
void Close()
{
}
void CheckTimeout()
{
}
};
}
#else
#include <boost/date_time/posix_time/posix_time.hpp>
namespace Orthanc
{
class TimeoutDicomConnectionManager : public IDicomConnectionManager
{
private:
class Resource;
std::auto_ptr<DicomUserConnection> connection_;
boost::posix_time::ptime lastUse_;
boost::posix_time::time_duration timeout_;
void Touch();
void CheckTimeoutInternal();
public:
TimeoutDicomConnectionManager() :
timeout_(boost::posix_time::milliseconds(1000))
{
}
void SetTimeout(unsigned int timeout);
unsigned int GetTimeout();
void Close();
void CheckTimeout();
virtual IResource* AcquireConnection(const std::string& localAet,
const RemoteModalityParameters& remote);
};
}
#endif
......@@ -164,20 +164,23 @@ namespace Orthanc
const DcmTagKey& key)
{
DcmElement* element = NULL;
result.clear();
if (source.findAndGetElement(key, element).good())
{
char* s = NULL;
if (element->isLeaf() &&
element->getString(s).good() &&
s != NULL)
element->getString(s).good())
{
result = Toolbox::ConvertToUtf8(s, encoding);
if (s != NULL)
{
result = Toolbox::ConvertToUtf8(s, encoding);
}
return true;
}
}
result.clear();
return false;
}
......
......@@ -36,6 +36,7 @@
#include "../Logging.h"
#include "../OrthancException.h"
#include "../SerializationToolbox.h"
#include "FromDcmtkBridge.h"
#include "ITagVisitor.h"
......@@ -315,6 +316,7 @@ namespace Orthanc
keepStudyInstanceUid_(false),
keepSeriesInstanceUid_(false),
updateReferencedRelationships_(true),
isAnonymization_(false),
identifierGenerator_(NULL)
{
}
......@@ -819,6 +821,8 @@ namespace Orthanc
void DicomModification::SetupAnonymization(DicomVersion version)
{
isAnonymization_ = true;
removals_.clear();
clearings_.clear();
ClearReplacements();
......@@ -1029,7 +1033,8 @@ namespace Orthanc
MapDicomTags(toModify, ResourceType_Instance);
}
// (6) Update the "referenced" relationships
// (6) Update the "referenced" relationships in the case of an anonymization
if (isAnonymization_)
{
RelationshipsVisitor visitor(*this);
......@@ -1237,4 +1242,169 @@ namespace Orthanc
patientNameReplaced = (IsReplaced(DICOM_TAG_PATIENT_NAME) &&
GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName);
}
static const char* REMOVE_PRIVATE_TAGS = "RemovePrivateTags";
static const char* LEVEL = "Level";
static const char* ALLOW_MANUAL_IDENTIFIERS = "AllowManualIdentifiers";
static const char* KEEP_STUDY_INSTANCE_UID = "KeepStudyInstanceUID";
static const char* KEEP_SERIES_INSTANCE_UID = "KeepSeriesInstanceUID";
static const char* UPDATE_REFERENCED_RELATIONSHIPS = "UpdateReferencedRelationships";
static const char* IS_ANONYMIZATION = "IsAnonymization";
static const char* REMOVALS = "Removals";
static const char* CLEARINGS = "Clearings";
static const char* PRIVATE_TAGS_TO_KEEP = "PrivateTagsToKeep";
static const char* REPLACEMENTS = "Replacements";
static const char* MAP_PATIENTS = "MapPatients";
static const char* MAP_STUDIES = "MapStudies";
static const char* MAP_SERIES = "MapSeries";
static const char* MAP_INSTANCES = "MapInstances";
void DicomModification::Serialize(Json::Value& value) const
{
if (identifierGenerator_ != NULL)
{
LOG(ERROR) << "Cannot serialize a DicomModification with a custom identifier generator";
throw OrthancException(ErrorCode_InternalError);
}
value = Json::objectValue;
value[REMOVE_PRIVATE_TAGS] = removePrivateTags_;
value[LEVEL] = EnumerationToString(level_);
value[ALLOW_MANUAL_IDENTIFIERS] = allowManualIdentifiers_;
value[KEEP_STUDY_INSTANCE_UID] = keepStudyInstanceUid_;
value[KEEP_SERIES_INSTANCE_UID] = keepSeriesInstanceUid_;
value[UPDATE_REFERENCED_RELATIONSHIPS] = updateReferencedRelationships_;
value[IS_ANONYMIZATION] = isAnonymization_;
SerializationToolbox::WriteSetOfTags(value, removals_, REMOVALS);
SerializationToolbox::WriteSetOfTags(value, clearings_, CLEARINGS);
SerializationToolbox::WriteSetOfTags(value, privateTagsToKeep_, PRIVATE_TAGS_TO_KEEP);
Json::Value& tmp = value[REPLACEMENTS];
tmp = Json::objectValue;
for (Replacements::const_iterator it = replacements_.begin();
it != replacements_.end(); ++it)
{
assert(it->second != NULL);
tmp[it->first.Format()] = *it->second;
}
Json::Value& mapPatients = value[MAP_PATIENTS];
Json::Value& mapStudies = value[MAP_STUDIES];
Json::Value& mapSeries = value[MAP_SERIES];
Json::Value& mapInstances = value[MAP_INSTANCES];
mapPatients = Json::objectValue;
mapStudies = Json::objectValue;
mapSeries = Json::objectValue;
mapInstances = Json::objectValue;
for (UidMap::const_iterator it = uidMap_.begin(); it != uidMap_.end(); ++it)
{
Json::Value* tmp = NULL;
switch (it->first.first)
{
case ResourceType_Patient:
tmp = &mapPatients;
break;
case ResourceType_Study:
tmp = &mapStudies;
break;
case ResourceType_Series:
tmp = &mapSeries;
break;
case ResourceType_Instance:
tmp = &mapInstances;
break;
default:
throw OrthancException(ErrorCode_InternalError);
}
assert(tmp != NULL);
(*tmp) [it->first.second] = it->second;
}
}
void DicomModification::UnserializeUidMap(ResourceType level,
const Json::Value& serialized,
const char* field)
{
if (!serialized.isMember(field) ||
serialized[field].type() != Json::objectValue)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
Json::Value::Members names = serialized[field].getMemberNames();
for (Json::Value::Members::const_iterator it = names.begin(); it != names.end(); ++it)
{
const Json::Value& value = serialized[field][*it];
if (value.type() != Json::stringValue)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
else
{
uidMap_[std::make_pair(level, *it)] = value.asString();
}
}
}
DicomModification::DicomModification(const Json::Value& serialized) :
identifierGenerator_(NULL)
{
removePrivateTags_ = SerializationToolbox::ReadBoolean(serialized, REMOVE_PRIVATE_TAGS);
level_ = StringToResourceType(SerializationToolbox::ReadString(serialized, LEVEL).c_str());
allowManualIdentifiers_ = SerializationToolbox::ReadBoolean(serialized, ALLOW_MANUAL_IDENTIFIERS);
keepStudyInstanceUid_ = SerializationToolbox::ReadBoolean(serialized, KEEP_STUDY_INSTANCE_UID);
keepSeriesInstanceUid_ = SerializationToolbox::ReadBoolean(serialized, KEEP_SERIES_INSTANCE_UID);
updateReferencedRelationships_ = SerializationToolbox::ReadBoolean
(serialized, UPDATE_REFERENCED_RELATIONSHIPS);
isAnonymization_ = SerializationToolbox::ReadBoolean(serialized, IS_ANONYMIZATION);
SerializationToolbox::ReadSetOfTags(removals_, serialized, REMOVALS);
SerializationToolbox::ReadSetOfTags(clearings_, serialized, CLEARINGS);
SerializationToolbox::ReadSetOfTags(privateTagsToKeep_, serialized, PRIVATE_TAGS_TO_KEEP);
if (!serialized.isMember(REPLACEMENTS) ||
serialized[REPLACEMENTS].type() != Json::objectValue)
{
throw OrthancException(ErrorCode_BadFileFormat);
}
Json::Value::Members names = serialized[REPLACEMENTS].getMemberNames();
for (Json::Value::Members::const_iterator it = names.begin(); it != names.end(); ++it)
{
DicomTag tag(0, 0);
if (!DicomTag::ParseHexadecimal(tag, it->c_str()))
{
throw OrthancException(ErrorCode_BadFileFormat);
}
else
{
const Json::Value& value = serialized[REPLACEMENTS][*it];
replacements_.insert(std::make_pair(tag, new Json::Value(value)));
}
}
UnserializeUidMap(ResourceType_Patient, serialized, MAP_PATIENTS);
UnserializeUidMap(ResourceType_Study, serialized, MAP_STUDIES);
UnserializeUidMap(ResourceType_Series, serialized, MAP_SERIES);
UnserializeUidMap(ResourceType_Instance, serialized, MAP_INSTANCES);
}
}
......@@ -84,6 +84,7 @@ namespace Orthanc
bool keepStudyInstanceUid_;
bool keepSeriesInstanceUid_;
bool updateReferencedRelationships_;
bool isAnonymization_;
DicomMap currentSource_;
IDicomIdentifierGenerator* identifierGenerator_;
......@@ -107,9 +108,15 @@ namespace Orthanc
void SetupAnonymization2017c();
void UnserializeUidMap(ResourceType level,
const Json::Value& serialized,
const char* field);
public:
DicomModification();
DicomModification(const Json::Value& serialized);
~DicomModification();
void Keep(const DicomTag& tag);
......@@ -172,5 +179,7 @@ namespace Orthanc
{
identifierGenerator_ = &generator;
}
void Serialize(Json::Value& value) const;
};
}
......@@ -57,6 +57,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
#include <dcmtk/dcmdata/dcdeftag.h>
#include <dcmtk/dcmdata/dcdicent.h>
......@@ -105,27 +106,6 @@
namespace Orthanc
{
static inline uint16_t GetCharValue(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else
return 0;
}
static inline uint16_t GetTagValue(const char* c)
{
return ((GetCharValue(c[0]) << 12) +
(GetCharValue(c[1]) << 8) +
(GetCharValue(c[2]) << 4) +
GetCharValue(c[3]));
}
#if DCMTK_USE_EMBEDDED_DICTIONARIES == 1
static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary,
EmbeddedResources::FileResourceId resource)
......@@ -198,6 +178,56 @@ namespace Orthanc
return &dictionary_;
}
};
#define DCMTK_TO_CTYPE_CONVERTER(converter, cType, dcmtkType, getter) \
\
struct converter \
{ \
typedef cType CType; \
\
static bool Apply(CType& result, \
DcmElement& element, \
size_t i) \
{ \
return dynamic_cast<dcmtkType&>(element).getter(result, i).good(); \
} \
};
DCMTK_TO_CTYPE_CONVERTER(DcmtkToSint32Converter, Sint32, DcmSignedLong, getSint32)
DCMTK_TO_CTYPE_CONVERTER(DcmtkToSint16Converter, Sint16, DcmSignedShort, getSint16)
DCMTK_TO_CTYPE_CONVERTER(DcmtkToUint32Converter, Uint32, DcmUnsignedLong, getUint32)
DCMTK_TO_CTYPE_CONVERTER(DcmtkToUint16Converter, Uint16, DcmUnsignedShort, getUint16)
DCMTK_TO_CTYPE_CONVERTER(DcmtkToFloat32Converter, Float32, DcmFloatingPointSingle, getFloat32)
DCMTK_TO_CTYPE_CONVERTER(DcmtkToFloat64Converter, Float64, DcmFloatingPointDouble, getFloat64)
template <typename F>
static DicomValue* ApplyDcmtkToCTypeConverter(DcmElement& element)
{
F f;
typename F::CType value;
if (element.getLength() > sizeof(typename F::CType)
&& (element.getLength() % sizeof(typename F::CType)) == 0)
{
size_t count = element.getLength() / sizeof(typename F::CType);
std::vector<std::string> strings;
for (size_t i = 0; i < count; i++) {
if (f.Apply(value, element, i)) {
strings.push_back(boost::lexical_cast<std::string>(value));
}
}
return new DicomValue(boost::algorithm::join(strings, "\\"), false);
}
else if (f.Apply(value, element, 0)) {
return new DicomValue(boost::lexical_cast<std::string>(value), false);
}
else {
return new DicomValue;
}
}
}
......@@ -572,56 +602,32 @@ namespace Orthanc
case EVR_SL: // signed long
{
Sint32 f;
if (dynamic_cast<DcmSignedLong&>(element).getSint32(f).good())
return new DicomValue(boost::lexical_cast<std::string>(f), false);
else
return new DicomValue;
return ApplyDcmtkToCTypeConverter<DcmtkToSint32Converter>(element);
}
case EVR_SS: // signed short
{
Sint16 f;
if (dynamic_cast<DcmSignedShort&>(element).getSint16(f).good())
return new DicomValue(boost::lexical_cast<std::string>(f), false);
else
return new DicomValue;
return ApplyDcmtkToCTypeConverter<DcmtkToSint16Converter>(element);
}
case EVR_UL: // unsigned long
{
Uint32 f;
if (dynamic_cast<DcmUnsignedLong&>(element).getUint32(f).good())
return new DicomValue(boost::lexical_cast<std::string>(f), false);
else
return new DicomValue;
return ApplyDcmtkToCTypeConverter<DcmtkToUint32Converter>(element);
}
case EVR_US: // unsigned short
{
Uint16 f;
if (dynamic_cast<DcmUnsignedShort&>(element).getUint16(f).good())
return new DicomValue(boost::lexical_cast<std::string>(f), false);
else
return new DicomValue;
return ApplyDcmtkToCTypeConverter<DcmtkToUint16Converter>(element);
}
case EVR_FL: // float single-precision
{
Float32 f;
if (dynamic_cast<DcmFloatingPointSingle&>(element).getFloat32(f).good())
return new DicomValue(boost::lexical_cast<std::string>(f), false);
else
return new DicomValue;
return ApplyDcmtkToCTypeConverter<DcmtkToFloat32Converter>(element);
}
case EVR_FD: // float double-precision
{
Float64 f;
if (dynamic_cast<DcmFloatingPointDouble&>(element).getFloat64(f).good())
return new DicomValue(boost::lexical_cast<std::string>(f), false);
else
return new DicomValue;
return ApplyDcmtkToCTypeConverter<DcmtkToFloat64Converter>(element);
}
......@@ -1034,35 +1040,10 @@ namespace Orthanc
DicomTag FromDcmtkBridge::ParseTag(const char* name)
{
if (strlen(name) == 9 &&
isxdigit(name[0]) &&
isxdigit(name[1]) &&
isxdigit(name[2]) &&
isxdigit(name[3]) &&
(name[4] == '-' || name[4] == ',') &&
isxdigit(name[5]) &&
isxdigit(name[6]) &&
isxdigit(name[7]) &&
isxdigit(name[8]))
{
uint16_t group = GetTagValue(name);
uint16_t element = GetTagValue(name + 5);
return DicomTag(group, element);
}
if (strlen(name) == 8 &&
isxdigit(name[0]) &&
isxdigit(name[1]) &&
isxdigit(name[2]) &&
isxdigit(name[3]) &&
isxdigit(name[4]) &&
isxdigit(name[5]) &&
isxdigit(name[6]) &&
isxdigit(name[7]))
{
uint16_t group = GetTagValue(name);
uint16_t element = GetTagValue(name + 4);
return DicomTag(group, element);
DicomTag parsed(0, 0);
if (DicomTag::ParseHexadecimal(parsed, name))
{
return parsed;
}
#if 0
......
......@@ -93,6 +93,7 @@
#if ORTHANC_ENABLE_JPEG == 1
# include "../../Images/JpegWriter.h"
#endif
#include "../../Images/PamWriter.h"
#include <boost/lexical_cast.hpp>
......@@ -952,6 +953,17 @@ namespace Orthanc
}
void DicomImageDecoder::ExtractPamImage(std::string& result,
std::auto_ptr<ImageAccessor>& image,
ImageExtractionMode mode,
bool invert)
{
ApplyExtractionMode(image, mode, invert);
PamWriter writer;
writer.WriteToMemory(result, *image);
}
#if ORTHANC_ENABLE_PNG == 1
void DicomImageDecoder::ExtractPngImage(std::string& result,
std::auto_ptr<ImageAccessor>& image,
......
......@@ -101,6 +101,11 @@ namespace Orthanc
static ImageAccessor *Decode(ParsedDicomFile& dicom,
unsigned int frame);
static void ExtractPamImage(std::string& result,
std::auto_ptr<ImageAccessor>& image,
ImageExtractionMode mode,
bool invert);
#if ORTHANC_ENABLE_PNG == 1
static void ExtractPngImage(std::string& result,
std::auto_ptr<ImageAccessor>& image,
......