Mercurial > hg > orthanc
diff OrthancServer/ParsedDicomFile.cpp @ 1701:4aaaecae5803 db-changes
integration mainline->db-changes
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 12 Oct 2015 14:47:58 +0200 |
parents | de1413733c97 8ca0e89798b2 |
children | a001f6226c7c |
line wrap: on
line diff
--- a/OrthancServer/ParsedDicomFile.cpp Tue Oct 06 14:44:52 2015 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Mon Oct 12 14:47:58 2015 +0200 @@ -137,6 +137,7 @@ #include <boost/math/special_functions/round.hpp> #include <dcmtk/dcmdata/dcostrmb.h> +#include <boost/algorithm/string/predicate.hpp> static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; @@ -148,7 +149,6 @@ struct ParsedDicomFile::PImpl { std::auto_ptr<DcmFileFormat> file_; - Encoding encoding_; }; @@ -173,8 +173,6 @@ } pimpl_->file_->loadAllDataIntoMemory(); pimpl_->file_->transferEnd(); - - pimpl_->encoding_ = FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset()); } @@ -519,284 +517,6 @@ } - - - - static DcmElement* CreateElementForTag(const DicomTag& tag) - { - DcmTag key(tag.GetGroup(), tag.GetElement()); - - switch (key.getEVR()) - { - // http://support.dcmtk.org/docs/dcvr_8h-source.html - - /** - * TODO. - **/ - - case EVR_OB: // other byte - case EVR_OF: // other float - case EVR_OW: // other word - case EVR_AT: // attribute tag - throw OrthancException(ErrorCode_NotImplemented); - - case EVR_UN: // unknown value representation - throw OrthancException(ErrorCode_ParameterOutOfRange); - - - /** - * String types. - * http://support.dcmtk.org/docs/classDcmByteString.html - **/ - - case EVR_AS: // age string - return new DcmAgeString(key); - - case EVR_AE: // application entity title - return new DcmApplicationEntity(key); - - case EVR_CS: // code string - return new DcmCodeString(key); - - case EVR_DA: // date string - return new DcmDate(key); - - case EVR_DT: // date time string - return new DcmDateTime(key); - - case EVR_DS: // decimal string - return new DcmDecimalString(key); - - case EVR_IS: // integer string - return new DcmIntegerString(key); - - case EVR_TM: // time string - return new DcmTime(key); - - case EVR_UI: // unique identifier - return new DcmUniqueIdentifier(key); - - case EVR_ST: // short text - return new DcmShortText(key); - - case EVR_LO: // long string - return new DcmLongString(key); - - case EVR_LT: // long text - return new DcmLongText(key); - - case EVR_UT: // unlimited text - return new DcmUnlimitedText(key); - - case EVR_SH: // short string - return new DcmShortString(key); - - case EVR_PN: // person name - return new DcmPersonName(key); - - - /** - * Numerical types - **/ - - case EVR_SL: // signed long - return new DcmSignedLong(key); - - case EVR_SS: // signed short - return new DcmSignedShort(key); - - case EVR_UL: // unsigned long - return new DcmUnsignedLong(key); - - case EVR_US: // unsigned short - return new DcmUnsignedShort(key); - - case EVR_FL: // float single-precision - return new DcmFloatingPointSingle(key); - - case EVR_FD: // float double-precision - return new DcmFloatingPointDouble(key); - - - /** - * Sequence types, should never occur at this point. - **/ - - case EVR_SQ: // sequence of items - throw OrthancException(ErrorCode_ParameterOutOfRange); - - - /** - * Internal to DCMTK. - **/ - - case EVR_ox: // OB or OW depending on context - case EVR_xs: // SS or US depending on context - case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) - case EVR_na: // na="not applicable", for data which has no VR - case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor - case EVR_item: // used internally for items - case EVR_metainfo: // used internally for meta info datasets - case EVR_dataset: // used internally for datasets - case EVR_fileFormat: // used internally for DICOM files - case EVR_dicomDir: // used internally for DICOMDIR objects - case EVR_dirRecord: // used internally for DICOMDIR records - case EVR_pixelSQ: // used internally for pixel sequences in a compressed image - case EVR_pixelItem: // used internally for pixel items in a compressed image - case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) - case EVR_PixelData: // used internally for uncompressed pixeld data - case EVR_OverlayData: // used internally for overlay data - case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR - default: - break; - } - - throw OrthancException(ErrorCode_InternalError); - } - - - - static void FillElementWithString(DcmElement& element, - const DicomTag& tag, - const std::string& value) - { - DcmTag key(tag.GetGroup(), tag.GetElement()); - bool ok = false; - - try - { - switch (key.getEVR()) - { - // http://support.dcmtk.org/docs/dcvr_8h-source.html - - /** - * TODO. - **/ - - case EVR_OB: // other byte - case EVR_OF: // other float - case EVR_OW: // other word - case EVR_AT: // attribute tag - throw OrthancException(ErrorCode_NotImplemented); - - case EVR_UN: // unknown value representation - throw OrthancException(ErrorCode_ParameterOutOfRange); - - - /** - * String types. - **/ - - case EVR_DS: // decimal string - case EVR_IS: // integer string - case EVR_AS: // age string - case EVR_DA: // date string - case EVR_DT: // date time string - case EVR_TM: // time string - case EVR_AE: // application entity title - case EVR_CS: // code string - case EVR_SH: // short string - case EVR_LO: // long string - case EVR_ST: // short text - case EVR_LT: // long text - case EVR_UT: // unlimited text - case EVR_PN: // person name - case EVR_UI: // unique identifier - { - ok = element.putString(value.c_str()).good(); - break; - } - - - /** - * Numerical types - **/ - - case EVR_SL: // signed long - { - ok = element.putSint32(boost::lexical_cast<Sint32>(value)).good(); - break; - } - - case EVR_SS: // signed short - { - ok = element.putSint16(boost::lexical_cast<Sint16>(value)).good(); - break; - } - - case EVR_UL: // unsigned long - { - ok = element.putUint32(boost::lexical_cast<Uint32>(value)).good(); - break; - } - - case EVR_US: // unsigned short - { - ok = element.putUint16(boost::lexical_cast<Uint16>(value)).good(); - break; - } - - case EVR_FL: // float single-precision - { - ok = element.putFloat32(boost::lexical_cast<float>(value)).good(); - break; - } - - case EVR_FD: // float double-precision - { - ok = element.putFloat64(boost::lexical_cast<double>(value)).good(); - break; - } - - - /** - * Sequence types, should never occur at this point. - **/ - - case EVR_SQ: // sequence of items - { - ok = false; - break; - } - - - /** - * Internal to DCMTK. - **/ - - case EVR_ox: // OB or OW depending on context - case EVR_xs: // SS or US depending on context - case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) - case EVR_na: // na="not applicable", for data which has no VR - case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor - case EVR_item: // used internally for items - case EVR_metainfo: // used internally for meta info datasets - case EVR_dataset: // used internally for datasets - case EVR_fileFormat: // used internally for DICOM files - case EVR_dicomDir: // used internally for DICOMDIR objects - case EVR_dirRecord: // used internally for DICOMDIR records - case EVR_pixelSQ: // used internally for pixel sequences in a compressed image - case EVR_pixelItem: // used internally for pixel items in a compressed image - case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) - case EVR_PixelData: // used internally for uncompressed pixeld data - case EVR_OverlayData: // used internally for overlay data - case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR - default: - break; - } - } - catch (boost::bad_lexical_cast&) - { - ok = false; - } - - if (!ok) - { - throw OrthancException(ErrorCode_InternalError); - } - } - - void ParsedDicomFile::Remove(const DicomTag& tag) { DcmTagKey key(tag.GetGroup(), tag.GetElement()); @@ -823,7 +543,7 @@ DcmTag tag(element->getTag()); // Is this a private tag? - if (FromDcmtkBridge::IsPrivateTag(tag)) + if (tag.isPrivate()) { bool remove = true; @@ -857,54 +577,40 @@ } - - - void ParsedDicomFile::Insert(const DicomTag& tag, - const std::string& value) + static void InsertInternal(DcmDataset& dicom, + DcmElement* element) { - OFCondition cond; - - if (FromDcmtkBridge::IsPrivateTag(tag) || - FromDcmtkBridge::IsUnknownTag(tag)) - { - // This is a private tag - // http://support.dcmtk.org/redmine/projects/dcmtk/wiki/howto_addprivatedata - - DcmTag key(tag.GetGroup(), tag.GetElement(), EVR_OB); - cond = pimpl_->file_->getDataset()->putAndInsertUint8Array - (key, (const Uint8*) value.c_str(), value.size(), false); - } - else - { - std::auto_ptr<DcmElement> element(CreateElementForTag(tag)); - FillElementWithString(*element, tag, value); - - cond = pimpl_->file_->getDataset()->insert(element.release(), false, false); - } - + OFCondition cond = dicom.insert(element, false, false); if (!cond.good()) { // This field already exists + delete element; throw OrthancException(ErrorCode_InternalError); } } - void ParsedDicomFile::Replace(const DicomTag& tag, - const std::string& value, - DicomReplaceMode mode) + void ParsedDicomFile::Insert(const DicomTag& tag, + const Json::Value& value, + bool decodeBinaryTags) { - DcmTagKey key(tag.GetGroup(), tag.GetElement()); - DcmElement* element = NULL; + std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeBinaryTags, GetEncoding())); + InsertInternal(*pimpl_->file_->getDataset(), element.release()); + } + - if (!pimpl_->file_->getDataset()->findAndGetElement(key, element).good() || - element == NULL) + static void ReplaceInternal(DcmDataset& dicom, + std::auto_ptr<DcmElement>& element, + DicomReplaceMode mode) + { + const DcmTagKey& tag = element->getTag(); + + if (!dicom.findAndDeleteElement(tag).good()) { // This field does not exist, act wrt. the specified "mode" switch (mode) { case DicomReplaceMode_InsertIfAbsent: - Insert(tag, value); break; case DicomReplaceMode_ThrowIfAbsent: @@ -914,23 +620,43 @@ return; } } + + // Either the tag was not existing, or the replace mode was set to + // "InsertIfAbsent" + InsertInternal(dicom, element.release()); + } + + + void ParsedDicomFile::UpdateStorageUid(const DicomTag& tag, + const std::string& utf8Value, + bool decodeBinaryTags) + { + if (tag != DICOM_TAG_SOP_CLASS_UID && + tag != DICOM_TAG_SOP_INSTANCE_UID) + { + return; + } + + std::string binary; + const std::string* decoded = &utf8Value; + + if (decodeBinaryTags && + boost::starts_with(utf8Value, "data:application/octet-stream;base64,")) + { + std::string mime; + Toolbox::DecodeDataUriScheme(mime, binary, utf8Value); + decoded = &binary; + } else { - if (FromDcmtkBridge::IsPrivateTag(tag) || - FromDcmtkBridge::IsUnknownTag(tag)) + Encoding encoding = GetEncoding(); + if (GetEncoding() != Encoding_Utf8) { - if (!element->putUint8Array((const Uint8*) value.c_str(), value.size()).good()) - { - throw OrthancException(ErrorCode_InternalError); - } - } - else - { - FillElementWithString(*element, tag, value); + binary = Toolbox::ConvertFromUtf8(utf8Value, encoding); + decoded = &binary; } } - /** * dcmodify will automatically correct 'Media Storage SOP Class * UID' and 'Media Storage SOP Instance UID' in the metaheader, if @@ -942,12 +668,44 @@ if (tag == DICOM_TAG_SOP_CLASS_UID) { - Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, value, DicomReplaceMode_InsertIfAbsent); + Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, *decoded, DicomReplaceMode_InsertIfAbsent); } if (tag == DICOM_TAG_SOP_INSTANCE_UID) { - Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, value, DicomReplaceMode_InsertIfAbsent); + Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, *decoded, DicomReplaceMode_InsertIfAbsent); + } + } + + + void ParsedDicomFile::Replace(const DicomTag& tag, + const std::string& utf8Value, + DicomReplaceMode mode) + { + std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag)); + FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, false, GetEncoding()); + ReplaceInternal(*pimpl_->file_->getDataset(), element, mode); + UpdateStorageUid(tag, utf8Value, false); + } + + + void ParsedDicomFile::Replace(const DicomTag& tag, + const Json::Value& value, + bool decodeBinaryTags, + DicomReplaceMode mode) + { + std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeBinaryTags, GetEncoding())); + ReplaceInternal(*pimpl_->file_->getDataset(), element, mode); + + if (tag == DICOM_TAG_SOP_CLASS_UID || + tag == DICOM_TAG_SOP_INSTANCE_UID) + { + if (value.type() != Json::stringValue) + { + throw OrthancException(ErrorCode_BadParameterType); + } + + UpdateStorageUid(tag, value.asString(), decodeBinaryTags); } } @@ -1005,7 +763,7 @@ return false; } - std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, pimpl_->encoding_)); + std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, GetEncoding())); if (v.get() == NULL) { @@ -1085,7 +843,6 @@ ParsedDicomFile::ParsedDicomFile() : pimpl_(new PImpl) { pimpl_->file_.reset(new DcmFileFormat); - pimpl_->encoding_ = Encoding_Ascii; Replace(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient)); Replace(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)); Replace(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series)); @@ -1115,7 +872,6 @@ pimpl_(new PImpl) { pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone())); - pimpl_->encoding_ = other.pimpl_->encoding_; // Create a new instance-level identifier Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); @@ -1156,7 +912,7 @@ } else { - LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file"; + LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file: " << mime; throw OrthancException(ErrorCode_NotImplemented); } } @@ -1345,7 +1101,7 @@ Encoding ParsedDicomFile::GetEncoding() const { - return pimpl_->encoding_; + return FromDcmtkBridge::DetectEncoding(*pimpl_->file_->getDataset()); } @@ -1358,24 +1114,15 @@ return; } - pimpl_->encoding_ = encoding; - std::string s = GetDicomSpecificCharacterSet(encoding); Replace(DICOM_TAG_SPECIFIC_CHARACTER_SET, s, DicomReplaceMode_InsertIfAbsent); } - void ParsedDicomFile::ToJson(Json::Value& target, bool simplify) + void ParsedDicomFile::ToJson(Json::Value& target, + DicomToJsonFormat format, + unsigned int maxStringLength) { - if (simplify) - { - Json::Value tmp; - FromDcmtkBridge::ToJson(tmp, *pimpl_->file_->getDataset()); - Toolbox::SimplifyTags(target, tmp); - } - else - { - FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset()); - } + FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset(), format, maxStringLength); }