Mercurial > hg > orthanc
diff Core/DicomNetworking/DicomStoreUserConnection.cpp @ 3896:210af28c4087 transcoding
merge
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Thu, 07 May 2020 11:32:15 +0200 |
parents | 8f7ad4989fec |
children | c62f84c7eda9 |
line wrap: on
line diff
--- a/Core/DicomNetworking/DicomStoreUserConnection.cpp Thu May 07 11:31:58 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.cpp Thu May 07 11:32:15 2020 +0200 @@ -84,13 +84,13 @@ it = syntaxes.begin(); it != syntaxes.end(); ++it) { association_->ProposePresentationContext(sopClassUid, *it); + proposedOriginalClasses_.insert(std::make_pair(sopClassUid, *it)); } if (addLittleEndianImplicit) { - std::set<DicomTransferSyntax> uncompressed; - uncompressed.insert(DicomTransferSyntax_LittleEndianImplicit); - association_->ProposePresentationContext(sopClassUid, uncompressed); + association_->ProposePresentationContext(sopClassUid, DicomTransferSyntax_LittleEndianImplicit); + proposedOriginalClasses_.insert(std::make_pair(sopClassUid, DicomTransferSyntax_LittleEndianImplicit)); } if (addLittleEndianExplicit || @@ -109,6 +109,14 @@ } association_->ProposePresentationContext(sopClassUid, uncompressed); + + assert(!uncompressed.empty()); + if (addLittleEndianExplicit ^ addBigEndianExplicit) + { + // Only one transfer syntax was proposed for this presentation context + assert(uncompressed.size() == 1); + proposedOriginalClasses_.insert(std::make_pair(sopClassUid, *uncompressed.begin())); + } } return true; @@ -139,42 +147,27 @@ } - void DicomStoreUserConnection::Setup() - { - association_.reset(new DicomAssociation); - proposeCommonClasses_ = true; - proposeUncompressedSyntaxes_ = true; - proposeRetiredBigEndian_ = false; - } - - - DicomStoreUserConnection::DicomStoreUserConnection( - const std::string& localAet, - const RemoteModalityParameters& remote) : - parameters_(localAet, remote) - { - Setup(); - } - - DicomStoreUserConnection::DicomStoreUserConnection( const DicomAssociationParameters& params) : - parameters_(params) + parameters_(params), + association_(new DicomAssociation), + proposeCommonClasses_(true), + proposeUncompressedSyntaxes_(true), + proposeRetiredBigEndian_(false) { - Setup(); } void DicomStoreUserConnection::RegisterStorageClass(const std::string& sopClassUid, DicomTransferSyntax syntax) { - StorageClasses::iterator found = storageClasses_.find(sopClassUid); + RegisteredClasses::iterator found = registeredClasses_.find(sopClassUid); - if (found == storageClasses_.end()) + if (found == registeredClasses_.end()) { std::set<DicomTransferSyntax> ts; ts.insert(syntax); - storageClasses_[sopClassUid] = ts; + registeredClasses_[sopClassUid] = ts; } else { @@ -186,22 +179,26 @@ void DicomStoreUserConnection::LookupParameters(std::string& sopClassUid, std::string& sopInstanceUid, DicomTransferSyntax& transferSyntax, - DcmDataset& dataset) + DcmFileFormat& dicom) { + if (dicom.getDataset() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + OFString a, b; - if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() || - !dataset.findAndGetOFString(DCM_SOPInstanceUID, b).good()) + if (!dicom.getDataset()->findAndGetOFString(DCM_SOPClassUID, a).good() || + !dicom.getDataset()->findAndGetOFString(DCM_SOPInstanceUID, b).good()) { throw OrthancException(ErrorCode_NoSopClassOrInstance, "Unable to determine the SOP class/instance for C-STORE with AET " + - parameters_.GetRemoteApplicationEntityTitle()); + parameters_.GetRemoteModality().GetApplicationEntityTitle()); } sopClassUid.assign(a.c_str()); sopInstanceUid.assign(b.c_str()); - if (!FromDcmtkBridge::LookupOrthancTransferSyntax( - transferSyntax, dataset.getOriginalXfer())) + if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax, dicom)) { throw OrthancException(ErrorCode_InternalError, "Unknown transfer syntax from DCMTK"); @@ -216,7 +213,7 @@ { /** * Step 1: Check whether this presentation context is already - * available in the previously negociated assocation. + * available in the previously negotiated assocation. **/ if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax)) @@ -227,24 +224,35 @@ // The association must be re-negotiated if (association_->IsOpen()) { - LOG(INFO) << "Re-negociating DICOM association with " - << parameters_.GetRemoteApplicationEntityTitle(); + LOG(INFO) << "Re-negotiating DICOM association with " + << parameters_.GetRemoteModality().GetApplicationEntityTitle(); + + if (proposedOriginalClasses_.find(std::make_pair(sopClassUid, transferSyntax)) != + proposedOriginalClasses_.end()) + { + LOG(INFO) << "The remote modality has already rejected SOP class UID \"" + << sopClassUid << "\" with transfer syntax \"" + << GetTransferSyntaxUid(transferSyntax) << "\", don't renegotiate"; + return false; + } } + + association_->ClearPresentationContexts(); + proposedOriginalClasses_.clear(); + RegisterStorageClass(sopClassUid, transferSyntax); // (*) + - association_->ClearPresentationContexts(); - RegisterStorageClass(sopClassUid, transferSyntax); - - /** * Step 2: Propose at least the mandatory SOP class. **/ { - StorageClasses::const_iterator mandatory = storageClasses_.find(sopClassUid); + RegisteredClasses::const_iterator mandatory = registeredClasses_.find(sopClassUid); - if (mandatory == storageClasses_.end() || + if (mandatory == registeredClasses_.end() || mandatory->second.find(transferSyntax) == mandatory->second.end()) { + // Should never fail because of (*) throw OrthancException(ErrorCode_InternalError); } @@ -263,8 +271,8 @@ * registered through the "RegisterStorageClass()" method. **/ - for (StorageClasses::const_iterator it = storageClasses_.begin(); - it != storageClasses_.end(); ++it) + for (RegisteredClasses::const_iterator it = registeredClasses_.begin(); + it != registeredClasses_.end(); ++it) { if (it->first != sopClassUid) { @@ -292,7 +300,7 @@ std::string c(dcmShortSCUStorageSOPClassUIDs[i]); if (c != sopClassUid && - storageClasses_.find(c) == storageClasses_.end()) + registeredClasses_.find(c) == registeredClasses_.end()) { ProposeStorageClass(c, ts); } @@ -312,13 +320,14 @@ void DicomStoreUserConnection::Store(std::string& sopClassUid, std::string& sopInstanceUid, - DcmDataset& dataset, + DcmFileFormat& dicom, + bool hasMoveOriginator, const std::string& moveOriginatorAET, uint16_t moveOriginatorID) { DicomTransferSyntax transferSyntax; - LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dataset); - + LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dicom); + uint8_t presID; if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax)) { @@ -327,7 +336,7 @@ "SOP class UID [" + sopClassUid + "] and transfer " "syntax [" + GetTransferSyntaxUid(transferSyntax) + "] " "while sending to modality [" + - parameters_.GetRemoteApplicationEntityTitle() + "]"); + parameters_.GetRemoteModality().GetApplicationEntityTitle() + "]"); } // Prepare the transmission of data @@ -339,8 +348,8 @@ request.DataSetType = DIMSE_DATASET_PRESENT; strncpy(request.AffectedSOPInstanceUID, sopInstanceUid.c_str(), DIC_UI_LEN); - if (!moveOriginatorAET.empty()) - { + if (hasMoveOriginator) + { strncpy(request.MoveOriginatorApplicationEntityTitle, moveOriginatorAET.c_str(), DIC_AE_LEN); request.opts = O_STORE_MOVEORIGINATORAETITLE; @@ -349,12 +358,17 @@ request.opts |= O_STORE_MOVEORIGINATORID; } + if (dicom.getDataset() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + // Finally conduct transmission of data T_DIMSE_C_StoreRSP response; DcmDataset* statusDetail = NULL; DicomAssociation::CheckCondition( DIMSE_storeUser(&association_->GetDcmtkAssociation(), presID, &request, - NULL, &dataset, /*progressCallback*/ NULL, NULL, + NULL, dicom.getDataset(), /*progressCallback*/ NULL, NULL, /*opt_blockMode*/ (GetParameters().HasTimeout() ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), /*opt_dimse_timeout*/ GetParameters().GetTimeout(), &response, &statusDetail, NULL), @@ -379,7 +393,7 @@ sprintf(buf, "%04X", response.DimseStatus); throw OrthancException(ErrorCode_NetworkProtocol, "C-STORE SCU to AET \"" + - GetParameters().GetRemoteApplicationEntityTitle() + + GetParameters().GetRemoteModality().GetApplicationEntityTitle() + "\" has failed with DIMSE status 0x" + buf); } } @@ -387,37 +401,148 @@ void DicomStoreUserConnection::Store(std::string& sopClassUid, std::string& sopInstanceUid, - ParsedDicomFile& parsed, - const std::string& moveOriginatorAET, - uint16_t moveOriginatorID) - { - if (parsed.GetDcmtkObject().getDataset() == NULL) - { - throw OrthancException(ErrorCode_InternalError); - } - - Store(sopClassUid, sopInstanceUid, *parsed.GetDcmtkObject().getDataset(), - moveOriginatorAET, moveOriginatorID); - } - - - void DicomStoreUserConnection::Store(std::string& sopClassUid, - std::string& sopInstanceUid, const void* buffer, size_t size, + bool hasMoveOriginator, const std::string& moveOriginatorAET, uint16_t moveOriginatorID) { std::unique_ptr<DcmFileFormat> dicom( FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); - if (dicom.get() == NULL || - dicom->getDataset() == NULL) + if (dicom.get() == NULL) { throw OrthancException(ErrorCode_InternalError); } - Store(sopClassUid, sopInstanceUid, *dicom->getDataset(), - moveOriginatorAET, moveOriginatorID); + Store(sopClassUid, sopInstanceUid, *dicom, hasMoveOriginator, moveOriginatorAET, moveOriginatorID); + } + + + void DicomStoreUserConnection::LookupTranscoding(std::set<DicomTransferSyntax>& acceptedSyntaxes, + const std::string& sopClassUid, + DicomTransferSyntax sourceSyntax) + { + acceptedSyntaxes.clear(); + + // Make sure a negotiation has already occurred for this transfer + // syntax. We don't use the return code: Transcoding is possible + // even if the "sourceSyntax" is not supported. + uint8_t presID; + NegotiatePresentationContext(presID, sopClassUid, sourceSyntax); + + std::map<DicomTransferSyntax, uint8_t> contexts; + if (association_->LookupAcceptedPresentationContext(contexts, sopClassUid)) + { + for (std::map<DicomTransferSyntax, uint8_t>::const_iterator + it = contexts.begin(); it != contexts.end(); ++it) + { + acceptedSyntaxes.insert(it->first); + } + } + } + + + void DicomStoreUserConnection::Transcode(std::string& sopClassUid /* out */, + std::string& sopInstanceUid /* out */, + IDicomTranscoder& transcoder, + const void* buffer, + size_t size, + bool hasMoveOriginator, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID) + { + std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); + if (dicom.get() == NULL || + dicom->getDataset() == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + + DicomTransferSyntax inputSyntax; + LookupParameters(sopClassUid, sopInstanceUid, inputSyntax, *dicom); + + std::set<DicomTransferSyntax> accepted; + LookupTranscoding(accepted, sopClassUid, inputSyntax); + + if (accepted.find(inputSyntax) != accepted.end()) + { + // No need for transcoding + Store(sopClassUid, sopInstanceUid, *dicom, + hasMoveOriginator, moveOriginatorAET, moveOriginatorID); + } + else + { + // Transcoding is needed + std::set<DicomTransferSyntax> uncompressedSyntaxes; + + if (accepted.find(DicomTransferSyntax_LittleEndianImplicit) != accepted.end()) + { + uncompressedSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit); + } + + if (accepted.find(DicomTransferSyntax_LittleEndianExplicit) != accepted.end()) + { + uncompressedSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit); + } + + if (accepted.find(DicomTransferSyntax_BigEndianExplicit) != accepted.end()) + { + uncompressedSyntaxes.insert(DicomTransferSyntax_BigEndianExplicit); + } + + std::unique_ptr<DcmFileFormat> transcoded; + + bool hasSopInstanceUidChanged; + + if (transcoder.HasInplaceTranscode()) + { + if (transcoder.InplaceTranscode(hasSopInstanceUidChanged, *dicom, uncompressedSyntaxes, false)) + { + // In-place transcoding is supported and has succeeded + transcoded.reset(dicom.release()); + } + } + else + { + transcoded.reset(transcoder.TranscodeToParsed(hasSopInstanceUidChanged, buffer, size, uncompressedSyntaxes, false)); + } + + if (hasSopInstanceUidChanged) + { + throw OrthancException(ErrorCode_Plugin, "The transcoder has changed the SOP " + "instance UID while transcoding to an uncompressed transfer syntax"); + } + + // WARNING: The "dicom" variable must not be used below this + // point. The "sopInstanceUid" might also have changed (if + // using lossy compression). + + if (transcoded == NULL || + transcoded->getDataset() == NULL) + { + throw OrthancException( + ErrorCode_NotImplemented, + "Cannot transcode from \"" + std::string(GetTransferSyntaxUid(inputSyntax)) + + "\" to an uncompressed syntax for modality: " + + GetParameters().GetRemoteModality().GetApplicationEntityTitle()); + } + else + { + DicomTransferSyntax transcodedSyntax; + + // Sanity check + if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transcodedSyntax, *transcoded) || + accepted.find(transcodedSyntax) == accepted.end()) + { + throw OrthancException(ErrorCode_InternalError); + } + else + { + Store(sopClassUid, sopInstanceUid, *transcoded, + hasMoveOriginator, moveOriginatorAET, moveOriginatorID); + } + } + } } }