Mercurial > hg > orthanc
changeset 1924:6c73df12ca51
New URI: "/instances/.../frames/.../raw" to access the raw frames (bypass image decoding)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 07 Mar 2016 17:43:20 +0100 |
parents | 6ac7f31fc543 |
children | 56276bad7e42 |
files | CMakeLists.txt Core/DicomFormat/DicomImageInformation.cpp Core/DicomFormat/DicomImageInformation.h Core/DicomFormat/DicomIntegerPixelAccessor.cpp NEWS OrthancServer/FromDcmtkBridge.cpp OrthancServer/FromDcmtkBridge.h OrthancServer/Internals/DicomFrameIndex.cpp OrthancServer/Internals/DicomFrameIndex.h OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/DicomImageDecoder.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/ParsedDicomFile.h |
diffstat | 14 files changed, 629 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Mon Mar 07 08:29:22 2016 +0100 +++ b/CMakeLists.txt Mon Mar 07 17:43:20 2016 +0100 @@ -163,6 +163,7 @@ OrthancServer/ExportedResource.cpp OrthancServer/FromDcmtkBridge.cpp OrthancServer/Internals/CommandDispatcher.cpp + OrthancServer/Internals/DicomFrameIndex.cpp OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/FindScp.cpp OrthancServer/Internals/MoveScp.cpp
--- a/Core/DicomFormat/DicomImageInformation.cpp Mon Mar 07 08:29:22 2016 +0100 +++ b/Core/DicomFormat/DicomImageInformation.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -260,4 +260,13 @@ return false; } + + + size_t DicomImageInformation::GetFrameSize() const + { + return (GetHeight() * + GetWidth() * + GetBytesPerValue() * + GetChannelCount()); + } }
--- a/Core/DicomFormat/DicomImageInformation.h Mon Mar 07 08:29:22 2016 +0100 +++ b/Core/DicomFormat/DicomImageInformation.h Mon Mar 07 17:43:20 2016 +0100 @@ -121,5 +121,7 @@ bool ExtractPixelFormat(PixelFormat& format, bool ignorePhotometricInterpretation) const; + + size_t GetFrameSize() const; }; }
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Mon Mar 07 08:29:22 2016 +0100 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -54,8 +54,7 @@ size_(size) { frame_ = 0; - frameOffset_ = (information_.GetHeight() * information_.GetWidth() * - information_.GetBytesPerValue() * information_.GetChannelCount()); + frameOffset_ = information_.GetFrameSize(); if (information_.GetNumberOfFrames() * frameOffset_ > size) {
--- a/NEWS Mon Mar 07 08:29:22 2016 +0100 +++ b/NEWS Mon Mar 07 17:43:20 2016 +0100 @@ -1,6 +1,8 @@ Pending changes in the mainline =============================== + +* New URI: "/instances/.../frames/.../raw" to access the raw frames (bypass image decoding) * Support of optional tags for counting resources in C-Find: 0008-0061, 0008-0062, 0020-1200, 0020-1202, 0020-1204, 0020-1206, 0020-1208, 0020-1209 * Support of Move Originator Message ID (0000,1031) in C-Store responses driven by C-Move
--- a/OrthancServer/FromDcmtkBridge.cpp Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/FromDcmtkBridge.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -1566,4 +1566,26 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } + + + DcmPixelSequence* FromDcmtkBridge::GetPixelSequence(DcmDataset& dataset) + { + DcmElement *element = NULL; + if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), element).good()) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); + DcmPixelSequence* pixelSequence = NULL; + if (!pixelData.getEncapsulatedRepresentation + (dataset.getOriginalXfer(), NULL, pixelSequence).good()) + { + return NULL; + } + else + { + return pixelSequence; + } + } }
--- a/OrthancServer/FromDcmtkBridge.h Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/FromDcmtkBridge.h Mon Mar 07 17:43:20 2016 +0100 @@ -38,6 +38,7 @@ #include <dcmtk/dcmdata/dcdatset.h> #include <dcmtk/dcmdata/dcmetinf.h> +#include <dcmtk/dcmdata/dcpixseq.h> #include <json/json.h> namespace Orthanc @@ -139,5 +140,7 @@ Encoding dicomEncoding); static DcmEVR ParseValueRepresentation(const std::string& s); + + static DcmPixelSequence* GetPixelSequence(DcmDataset& dataset); }; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Internals/DicomFrameIndex.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -0,0 +1,411 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 "../PrecompiledHeadersServer.h" +#include "DicomFrameIndex.h" + +#include "../../Core/OrthancException.h" +#include "../../Core/DicomFormat/DicomImageInformation.h" +#include "../FromDcmtkBridge.h" +#include "DicomImageDecoder.h" + +#include <boost/lexical_cast.hpp> + +#include <dcmtk/dcmdata/dcdeftag.h> +#include <dcmtk/dcmdata/dcpxitem.h> +#include <dcmtk/dcmdata/dcpixseq.h> + +#if defined(_WIN32) // Windows machines are always little-endian +# define le32toh(x) x +#elif defined(__APPLE__) +# include <libkern/OSByteOrder.h> +# define le32toh(x) OSSwapLittleToHostInt32(x) +#else +# include <endian.h> +#endif + + +namespace Orthanc +{ + class DicomFrameIndex::FragmentIndex : public DicomFrameIndex::IIndex + { + private: + DcmPixelSequence* pixelSequence_; + std::vector<DcmPixelItem*> startFragment_; + std::vector<unsigned int> countFragments_; + std::vector<unsigned int> frameSize_; + + void GetOffsetTable(std::vector<uint32_t>& table) + { + DcmPixelItem* item = NULL; + if (!pixelSequence_->getItem(item, 0).good() || + item == NULL) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + uint32_t length = item->getLength(); + if (length == 0) + { + table.clear(); + return; + } + + if (length % 4 != 0) + { + // Error: Each fragment is index with 4 bytes (uint32_t) + throw OrthancException(ErrorCode_BadFileFormat); + } + + uint8_t* content = NULL; + if (!item->getUint8Array(content).good() || + content == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + table.resize(length / 4); + + // The offset table is always in little endian in the DICOM + // file. Swap it to host endianness if needed. + const uint32_t* offset = reinterpret_cast<const uint32_t*>(content); + for (size_t i = 0; i < table.size(); i++, offset++) + { + table[i] = le32toh(*offset); + } + } + + + public: + FragmentIndex(DcmPixelSequence* pixelSequence, + unsigned int countFrames) : + pixelSequence_(pixelSequence) + { + assert(pixelSequence != NULL); + + startFragment_.resize(countFrames); + countFragments_.resize(countFrames); + frameSize_.resize(countFrames); + + // The first fragment corresponds to the offset table + unsigned int countFragments = static_cast<unsigned int>(pixelSequence_->card()); + if (countFragments < countFrames + 1) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + if (countFragments == countFrames + 1) + { + // Simple case: There is one fragment per frame. + + DcmObject* fragment = pixelSequence_->nextInContainer(NULL); // Skip the offset table + if (fragment == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + for (unsigned int i = 0; i < countFrames; i++) + { + fragment = pixelSequence_->nextInContainer(fragment); + startFragment_[i] = dynamic_cast<DcmPixelItem*>(fragment); + frameSize_[i] = fragment->getLength(); + countFragments_[i] = 1; + } + + return; + } + + // Parse the offset table + std::vector<uint32_t> offsetOfFrame; + GetOffsetTable(offsetOfFrame); + + if (offsetOfFrame.size() != countFrames || + offsetOfFrame[0] != 0) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + + // Loop over the fragments (ignoring the offset table). This is + // an alternative, faster implementation to DCMTK's + // "DcmCodec::determineStartFragment()". + DcmObject* fragment = pixelSequence_->nextInContainer(NULL); + if (fragment == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + fragment = pixelSequence_->nextInContainer(fragment); // Skip the offset table + if (fragment == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + uint32_t offset = 0; + unsigned int currentFrame = 0; + startFragment_[0] = dynamic_cast<DcmPixelItem*>(fragment); + + unsigned int currentFragment = 1; + while (fragment != NULL) + { + if (currentFrame + 1 < countFrames && + offset == offsetOfFrame[currentFrame + 1]) + { + currentFrame += 1; + startFragment_[currentFrame] = dynamic_cast<DcmPixelItem*>(fragment); + } + + frameSize_[currentFrame] += fragment->getLength(); + countFragments_[currentFrame]++; + + // 8 bytes = overhead for the item tag and length field + offset += fragment->getLength() + 8; + + currentFragment++; + fragment = pixelSequence_->nextInContainer(fragment); + } + + if (currentFragment != countFragments || + currentFrame + 1 != countFrames || + fragment != NULL) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + assert(startFragment_.size() == countFragments_.size() && + startFragment_.size() == frameSize_.size()); + } + + + virtual void GetRawFrame(std::string& frame, + unsigned int index) const + { + if (index >= startFragment_.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + frame.resize(frameSize_[index]); + if (frame.size() == 0) + { + return; + } + + uint8_t* target = reinterpret_cast<uint8_t*>(&frame[0]); + + size_t offset = 0; + DcmPixelItem* fragment = startFragment_[index]; + for (unsigned int i = 0; i < countFragments_[index]; i++) + { + uint8_t* content = NULL; + if (!fragment->getUint8Array(content).good() || + content == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + assert(offset + fragment->getLength() <= frame.size()); + + memcpy(target + offset, content, fragment->getLength()); + offset += fragment->getLength(); + + fragment = dynamic_cast<DcmPixelItem*>(pixelSequence_->nextInContainer(fragment)); + } + } + }; + + + + class DicomFrameIndex::UncompressedIndex : public DicomFrameIndex::IIndex + { + private: + uint8_t* pixelData_; + size_t frameSize_; + + public: + UncompressedIndex(DcmDataset& dataset, + unsigned int countFrames, + size_t frameSize) : + pixelData_(NULL), + frameSize_(frameSize) + { + size_t size = 0; + + DcmElement* e; + if (dataset.findAndGetElement(DCM_PixelData, e).good() && + e != NULL) + { + size = e->getLength(); + + if (size > 0) + { + pixelData_ = NULL; + if (!e->getUint8Array(pixelData_).good() || + pixelData_ == NULL) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + } + + if (size < frameSize_ * countFrames) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + virtual void GetRawFrame(std::string& frame, + unsigned int index) const + { + frame.resize(frameSize_); + if (frameSize_ > 0) + { + memcpy(&frame[0], pixelData_ + index * frameSize_, frameSize_); + } + } + }; + + + class DicomFrameIndex::PsmctRle1Index : public DicomFrameIndex::IIndex + { + private: + std::string pixelData_; + size_t frameSize_; + + public: + PsmctRle1Index(DcmDataset& dataset, + unsigned int countFrames, + size_t frameSize) : + frameSize_(frameSize) + { + if (!DicomImageDecoder::DecodePsmctRle1(pixelData_, dataset) || + pixelData_.size() < frameSize * countFrames) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + virtual void GetRawFrame(std::string& frame, + unsigned int index) const + { + frame.resize(frameSize_); + if (frameSize_ > 0) + { + memcpy(&frame[0], reinterpret_cast<const uint8_t*>(&pixelData_[0]) + index * frameSize_, frameSize_); + } + } + }; + + + + + unsigned int DicomFrameIndex::GetFramesCount(DcmDataset& dataset) + { + const char* tmp = NULL; + if (!dataset.findAndGetString(DCM_NumberOfFrames, tmp).good() || + tmp == NULL) + { + return 1; + } + + int count = -1; + try + { + count = boost::lexical_cast<int>(tmp); + } + catch (boost::bad_lexical_cast&) + { + } + + if (count < 0) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + else + { + return count; + } + } + + + DicomFrameIndex::DicomFrameIndex(DcmDataset& dataset) + { + countFrames_ = GetFramesCount(dataset); + if (countFrames_ == 0) + { + // The image has no frame. No index is to be built. + return; + } + + // Test whether this image is composed of a sequence of fragments + DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset); + if (pixelSequence != NULL) + { + index_.reset(new FragmentIndex(pixelSequence, countFrames_)); + return; + } + + // Extract information about the image structure + DicomMap tags; + FromDcmtkBridge::Convert(tags, dataset); + + DicomImageInformation information(tags); + + // Access to the raw pixel data + if (DicomImageDecoder::IsPsmctRle1(dataset)) + { + index_.reset(new PsmctRle1Index(dataset, countFrames_, information.GetFrameSize())); + } + else + { + index_.reset(new UncompressedIndex(dataset, countFrames_, information.GetFrameSize())); + } + } + + + void DicomFrameIndex::GetRawFrame(std::string& frame, + unsigned int index) const + { + if (index >= countFrames_) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else if (index_.get() != NULL) + { + return index_->GetRawFrame(frame, index); + } + else + { + frame.clear(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Internals/DicomFrameIndex.h Mon Mar 07 17:43:20 2016 +0100 @@ -0,0 +1,77 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 <dcmtk/dcmdata/dcdatset.h> +#include <vector> +#include <stdint.h> +#include <boost/noncopyable.hpp> +#include <memory> + +namespace Orthanc +{ + class DicomFrameIndex + { + private: + class IIndex : public boost::noncopyable + { + public: + virtual ~IIndex() + { + } + + virtual void GetRawFrame(std::string& frame, + unsigned int index) const = 0; + }; + + class FragmentIndex; + class UncompressedIndex; + class PsmctRle1Index; + + std::auto_ptr<IIndex> index_; + unsigned int countFrames_; + + public: + DicomFrameIndex(DcmDataset& dataset); + + unsigned int GetFramesCount() const + { + return countFrames_; + } + + void GetRawFrame(std::string& frame, + unsigned int index) const; + + static unsigned int GetFramesCount(DcmDataset& dataset); + }; +}
--- a/OrthancServer/Internals/DicomImageDecoder.cpp Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -121,7 +121,7 @@ static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011); - static bool IsPsmctRle1(DcmDataset& dataset) + bool DicomImageDecoder::IsPsmctRle1(DcmDataset& dataset) { DcmElement* e; char* c; @@ -144,8 +144,8 @@ } - static bool DecodePsmctRle1(std::string& output, - DcmDataset& dataset) + bool DicomImageDecoder::DecodePsmctRle1(std::string& output, + DcmDataset& dataset) { // Check whether the DICOM instance contains an image encoded with // the PMSCT_RLE1 scheme. @@ -459,38 +459,24 @@ } - static DcmPixelSequence* GetPixelSequence(DcmDataset& dataset) - { - DcmElement *element = NULL; - if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), element).good()) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); - DcmPixelSequence* pixelSequence = NULL; - if (!pixelData.getEncapsulatedRepresentation - (dataset.getOriginalXfer(), NULL, pixelSequence).good() || - pixelSequence == NULL) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - return pixelSequence; - } - - ImageAccessor* DicomImageDecoder::ApplyCodec(const DcmCodec& codec, const DcmCodecParameter& parameters, DcmDataset& dataset, unsigned int frame) { + DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset); + if (pixelSequence == NULL) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + std::auto_ptr<ImageAccessor> target(CreateImage(dataset, true)); Uint32 startFragment = 0; // Default OFString decompressedColorModel; // Out DJ_RPLossless representationParameter; - OFCondition c = codec.decodeFrame(&representationParameter, GetPixelSequence(dataset), ¶meters, + OFCondition c = codec.decodeFrame(&representationParameter, + pixelSequence, ¶meters, &dataset, frame, startFragment, target->GetBuffer(), target->GetSize(), decompressedColorModel);
--- a/OrthancServer/Internals/DicomImageDecoder.h Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/Internals/DicomImageDecoder.h Mon Mar 07 17:43:20 2016 +0100 @@ -57,8 +57,6 @@ static ImageAccessor* DecodeUncompressedImage(DcmDataset& dataset, unsigned int frame); - static bool IsPsmctRle1(DcmDataset& dataset); - static ImageAccessor* ApplyCodec(const DcmCodec& codec, const DcmCodecParameter& parameters, DcmDataset& dataset, @@ -74,6 +72,11 @@ ImageExtractionMode mode); public: + static bool IsPsmctRle1(DcmDataset& dataset); + + static bool DecodePsmctRle1(std::string& output, + DcmDataset& dataset); + static ImageAccessor *Decode(ParsedDicomFile& dicom, unsigned int frame);
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -466,6 +466,35 @@ + static void GetRawFrame(RestApiGetCall& call) + { + ServerContext& context = OrthancRestApi::GetContext(call); + + std::string frameId = call.GetUriComponent("frame", "0"); + + unsigned int frame; + try + { + frame = boost::lexical_cast<unsigned int>(frameId); + } + catch (boost::bad_lexical_cast) + { + return; + } + + std::string publicId = call.GetUriComponent("id", ""); + std::string raw, mime; + + { + ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), publicId); + locker.GetDicom().GetRawFrame(raw, mime, frame); + } + + call.GetOutput().AnswerBuffer(raw, mime); + } + + + static void GetResourceStatistics(RestApiGetCall& call) { std::string publicId = call.GetUriComponent("id", ""); @@ -1328,6 +1357,7 @@ Register("/instances/{id}/frames/{frame}/image-uint16", GetImage<ImageExtractionMode_UInt16>); Register("/instances/{id}/frames/{frame}/image-int16", GetImage<ImageExtractionMode_Int16>); Register("/instances/{id}/frames/{frame}/matlab", GetMatlabImage); + Register("/instances/{id}/frames/{frame}/raw", GetRawFrame); Register("/instances/{id}/pdf", ExtractPdf); Register("/instances/{id}/preview", GetImage<ImageExtractionMode_Preview>); Register("/instances/{id}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
--- a/OrthancServer/ParsedDicomFile.cpp Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/ParsedDicomFile.cpp Mon Mar 07 17:43:20 2016 +0100 @@ -84,6 +84,7 @@ #include "ServerToolbox.h" #include "FromDcmtkBridge.h" #include "ToDcmtkBridge.h" +#include "Internals/DicomFrameIndex.h" #include "../Core/Images/JpegReader.h" #include "../Core/Images/PngReader.h" #include "../Core/Logging.h" @@ -145,6 +146,7 @@ struct ParsedDicomFile::PImpl { std::auto_ptr<DcmFileFormat> file_; + std::auto_ptr<DicomFrameIndex> frameIndex_; }; @@ -516,6 +518,8 @@ void ParsedDicomFile::Remove(const DicomTag& tag) { + InvalidateCache(); + DcmTagKey key(tag.GetGroup(), tag.GetElement()); DcmElement* element = pimpl_->file_->getDataset()->remove(key); if (element != NULL) @@ -528,6 +532,8 @@ void ParsedDicomFile::RemovePrivateTagsInternal(const std::set<DicomTag>* toKeep) { + InvalidateCache(); + DcmDataset& dataset = *pimpl_->file_->getDataset(); // Loop over the dataset to detect its private tags @@ -591,6 +597,8 @@ const Json::Value& value, bool decodeDataUriScheme) { + InvalidateCache(); + std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding())); InsertInternal(*pimpl_->file_->getDataset(), element.release()); } @@ -679,6 +687,8 @@ const std::string& utf8Value, DicomReplaceMode mode) { + InvalidateCache(); + std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag)); FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, false, GetEncoding()); ReplaceInternal(*pimpl_->file_->getDataset(), element, mode); @@ -691,6 +701,8 @@ bool decodeDataUriScheme, DicomReplaceMode mode) { + InvalidateCache(); + std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding())); ReplaceInternal(*pimpl_->file_->getDataset(), element, mode); @@ -921,6 +933,8 @@ void ParsedDicomFile::EmbedImage(const std::string& mime, const std::string& content) { + InvalidateCache(); + if (mime == "image/png") { PngReader reader; @@ -1228,4 +1242,39 @@ return result.release(); } + + + void ParsedDicomFile::GetRawFrame(std::string& target, + std::string& mime, + unsigned int frameId) + { + if (pimpl_->frameIndex_.get() == NULL) + { + pimpl_->frameIndex_.reset(new DicomFrameIndex(*pimpl_->file_->getDataset())); + } + + pimpl_->frameIndex_->GetRawFrame(target, frameId); + + E_TransferSyntax transferSyntax = pimpl_->file_->getDataset()->getOriginalXfer(); + switch (transferSyntax) + { + case EXS_JPEGProcess1TransferSyntax: + mime = "image/jpeg"; + break; + + case EXS_JPEG2000LosslessOnly: + case EXS_JPEG2000: + mime = "image/jp2"; + + default: + mime = "application/octet-stream"; + break; + } + } + + + void ParsedDicomFile::InvalidateCache() + { + pimpl_->frameIndex_.reset(NULL); + } }
--- a/OrthancServer/ParsedDicomFile.h Mon Mar 07 08:29:22 2016 +0100 +++ b/OrthancServer/ParsedDicomFile.h Mon Mar 07 17:43:20 2016 +0100 @@ -60,6 +60,8 @@ const std::string& value, bool decodeDataUriScheme); + void InvalidateCache(); + public: ParsedDicomFile(bool createIdentifiers); // Create a minimal DICOM instance @@ -146,6 +148,10 @@ void Convert(DicomMap& tags); + void GetRawFrame(std::string& target, // OUT + std::string& mime, // OUT + unsigned int frameId); // IN + static ParsedDicomFile* CreateFromJson(const Json::Value& value, DicomFromJsonFlags flags); };