Mercurial > hg > orthanc
changeset 53:293038baf8f1
access to multi-frame images
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 10 Sep 2012 15:17:02 +0200 |
parents | e0cac5540668 |
children | 42a449dac415 |
files | Core/DicomFormat/DicomMap.cpp NEWS PalanthirServer/DicomIntegerPixelAccessor.cpp PalanthirServer/DicomIntegerPixelAccessor.h PalanthirServer/FromDcmtkBridge.cpp PalanthirServer/FromDcmtkBridge.h PalanthirServer/PalanthirRestApi.cpp |
diffstat | 7 files changed, 125 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/Core/DicomFormat/DicomMap.cpp Fri Sep 07 12:53:38 2012 +0200 +++ b/Core/DicomFormat/DicomMap.cpp Mon Sep 10 15:17:02 2012 +0200 @@ -76,6 +76,7 @@ DicomTag(0x0008, 0x0018), // SOPInstanceUID DicomTag(0x0020, 0x0012), // AcquisitionNumber DicomTag(0x0020, 0x0013), // InstanceNumber + DicomTag(0x0028, 0x0008), // NumberOfFrames DicomTag(0x0054, 0x1330) // ImageIndex };
--- a/NEWS Fri Sep 07 12:53:38 2012 +0200 +++ b/NEWS Mon Sep 10 15:17:02 2012 +0200 @@ -2,6 +2,7 @@ =============== * Renaming to "Palanthir" +* Access to multi-frame images * Access to the raw PNG images (in 8bpp and 16bpp) * Security: Support of SSL, HTTP Basic Authentication and interdiction of remote access
--- a/PalanthirServer/DicomIntegerPixelAccessor.cpp Fri Sep 07 12:53:38 2012 +0200 +++ b/PalanthirServer/DicomIntegerPixelAccessor.cpp Mon Sep 10 15:17:02 2012 +0200 @@ -57,8 +57,24 @@ throw PalanthirException(ErrorCode_NotImplemented); } - if (bitsAllocated != 8 && bitsAllocated != 16 && - bitsAllocated != 24 && bitsAllocated != 32) + frame_ = 0; + try + { + numberOfFrames_ = boost::lexical_cast<unsigned int>(FromDcmtkBridge::GetValue(values, "NumberOfFrames").AsString()); + } + catch (PalanthirException) + { + // If the tag "NumberOfFrames" is absent, assume there is a single frame + numberOfFrames_ = 1; + } + catch (boost::bad_lexical_cast) + { + throw PalanthirException(ErrorCode_NotImplemented); + } + + if ((bitsAllocated != 8 && bitsAllocated != 16 && + bitsAllocated != 24 && bitsAllocated != 32) || + numberOfFrames_ == 0) { throw PalanthirException(ErrorCode_NotImplemented); } @@ -69,19 +85,19 @@ // Not available, as the accessor internally uses int32_t values throw PalanthirException(ErrorCode_NotImplemented); } - + if (samplesPerPixel_ != 1) { throw PalanthirException(ErrorCode_NotImplemented); } - if (width_ * height_ * bitsAllocated / 8 != size) + if (width_ * height_ * bitsAllocated / 8 * numberOfFrames_ != size) { throw PalanthirException(ErrorCode_NotImplemented); } - /*printf("%d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated, - bitsStored, highBit, pixelRepresentation);*/ + /*printf("%d %d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated, + bitsStored, highBit, pixelRepresentation, numberOfFrames_);*/ bytesPerPixel_ = bitsAllocated / 8; shift_ = highBit + 1 - bitsStored; @@ -96,6 +112,9 @@ mask_ = (1 << bitsStored) - 1; signMask_ = 0; } + + rowOffset_ = width_ * bytesPerPixel_; + frameOffset_ = height_ * width_ * bytesPerPixel_; } @@ -129,7 +148,8 @@ { assert(x < width_ && y < height_); - const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + (y * width_ + x) * bytesPerPixel_; + const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + + y * rowOffset_ + x * bytesPerPixel_ + frame_ * frameOffset_; int32_t v; v = pixel[0]; @@ -151,4 +171,16 @@ return v; } + + + void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame) + { + if (frame >= numberOfFrames_) + { + throw PalanthirException(ErrorCode_ParameterOutOfRange); + } + + frame_ = frame; + } + }
--- a/PalanthirServer/DicomIntegerPixelAccessor.h Fri Sep 07 12:53:38 2012 +0200 +++ b/PalanthirServer/DicomIntegerPixelAccessor.h Mon Sep 10 15:17:02 2012 +0200 @@ -32,6 +32,7 @@ unsigned int width_; unsigned int height_; unsigned int samplesPerPixel_; + unsigned int numberOfFrames_; const void* pixelData_; size_t size_; @@ -39,6 +40,10 @@ uint32_t signMask_; uint32_t mask_; size_t bytesPerPixel_; + unsigned int frame_; + + size_t frameOffset_; + size_t rowOffset_; public: DicomIntegerPixelAccessor(const DicomMap& values, @@ -55,6 +60,18 @@ return height_; } + unsigned int GetNumberOfFrames() const + { + return numberOfFrames_; + } + + unsigned int GetCurrentFrame() const + { + return frame_; + } + + void SetCurrentFrame(unsigned int frame); + void GetExtremeValues(int32_t& min, int32_t& max) const;
--- a/PalanthirServer/FromDcmtkBridge.cpp Fri Sep 07 12:53:38 2012 +0200 +++ b/PalanthirServer/FromDcmtkBridge.cpp Mon Sep 10 15:17:02 2012 +0200 @@ -438,6 +438,7 @@ 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 @@ -455,6 +456,7 @@ if (e->getUint8Array(pixData) == EC_Normal) { accessor.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength())); + accessor->SetCurrentFrame(frame); } } @@ -506,6 +508,7 @@ void FromDcmtkBridge::ExtractPngImage(std::string& result, const std::string& dicomContent, + unsigned int frame, ImageExtractionMode mode) { DcmInputBufferStream is; @@ -518,7 +521,7 @@ DcmFileFormat dicom; if (dicom.read(is).good()) { - ExtractPngImage(result, *dicom.getDataset(), mode); + ExtractPngImage(result, *dicom.getDataset(), frame, mode); } else {
--- a/PalanthirServer/FromDcmtkBridge.h Fri Sep 07 12:53:38 2012 +0200 +++ b/PalanthirServer/FromDcmtkBridge.h Mon Sep 10 15:17:02 2012 +0200 @@ -52,10 +52,12 @@ static void ExtractPngImage(std::string& result, DcmDataset& dataset, + unsigned int frame, ImageExtractionMode mode); static void ExtractPngImage(std::string& result, const std::string& dicomContent, + unsigned int frame, ImageExtractionMode mode); static std::string GetName(const DicomTag& tag);
--- a/PalanthirServer/PalanthirRestApi.cpp Fri Sep 07 12:53:38 2012 +0200 +++ b/PalanthirServer/PalanthirRestApi.cpp Mon Sep 10 15:17:02 2012 +0200 @@ -568,30 +568,82 @@ else if (uri.size() == 3 && uri[0] == "instances" && - (uri[2] == "preview" || - uri[2] == "image-uint8" || - uri[2] == "image-uint16")) + uri[2] == "frames") + { + Json::Value instance(Json::objectValue); + existingResource = index_.GetInstance(instance, uri[1]); + + if (existingResource) + { + result = Json::arrayValue; + + unsigned int numberOfFrames = 1; + try + { + Json::Value tmp = instance["MainDicomTags"]["NumberOfFrames"]; + numberOfFrames = boost::lexical_cast<unsigned int>(tmp.asString()); + } + catch (boost::bad_lexical_cast) + { + } + + for (unsigned int i = 0; i < numberOfFrames; i++) + { + result.append(i); + } + } + } + + + else if (uri[0] == "instances" && + ((uri.size() == 3 && + (uri[2] == "preview" || + uri[2] == "image-uint8" || + uri[2] == "image-uint16")) || + (uri.size() == 5 && + uri[2] == "frames" && + (uri[4] == "preview" || + uri[4] == "image-uint8" || + uri[4] == "image-uint16")))) { std::string uuid; existingResource = index_.GetDicomFile(uuid, uri[1]); + std::string action = uri[2]; + + unsigned int frame = 0; + if (existingResource && + uri.size() == 5) + { + // Access to multi-frame image + action = uri[4]; + try + { + frame = boost::lexical_cast<unsigned int>(uri[3]); + } + catch (boost::bad_lexical_cast) + { + existingResource = false; + } + } + if (existingResource) { std::string dicomContent, png; storage_.ReadFile(dicomContent, uuid); try { - if (uri[2] == "preview") + if (action == "preview") { - FromDcmtkBridge::ExtractPngImage(png, dicomContent, ImageExtractionMode_Preview); + FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_Preview); } - else if (uri[2] == "image-uint8") + else if (action == "image-uint8") { - FromDcmtkBridge::ExtractPngImage(png, dicomContent, ImageExtractionMode_UInt8); + FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_UInt8); } - else if (uri[2] == "image-uint16") + else if (action == "image-uint16") { - FromDcmtkBridge::ExtractPngImage(png, dicomContent, ImageExtractionMode_UInt16); + FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, ImageExtractionMode_UInt16); } else {