Mercurial > hg > orthanc
diff OrthancServer/FromDcmtkBridge.cpp @ 948:e57e08ed510f dicom-rt
integration mainline -> dicom-rt
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 25 Jun 2014 13:57:05 +0200 |
parents | 8cfc6119a5bd 87791ebc1f50 |
children | 98d6ba37c7dc |
line wrap: on
line diff
--- a/OrthancServer/FromDcmtkBridge.cpp Wed Apr 16 16:15:58 2014 +0200 +++ b/OrthancServer/FromDcmtkBridge.cpp Wed Jun 25 13:57:05 2014 +0200 @@ -31,57 +31,19 @@ -/*========================================================================= - - This file is based on portions of the following project: - - Program: GDCM (Grassroots DICOM). A DICOM library - Module: http://gdcm.sourceforge.net/Copyright.html - -Copyright (c) 2006-2011 Mathieu Malaterre -Copyright (c) 1993-2005 CREATIS -(CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image) -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any - contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -=========================================================================*/ - +#include "PrecompiledHeadersServer.h" #ifndef NOMINMAX #define NOMINMAX #endif +#include "Internals/DicomImageDecoder.h" + #include "FromDcmtkBridge.h" - #include "ToDcmtkBridge.h" #include "../Core/Toolbox.h" #include "../Core/OrthancException.h" -#include "../Core/FileFormats/PngWriter.h" +#include "../Core/ImageFormats/PngWriter.h" #include "../Core/Uuid.h" #include "../Core/DicomFormat/DicomString.h" #include "../Core/DicomFormat/DicomNullValue.h" @@ -131,51 +93,8 @@ #include <dcmtk/dcmdata/dcostrmb.h> -static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; - - - namespace Orthanc { - void ParsedDicomFile::Setup(const char* buffer, size_t size) - { - DcmInputBufferStream is; - if (size > 0) - { - is.setBuffer(buffer, size); - } - is.setEos(); - - file_.reset(new DcmFileFormat); - file_->transferInit(); - if (!file_->read(is).good()) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - file_->loadAllDataIntoMemory(); - file_->transferEnd(); - } - - - static void SendPathValueForDictionary(RestApiOutput& output, - DcmItem& dicom) - { - Json::Value v = Json::arrayValue; - - for (unsigned long i = 0; i < dicom.card(); i++) - { - DcmElement* element = dicom.getElement(i); - if (element) - { - char buf[16]; - sprintf(buf, "%04x-%04x", element->getTag().getGTag(), element->getTag().getETag()); - v.append(buf); - } - } - - output.AnswerJson(v); - } - static inline uint16_t GetCharValue(char c) { if (c >= '0' && c <= '9') @@ -196,743 +115,6 @@ GetCharValue(c[3])); } - static void ParseTagAndGroup(DcmTagKey& key, - const std::string& tag) - { - DicomTag t = FromDcmtkBridge::ParseTag(tag); - key = DcmTagKey(t.GetGroup(), t.GetElement()); - } - - - static void SendSequence(RestApiOutput& output, - DcmSequenceOfItems& sequence) - { - // This element is a sequence - Json::Value v = Json::arrayValue; - - for (unsigned long i = 0; i < sequence.card(); i++) - { - v.append(boost::lexical_cast<std::string>(i)); - } - - output.AnswerJson(v); - } - - - static unsigned int GetPixelDataBlockCount(DcmPixelData& pixelData, - E_TransferSyntax transferSyntax) - { - DcmPixelSequence* pixelSequence = NULL; - if (pixelData.getEncapsulatedRepresentation - (transferSyntax, NULL, pixelSequence).good() && pixelSequence) - { - return pixelSequence->card(); - } - else - { - return 1; - } - } - - - static void AnswerDicomField(RestApiOutput& output, - DcmElement& element, - E_TransferSyntax transferSyntax) - { - // This element is nor a sequence, neither a pixel-data - std::string buffer; - buffer.resize(65536); - Uint32 length = element.getLength(transferSyntax); - Uint32 offset = 0; - - output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL); - - while (offset < length) - { - Uint32 nbytes; - if (length - offset < buffer.size()) - { - nbytes = length - offset; - } - else - { - nbytes = buffer.size(); - } - - OFCondition cond = element.getPartialValue(&buffer[0], offset, nbytes); - - if (cond.good()) - { - output.GetLowLevelOutput().Send(&buffer[0], nbytes); - offset += nbytes; - } - else - { - LOG(ERROR) << "Error while sending a DICOM field: " << cond.text(); - return; - } - } - - output.MarkLowLevelOutputDone(); - } - - - static bool AnswerPixelData(RestApiOutput& output, - DcmItem& dicom, - E_TransferSyntax transferSyntax, - const std::string* blockUri) - { - DcmTag k(DICOM_TAG_PIXEL_DATA.GetGroup(), - DICOM_TAG_PIXEL_DATA.GetElement()); - - DcmElement *element = NULL; - if (!dicom.findAndGetElement(k, element).good() || - element == NULL) - { - return false; - } - - try - { - DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); - if (blockUri == NULL) - { - // The user asks how many blocks are presents in this pixel data - unsigned int blocks = GetPixelDataBlockCount(pixelData, transferSyntax); - - Json::Value result(Json::arrayValue); - for (unsigned int i = 0; i < blocks; i++) - { - result.append(boost::lexical_cast<std::string>(i)); - } - - output.AnswerJson(result); - return true; - } - - - unsigned int block = boost::lexical_cast<unsigned int>(*blockUri); - - if (block < GetPixelDataBlockCount(pixelData, transferSyntax)) - { - DcmPixelSequence* pixelSequence = NULL; - if (pixelData.getEncapsulatedRepresentation - (transferSyntax, NULL, pixelSequence).good() && pixelSequence) - { - // This is the case for JPEG transfer syntaxes - if (block < pixelSequence->card()) - { - DcmPixelItem* pixelItem = NULL; - if (pixelSequence->getItem(pixelItem, block).good() && pixelItem) - { - if (pixelItem->getLength() == 0) - { - output.AnswerBuffer(NULL, 0, CONTENT_TYPE_OCTET_STREAM); - return true; - } - - Uint8* buffer = NULL; - if (pixelItem->getUint8Array(buffer).good() && buffer) - { - output.AnswerBuffer(buffer, pixelItem->getLength(), CONTENT_TYPE_OCTET_STREAM); - return true; - } - } - } - } - else - { - // This is the case for raw, uncompressed image buffers - assert(*blockUri == "0"); - AnswerDicomField(output, *element, transferSyntax); - } - } - } - catch (boost::bad_lexical_cast&) - { - // The URI entered by the user is not a number - } - catch (std::bad_cast&) - { - // This should never happen - } - - return false; - } - - - - static void SendPathValueForLeaf(RestApiOutput& output, - const std::string& tag, - DcmItem& dicom, - E_TransferSyntax transferSyntax) - { - DcmTagKey k; - ParseTagAndGroup(k, tag); - - DcmSequenceOfItems* sequence = NULL; - if (dicom.findAndGetSequence(k, sequence).good() && - sequence != NULL && - sequence->getVR() == EVR_SQ) - { - SendSequence(output, *sequence); - return; - } - - DcmElement* element = NULL; - if (dicom.findAndGetElement(k, element).good() && - element != NULL && - //element->getVR() != EVR_UNKNOWN && // This would forbid private tags - element->getVR() != EVR_SQ) - { - AnswerDicomField(output, *element, transferSyntax); - } - } - - void ParsedDicomFile::SendPathValue(RestApiOutput& output, - const UriComponents& uri) - { - DcmItem* dicom = file_->getDataset(); - E_TransferSyntax transferSyntax = file_->getDataset()->getOriginalXfer(); - - // Special case: Accessing the pixel data - if (uri.size() == 1 || - uri.size() == 2) - { - DcmTagKey tag; - ParseTagAndGroup(tag, uri[0]); - - if (tag.getGroup() == DICOM_TAG_PIXEL_DATA.GetGroup() && - tag.getElement() == DICOM_TAG_PIXEL_DATA.GetElement()) - { - AnswerPixelData(output, *dicom, transferSyntax, uri.size() == 1 ? NULL : &uri[1]); - return; - } - } - - // Go down in the tag hierarchy according to the URI - for (size_t pos = 0; pos < uri.size() / 2; pos++) - { - size_t index; - try - { - index = boost::lexical_cast<size_t>(uri[2 * pos + 1]); - } - catch (boost::bad_lexical_cast&) - { - return; - } - - DcmTagKey k; - DcmItem *child = NULL; - ParseTagAndGroup(k, uri[2 * pos]); - if (!dicom->findAndGetSequenceItem(k, child, index).good() || - child == NULL) - { - return; - } - - dicom = child; - } - - // We have reached the end of the URI - if (uri.size() % 2 == 0) - { - SendPathValueForDictionary(output, *dicom); - } - else - { - SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); - } - } - - - - - - 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()); - DcmElement* element = file_->getDataset()->remove(key); - if (element != NULL) - { - delete element; - } - } - - - - void ParsedDicomFile::RemovePrivateTags() - { - typedef std::list<DcmElement*> Tags; - - Tags privateTags; - - DcmDataset& dataset = *file_->getDataset(); - for (unsigned long i = 0; i < dataset.card(); i++) - { - DcmElement* element = dataset.getElement(i); - DcmTag tag(element->getTag()); - if (!strcmp("PrivateCreator", tag.getTagName()) || // TODO - This may change with future versions of DCMTK - tag.getPrivateCreator() != NULL) - { - privateTags.push_back(element); - } - } - - for (Tags::iterator it = privateTags.begin(); - it != privateTags.end(); ++it) - { - DcmElement* tmp = dataset.remove(*it); - if (tmp != NULL) - { - delete tmp; - } - } - } - - - - void ParsedDicomFile::Insert(const DicomTag& tag, - const std::string& value) - { - std::auto_ptr<DcmElement> element(CreateElementForTag(tag)); - FillElementWithString(*element, tag, value); - - if (!file_->getDataset()->insert(element.release(), false, false).good()) - { - // This field already exists - throw OrthancException(ErrorCode_InternalError); - } - } - - - void ParsedDicomFile::Replace(const DicomTag& tag, - const std::string& value, - DicomReplaceMode mode) - { - DcmTagKey key(tag.GetGroup(), tag.GetElement()); - DcmElement* element = NULL; - - if (!file_->getDataset()->findAndGetElement(key, element).good() || - element == NULL) - { - // This field does not exist, act wrt. the specified "mode" - switch (mode) - { - case DicomReplaceMode_InsertIfAbsent: - Insert(tag, value); - break; - - case DicomReplaceMode_ThrowIfAbsent: - throw OrthancException(ErrorCode_InexistentItem); - - case DicomReplaceMode_IgnoreIfAbsent: - return; - } - } - else - { - FillElementWithString(*element, tag, value); - } - - - /** - * dcmodify will automatically correct 'Media Storage SOP Class - * UID' and 'Media Storage SOP Instance UID' in the metaheader, if - * you make changes to the related tags in the dataset ('SOP Class - * UID' and 'SOP Instance UID') via insert or modify mode - * options. You can disable this behaviour by using the -nmu - * option. - **/ - if (tag == DICOM_TAG_SOP_CLASS_UID) - Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, value, DicomReplaceMode_InsertIfAbsent); - - if (tag == DICOM_TAG_SOP_INSTANCE_UID) - Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, value, DicomReplaceMode_InsertIfAbsent); - } - - - void ParsedDicomFile::Answer(RestApiOutput& output) - { - std::string serialized; - if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset())) - { - output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM); - } - } - - - - static bool GetTagValueInternal(std::string& value, - DcmItem& item, - const DicomTag& tag) - { - DcmTagKey k(tag.GetGroup(), tag.GetElement()); - DcmElement* element = NULL; - if (!item.findAndGetElement(k, element).good() || - element == NULL) - { - return false; - } - - std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element)); - - if (v.get() == NULL) - { - value = ""; - } - else - { - value = v->AsString(); - } - - return true; - } - - - bool ParsedDicomFile::GetTagValue(std::string& value, - const DicomTag& tag) - { - DcmDataset& dataset = *file_->getDataset(); - return GetTagValueInternal(value, dataset, tag); - } - - - - bool ParsedDicomFile::GetTagValue(std::string& value, - const SequencePath& path, - const DicomTag& tag) - { - if (path.size() == 0) - { - return GetTagValue(value, tag); - } - - DcmItem* current = file_->getDataset(); - assert(current != NULL); - - for (SequencePath::const_iterator it = path.begin(); it != path.end(); it++) - { - DcmTagKey k(it->first.GetGroup(), it->first.GetElement()); - - DcmSequenceOfItems* sequence = NULL; - if (!current->findAndGetSequence(k, sequence).good() || - sequence == NULL || - sequence->getVR() != EVR_SQ) - { - return false; - } - - if (it->second < 0 || it->second > sequence->card()) - { - return false; - } - - current = sequence->getItem(it->second); - - if (current == NULL) - { - return false; - } - } - - return GetTagValueInternal(value, *current, tag); - } - - - - DicomInstanceHasher ParsedDicomFile::GetHasher() - { - std::string patientId, studyUid, seriesUid, instanceUid; - - if (!GetTagValue(patientId, DICOM_TAG_PATIENT_ID) || - !GetTagValue(studyUid, DICOM_TAG_STUDY_INSTANCE_UID) || - !GetTagValue(seriesUid, DICOM_TAG_SERIES_INSTANCE_UID) || - !GetTagValue(instanceUid, DICOM_TAG_SOP_INSTANCE_UID)) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - return DicomInstanceHasher(patientId, studyUid, seriesUid, instanceUid); - } - - void FromDcmtkBridge::Convert(DicomMap& target, DcmDataset& dataset) { target.Clear(); @@ -1246,345 +428,6 @@ } - static void ExtractPngImageColorPreview(std::string& result, - DicomIntegerPixelAccessor& accessor) - { - assert(accessor.GetChannelCount() == 3); - PngWriter w; - - std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight() * 3, 0); - uint8_t* pixel = &image[0]; - - for (unsigned int y = 0; y < accessor.GetHeight(); y++) - { - for (unsigned int x = 0; x < accessor.GetWidth(); x++) - { - for (unsigned int c = 0; c < 3; c++, pixel++) - { - int32_t v = accessor.GetValue(x, y, c); - if (v < 0) - *pixel = 0; - else if (v > 255) - *pixel = 255; - else - *pixel = v; - } - } - } - - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth() * 3, PixelFormat_RGB24, &image[0]); - } - - - static void ExtractPngImageGrayscalePreview(std::string& result, - DicomIntegerPixelAccessor& accessor) - { - assert(accessor.GetChannelCount() == 1); - PngWriter w; - - int32_t min, max; - accessor.GetExtremeValues(min, max); - - std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight(), 0); - if (min != max) - { - uint8_t* pixel = &image[0]; - for (unsigned int y = 0; y < accessor.GetHeight(); y++) - { - for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++) - { - int32_t v = accessor.GetValue(x, y); - *pixel = static_cast<uint8_t>( - boost::math::lround(static_cast<float>(v - min) / - static_cast<float>(max - min) * 255.0f)); - } - } - } - - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth(), PixelFormat_Grayscale8, &image[0]); - } - - - template <typename T> - static void ExtractPngImageTruncate(std::string& result, - DicomIntegerPixelAccessor& accessor, - PixelFormat format) - { - assert(accessor.GetChannelCount() == 1); - - PngWriter w; - - std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0); - T* pixel = &image[0]; - for (unsigned int y = 0; y < accessor.GetHeight(); y++) - { - for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++) - { - int32_t v = accessor.GetValue(x, y); - if (v < static_cast<int32_t>(std::numeric_limits<T>::min())) - *pixel = std::numeric_limits<T>::min(); - else if (v > static_cast<int32_t>(std::numeric_limits<T>::max())) - *pixel = std::numeric_limits<T>::max(); - else - *pixel = static_cast<T>(v); - } - } - - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth() * sizeof(T), format, &image[0]); - } - - - static bool DecodePsmctRle1(std::string& output, - DcmDataset& dataset) - { - static const DicomTag tagContent(0x07a1, 0x100a); - static const DicomTag tagCompressionType(0x07a1, 0x1011); - - DcmElement* e; - char* c; - - // Check whether the DICOM instance contains an image encoded with - // the PMSCT_RLE1 scheme. - if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(tagCompressionType), e).good() || - e == NULL || - !e->isaString() || - !e->getString(c).good() || - c == NULL || - strcmp("PMSCT_RLE1", c)) - { - return false; - } - - // OK, this is a custom RLE encoding from Philips. Get the pixel - // data from the appropriate private DICOM tag. - Uint8* pixData = NULL; - if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(tagContent), e).good() || - e == NULL || - e->getUint8Array(pixData) != EC_Normal) - { - return false; - } - - // The "unsigned" below IS VERY IMPORTANT - const uint8_t* inbuffer = reinterpret_cast<const uint8_t*>(pixData); - const size_t length = e->getLength(); - - /** - * The code below is an adaptation of a sample code for GDCM by - * Mathieu Malaterre (under a BSD license). - * http://gdcm.sourceforge.net/html/rle2img_8cxx-example.html - **/ - - // RLE pass - std::vector<uint8_t> temp; - temp.reserve(length); - for (size_t i = 0; i < length; i++) - { - if (inbuffer[i] == 0xa5) - { - temp.push_back(inbuffer[i+2]); - for (uint8_t repeat = inbuffer[i + 1]; repeat != 0; repeat--) - { - temp.push_back(inbuffer[i+2]); - } - i += 2; - } - else - { - temp.push_back(inbuffer[i]); - } - } - - // Delta encoding pass - uint16_t delta = 0; - output.clear(); - output.reserve(temp.size()); - for (size_t i = 0; i < temp.size(); i++) - { - uint16_t value; - - if (temp[i] == 0x5a) - { - uint16_t v1 = temp[i + 1]; - uint16_t v2 = temp[i + 2]; - value = (v2 << 8) + v1; - i += 2; - } - else - { - value = delta + (int8_t) temp[i]; - } - - output.push_back(value & 0xff); - output.push_back(value >> 8); - delta = value; - } - - if (output.size() % 2) - { - output.resize(output.size() - 1); - } - - return true; - } - - - void FromDcmtkBridge::ExtractPngImage(std::string& result, - DcmDataset& dataset, - unsigned int frame, - ImageExtractionMode mode) - { - // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data - - std::auto_ptr<DicomIntegerPixelAccessor> accessor; - - DicomMap m; - FromDcmtkBridge::Convert(m, dataset); - - std::string privateContent; - - DcmElement* e; - if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() && - e != NULL) - { - Uint8* pixData = NULL; - if (e->getUint8Array(pixData) == EC_Normal) - { - accessor.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength())); - accessor->SetCurrentFrame(frame); - } - } - else if (DecodePsmctRle1(privateContent, dataset)) - { - LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded"; - Uint8* pixData = NULL; - if (privateContent.size() > 0) - pixData = reinterpret_cast<Uint8*>(&privateContent[0]); - accessor.reset(new DicomIntegerPixelAccessor(m, pixData, privateContent.size())); - accessor->SetCurrentFrame(frame); - } - - if (accessor.get() == NULL) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - PixelFormat format; - bool supported = false; - - if (accessor->GetChannelCount() == 1) - { - switch (mode) - { - case ImageExtractionMode_Preview: - supported = true; - format = PixelFormat_Grayscale8; - break; - - case ImageExtractionMode_UInt8: - supported = true; - format = PixelFormat_Grayscale8; - break; - - case ImageExtractionMode_UInt16: - supported = true; - format = PixelFormat_Grayscale16; - break; - - case ImageExtractionMode_Int16: - supported = true; - format = PixelFormat_SignedGrayscale16; - break; - - default: - supported = false; - break; - } - } - else if (accessor->GetChannelCount() == 3) - { - switch (mode) - { - case ImageExtractionMode_Preview: - supported = true; - format = PixelFormat_RGB24; - break; - - default: - supported = false; - break; - } - } - - if (!supported) - { - throw OrthancException(ErrorCode_NotImplemented); - } - - if (accessor.get() == NULL || - accessor->GetWidth() == 0 || - accessor->GetHeight() == 0) - { - PngWriter w; - w.WriteToMemory(result, 0, 0, 0, format, NULL); - } - else - { - switch (mode) - { - case ImageExtractionMode_Preview: - if (format == PixelFormat_Grayscale8) - ExtractPngImageGrayscalePreview(result, *accessor); - else - ExtractPngImageColorPreview(result, *accessor); - break; - - case ImageExtractionMode_UInt8: - ExtractPngImageTruncate<uint8_t>(result, *accessor, format); - break; - - case ImageExtractionMode_UInt16: - ExtractPngImageTruncate<uint16_t>(result, *accessor, format); - break; - - case ImageExtractionMode_Int16: - ExtractPngImageTruncate<int16_t>(result, *accessor, format); - break; - - default: - throw OrthancException(ErrorCode_NotImplemented); - } - } - } - - - void FromDcmtkBridge::ExtractPngImage(std::string& result, - const std::string& dicomContent, - unsigned int frame, - ImageExtractionMode mode) - { - DcmInputBufferStream is; - if (dicomContent.size() > 0) - { - is.setBuffer(&dicomContent[0], dicomContent.size()); - } - is.setEos(); - - DcmFileFormat dicom; - if (dicom.read(is).good()) - { - ExtractPngImage(result, *dicom.getDataset(), frame, mode); - } - else - { - throw OrthancException(ErrorCode_BadFileFormat); - } - } - - std::string FromDcmtkBridge::GetName(const DicomTag& t) { @@ -1703,25 +546,25 @@ } - std::string FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel level) + std::string FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType level) { char uid[100]; switch (level) { - case DicomRootLevel_Patient: + case ResourceType_Patient: // The "PatientID" field is of type LO (Long String), 64 // Bytes Maximum. An UUID is of length 36, thus it can be used // as a random PatientID. return Toolbox::GenerateUuid(); - case DicomRootLevel_Instance: + case ResourceType_Instance: return dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT); - case DicomRootLevel_Series: + case ResourceType_Series: return dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT); - case DicomRootLevel_Study: + case ResourceType_Study: return dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT); default: