# HG changeset patch # User Sebastien Jodogne # Date 1391090714 -3600 # Node ID efc4928be6fbac0a0731471b2b68930f4f6b6198 # Parent 571583642ce2e8e01aaea6229a4b4736ae2e4b28 Recover pixel data for more transfer syntaxes (notably JPEG) diff -r 571583642ce2 -r efc4928be6fb NEWS --- a/NEWS Wed Jan 29 16:20:05 2014 +0100 +++ b/NEWS Thu Jan 30 15:05:14 2014 +0100 @@ -1,6 +1,7 @@ Pending changes in the mainline =============================== +* Recover pixel data for more transfer syntaxes (notably JPEG) * Maintenance tool to recover DICOM files compressed by Orthanc diff -r 571583642ce2 -r efc4928be6fb OrthancServer/FromDcmtkBridge.cpp --- a/OrthancServer/FromDcmtkBridge.cpp Wed Jan 29 16:20:05 2014 +0100 +++ b/OrthancServer/FromDcmtkBridge.cpp Thu Jan 30 15:05:14 2014 +0100 @@ -130,6 +130,11 @@ #include #include + +static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; + + + namespace Orthanc { void ParsedDicomFile::Setup(const char* buffer, size_t size) @@ -214,36 +219,18 @@ } - static void AnswerPixelData(RestApiOutput& output, - DcmPixelData& pixelData, - E_TransferSyntax transferSyntax) + static unsigned int GetPixelDataBlockCount(DcmPixelData& pixelData, + E_TransferSyntax transferSyntax) { DcmPixelSequence* pixelSequence = NULL; if (pixelData.getEncapsulatedRepresentation (transferSyntax, NULL, pixelSequence).good() && pixelSequence) { - for (unsigned long i = 0; i < pixelSequence->card(); i++) - { - DcmPixelItem* pixelItem = NULL; - if (pixelSequence->getItem(pixelItem, i).good() && pixelItem) - { - Uint8* b = NULL; - if (pixelItem->getUint8Array(b).good() && b) - { - // JPEG-LS (lossless) - // http://gdcm.sourceforge.net/wiki/index.php/Tools/ffmpeg#JPEG_LS - // http://www.stat.columbia.edu/~jakulin/jpeg-ls/ - // http://itohws03.ee.noda.sut.ac.jp/~matsuda/mrp/ - - printf("ITEM: %d %d\n", transferSyntax, pixelItem->getLength()); - char buf[64]; - sprintf(buf, "/tmp/toto-%06ld.jpg", i); - FILE* fp = fopen(buf, "wb"); - fwrite(b, pixelItem->getLength(), 1, fp); - fclose(fp); - } - } - } + return pixelSequence->card(); + } + else + { + return 1; } } @@ -252,29 +239,13 @@ DcmElement& element, E_TransferSyntax transferSyntax) { - // This element is not a sequence. Test if it is pixel data. - if (element.getTag().getGTag() == DICOM_TAG_PIXEL_DATA.GetGroup() && - element.getTag().getETag() == DICOM_TAG_PIXEL_DATA.GetElement()) - { - try - { - DcmPixelData& pixelData = dynamic_cast(element); - AnswerPixelData(output, pixelData, transferSyntax); - //return; - } - catch (std::bad_cast&) - { - } - } - - // 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("application/octet-stream", true, length, NULL); + output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL); while (offset < length) { @@ -305,6 +276,92 @@ 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(*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(i)); + } + + output.AnswerJson(result); + return true; + } + + + unsigned int block = boost::lexical_cast(*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, @@ -336,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++) @@ -369,7 +442,7 @@ } else { - SendPathValueForLeaf(output, uri.back(), *dicom, file_->getDataset()->getOriginalXfer()); + SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); } } @@ -760,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); } } diff -r 571583642ce2 -r efc4928be6fb OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Wed Jan 29 16:20:05 2014 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Thu Jan 30 15:05:14 2014 +0100 @@ -480,3 +480,15 @@ } } } + + + +/** + * TODO : Case-insensitive match for PN value representation (Patient + * Name). Case-senstive match for all the other value representations. + * + * Reference: DICOM PS 3.4 + * - C.2.2.2.1 ("Single Value Matching") + * - C.2.2.2.4 ("Wild Card Matching") + * http://medical.nema.org/Dicom/2011/11_04pu.pdf ( + **/