Mercurial > hg > orthanc
diff OrthancServer/FromDcmtkBridge.cpp @ 759:8cfc6119a5bd dicom-rt
integration mainline -> dicom-rt
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 16 Apr 2014 16:04:55 +0200 |
parents | da8e064d0d49 2d0a347e8cfc |
children | e57e08ed510f |
line wrap: on
line diff
--- a/OrthancServer/FromDcmtkBridge.cpp Thu Oct 17 14:21:50 2013 +0200 +++ b/OrthancServer/FromDcmtkBridge.cpp Wed Apr 16 16:04:55 2014 +0200 @@ -1,6 +1,6 @@ /** * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, * Belgium * * This program is free software: you can redistribute it and/or @@ -98,6 +98,7 @@ #include <dcmtk/dcmdata/dcfilefo.h> #include <dcmtk/dcmdata/dcistrmb.h> #include <dcmtk/dcmdata/dcuid.h> +#include <dcmtk/dcmdata/dcmetinf.h> #include <dcmtk/dcmdata/dcvrae.h> #include <dcmtk/dcmdata/dcvras.h> @@ -120,11 +121,20 @@ #include <dcmtk/dcmdata/dcvrul.h> #include <dcmtk/dcmdata/dcvrus.h> #include <dcmtk/dcmdata/dcvrut.h> +#include <dcmtk/dcmdata/dcpixel.h> +#include <dcmtk/dcmdata/dcpixseq.h> +#include <dcmtk/dcmdata/dcpxitem.h> + #include <boost/math/special_functions/round.hpp> #include <glog/logging.h> #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) @@ -208,16 +218,34 @@ 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) + DcmElement& element, + E_TransferSyntax transferSyntax) { - // This element is not a sequence + // This element is nor a sequence, neither a pixel-data std::string buffer; buffer.resize(65536); - Uint32 length = element.getLength(); + Uint32 length = element.getLength(transferSyntax); Uint32 offset = 0; - output.GetLowLevelOutput().SendOkHeader("application/octet-stream", true, length, NULL); + output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL); while (offset < length) { @@ -231,14 +259,16 @@ nbytes = buffer.size(); } - if (element.getPartialValue(&buffer[0], offset, nbytes).good()) + 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"; + LOG(ERROR) << "Error while sending a DICOM field: " << cond.text(); return; } } @@ -246,9 +276,96 @@ 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) + DcmItem& dicom, + E_TransferSyntax transferSyntax) { DcmTagKey k; ParseTagAndGroup(k, tag); @@ -268,7 +385,7 @@ //element->getVR() != EVR_UNKNOWN && // This would forbid private tags element->getVR() != EVR_SQ) { - AnswerDicomField(output, *element); + AnswerDicomField(output, *element, transferSyntax); } } @@ -276,6 +393,22 @@ 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++) @@ -309,7 +442,7 @@ } else { - SendPathValueForLeaf(output, uri.back(), *dicom); + SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); } } @@ -623,7 +756,7 @@ } for (Tags::iterator it = privateTags.begin(); - it != privateTags.end(); it++) + it != privateTags.end(); ++it) { DcmElement* tmp = dataset.remove(*it); if (tmp != NULL) @@ -700,7 +833,7 @@ std::string serialized; if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset())) { - output.AnswerBuffer(serialized, "application/octet-stream"); + output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM); } } @@ -1543,7 +1676,7 @@ void FromDcmtkBridge::Print(FILE* fp, const DicomMap& m) { for (DicomMap::Map::const_iterator - it = m.map_.begin(); it != m.map_.end(); it++) + it = m.map_.begin(); it != m.map_.end(); ++it) { DicomTag t = it->first; std::string s = it->second->AsString(); @@ -1563,7 +1696,7 @@ result.clear(); for (DicomMap::Map::const_iterator - it = values.map_.begin(); it != values.map_.end(); it++) + it = values.map_.begin(); it != values.map_.end(); ++it) { result[GetName(it->first)] = it->second->AsString(); } @@ -1604,25 +1737,41 @@ // syntax, with explicit length. // http://support.dcmtk.org/docs/dcxfer_8h-source.html - E_TransferSyntax xfer = EXS_LittleEndianExplicit; + + + /** + * Note that up to Orthanc 0.7.1 (inclusive), the + * "EXS_LittleEndianExplicit" was always used to save the DICOM + * dataset into memory. We now keep the original transfer syntax + * (if available). + **/ + E_TransferSyntax xfer = dataSet->getOriginalXfer(); + if (xfer == EXS_Unknown) + { + // No information about the original transfer syntax: This is + // most probably a DICOM dataset that was read from memory. + xfer = EXS_LittleEndianExplicit; + } + E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength; - uint32_t s = dataSet->getLength(xfer, encodingType); + // Create the meta-header information + DcmFileFormat ff(dataSet); + ff.validateMetaInfo(xfer); + // Create a memory buffer with the proper size + uint32_t s = ff.calcElementLength(xfer, encodingType); buffer.resize(s); DcmOutputBufferStream ob(&buffer[0], s); - dataSet->transferInit(); + // Fill the memory buffer with the meta-header and the dataset + ff.transferInit(); + OFCondition c = ff.write(ob, xfer, encodingType, NULL, + /*opt_groupLength*/ EGL_recalcGL, + /*opt_paddingType*/ EPD_withoutPadding); + ff.transferEnd(); -#if DCMTK_VERSION_NUMBER >= 360 - OFCondition c = dataSet->write(ob, xfer, encodingType, NULL, - /*opt_groupLength*/ EGL_recalcGL, - /*opt_paddingType*/ EPD_withoutPadding); -#else - OFCondition c = dataSet->write(ob, xfer, encodingType, NULL); -#endif - - dataSet->transferEnd(); + // Handle errors if (c.good()) { return true; @@ -1632,15 +1781,5 @@ buffer.clear(); return false; } - -#if 0 - OFCondition cond = cbdata->dcmff->saveFile(fileName.c_str(), xfer, - encodingType, - /*opt_groupLength*/ EGL_recalcGL, - /*opt_paddingType*/ EPD_withoutPadding, - OFstatic_cast(Uint32, /*opt_filepad*/ 0), - OFstatic_cast(Uint32, /*opt_itempad*/ 0), - (opt_useMetaheader) ? EWM_fileformat : EWM_dataset); -#endif } }