comparison 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
comparison
equal deleted inserted replaced
605:b82292ba2083 759:8cfc6119a5bd
1 /** 1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store 2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, 3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
4 * Belgium 4 * Belgium
5 * 5 *
6 * This program is free software: you can redistribute it and/or 6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as 7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the 8 * published by the Free Software Foundation, either version 3 of the
96 #include <dcmtk/dcmdata/dcdicent.h> 96 #include <dcmtk/dcmdata/dcdicent.h>
97 #include <dcmtk/dcmdata/dcdict.h> 97 #include <dcmtk/dcmdata/dcdict.h>
98 #include <dcmtk/dcmdata/dcfilefo.h> 98 #include <dcmtk/dcmdata/dcfilefo.h>
99 #include <dcmtk/dcmdata/dcistrmb.h> 99 #include <dcmtk/dcmdata/dcistrmb.h>
100 #include <dcmtk/dcmdata/dcuid.h> 100 #include <dcmtk/dcmdata/dcuid.h>
101 #include <dcmtk/dcmdata/dcmetinf.h>
101 102
102 #include <dcmtk/dcmdata/dcvrae.h> 103 #include <dcmtk/dcmdata/dcvrae.h>
103 #include <dcmtk/dcmdata/dcvras.h> 104 #include <dcmtk/dcmdata/dcvras.h>
104 #include <dcmtk/dcmdata/dcvrcs.h> 105 #include <dcmtk/dcmdata/dcvrcs.h>
105 #include <dcmtk/dcmdata/dcvrda.h> 106 #include <dcmtk/dcmdata/dcvrda.h>
118 #include <dcmtk/dcmdata/dcvrtm.h> 119 #include <dcmtk/dcmdata/dcvrtm.h>
119 #include <dcmtk/dcmdata/dcvrui.h> 120 #include <dcmtk/dcmdata/dcvrui.h>
120 #include <dcmtk/dcmdata/dcvrul.h> 121 #include <dcmtk/dcmdata/dcvrul.h>
121 #include <dcmtk/dcmdata/dcvrus.h> 122 #include <dcmtk/dcmdata/dcvrus.h>
122 #include <dcmtk/dcmdata/dcvrut.h> 123 #include <dcmtk/dcmdata/dcvrut.h>
124 #include <dcmtk/dcmdata/dcpixel.h>
125 #include <dcmtk/dcmdata/dcpixseq.h>
126 #include <dcmtk/dcmdata/dcpxitem.h>
127
123 128
124 #include <boost/math/special_functions/round.hpp> 129 #include <boost/math/special_functions/round.hpp>
125 #include <glog/logging.h> 130 #include <glog/logging.h>
126 #include <dcmtk/dcmdata/dcostrmb.h> 131 #include <dcmtk/dcmdata/dcostrmb.h>
132
133
134 static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
135
136
127 137
128 namespace Orthanc 138 namespace Orthanc
129 { 139 {
130 void ParsedDicomFile::Setup(const char* buffer, size_t size) 140 void ParsedDicomFile::Setup(const char* buffer, size_t size)
131 { 141 {
206 } 216 }
207 217
208 output.AnswerJson(v); 218 output.AnswerJson(v);
209 } 219 }
210 220
221
222 static unsigned int GetPixelDataBlockCount(DcmPixelData& pixelData,
223 E_TransferSyntax transferSyntax)
224 {
225 DcmPixelSequence* pixelSequence = NULL;
226 if (pixelData.getEncapsulatedRepresentation
227 (transferSyntax, NULL, pixelSequence).good() && pixelSequence)
228 {
229 return pixelSequence->card();
230 }
231 else
232 {
233 return 1;
234 }
235 }
236
237
211 static void AnswerDicomField(RestApiOutput& output, 238 static void AnswerDicomField(RestApiOutput& output,
212 DcmElement& element) 239 DcmElement& element,
213 { 240 E_TransferSyntax transferSyntax)
214 // This element is not a sequence 241 {
242 // This element is nor a sequence, neither a pixel-data
215 std::string buffer; 243 std::string buffer;
216 buffer.resize(65536); 244 buffer.resize(65536);
217 Uint32 length = element.getLength(); 245 Uint32 length = element.getLength(transferSyntax);
218 Uint32 offset = 0; 246 Uint32 offset = 0;
219 247
220 output.GetLowLevelOutput().SendOkHeader("application/octet-stream", true, length, NULL); 248 output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL);
221 249
222 while (offset < length) 250 while (offset < length)
223 { 251 {
224 Uint32 nbytes; 252 Uint32 nbytes;
225 if (length - offset < buffer.size()) 253 if (length - offset < buffer.size())
229 else 257 else
230 { 258 {
231 nbytes = buffer.size(); 259 nbytes = buffer.size();
232 } 260 }
233 261
234 if (element.getPartialValue(&buffer[0], offset, nbytes).good()) 262 OFCondition cond = element.getPartialValue(&buffer[0], offset, nbytes);
263
264 if (cond.good())
235 { 265 {
236 output.GetLowLevelOutput().Send(&buffer[0], nbytes); 266 output.GetLowLevelOutput().Send(&buffer[0], nbytes);
237 offset += nbytes; 267 offset += nbytes;
238 } 268 }
239 else 269 else
240 { 270 {
241 LOG(ERROR) << "Error while sending a DICOM field"; 271 LOG(ERROR) << "Error while sending a DICOM field: " << cond.text();
242 return; 272 return;
243 } 273 }
244 } 274 }
245 275
246 output.MarkLowLevelOutputDone(); 276 output.MarkLowLevelOutputDone();
247 } 277 }
278
279
280 static bool AnswerPixelData(RestApiOutput& output,
281 DcmItem& dicom,
282 E_TransferSyntax transferSyntax,
283 const std::string* blockUri)
284 {
285 DcmTag k(DICOM_TAG_PIXEL_DATA.GetGroup(),
286 DICOM_TAG_PIXEL_DATA.GetElement());
287
288 DcmElement *element = NULL;
289 if (!dicom.findAndGetElement(k, element).good() ||
290 element == NULL)
291 {
292 return false;
293 }
294
295 try
296 {
297 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
298 if (blockUri == NULL)
299 {
300 // The user asks how many blocks are presents in this pixel data
301 unsigned int blocks = GetPixelDataBlockCount(pixelData, transferSyntax);
302
303 Json::Value result(Json::arrayValue);
304 for (unsigned int i = 0; i < blocks; i++)
305 {
306 result.append(boost::lexical_cast<std::string>(i));
307 }
308
309 output.AnswerJson(result);
310 return true;
311 }
312
313
314 unsigned int block = boost::lexical_cast<unsigned int>(*blockUri);
315
316 if (block < GetPixelDataBlockCount(pixelData, transferSyntax))
317 {
318 DcmPixelSequence* pixelSequence = NULL;
319 if (pixelData.getEncapsulatedRepresentation
320 (transferSyntax, NULL, pixelSequence).good() && pixelSequence)
321 {
322 // This is the case for JPEG transfer syntaxes
323 if (block < pixelSequence->card())
324 {
325 DcmPixelItem* pixelItem = NULL;
326 if (pixelSequence->getItem(pixelItem, block).good() && pixelItem)
327 {
328 if (pixelItem->getLength() == 0)
329 {
330 output.AnswerBuffer(NULL, 0, CONTENT_TYPE_OCTET_STREAM);
331 return true;
332 }
333
334 Uint8* buffer = NULL;
335 if (pixelItem->getUint8Array(buffer).good() && buffer)
336 {
337 output.AnswerBuffer(buffer, pixelItem->getLength(), CONTENT_TYPE_OCTET_STREAM);
338 return true;
339 }
340 }
341 }
342 }
343 else
344 {
345 // This is the case for raw, uncompressed image buffers
346 assert(*blockUri == "0");
347 AnswerDicomField(output, *element, transferSyntax);
348 }
349 }
350 }
351 catch (boost::bad_lexical_cast&)
352 {
353 // The URI entered by the user is not a number
354 }
355 catch (std::bad_cast&)
356 {
357 // This should never happen
358 }
359
360 return false;
361 }
362
363
248 364
249 static void SendPathValueForLeaf(RestApiOutput& output, 365 static void SendPathValueForLeaf(RestApiOutput& output,
250 const std::string& tag, 366 const std::string& tag,
251 DcmItem& dicom) 367 DcmItem& dicom,
368 E_TransferSyntax transferSyntax)
252 { 369 {
253 DcmTagKey k; 370 DcmTagKey k;
254 ParseTagAndGroup(k, tag); 371 ParseTagAndGroup(k, tag);
255 372
256 DcmSequenceOfItems* sequence = NULL; 373 DcmSequenceOfItems* sequence = NULL;
266 if (dicom.findAndGetElement(k, element).good() && 383 if (dicom.findAndGetElement(k, element).good() &&
267 element != NULL && 384 element != NULL &&
268 //element->getVR() != EVR_UNKNOWN && // This would forbid private tags 385 //element->getVR() != EVR_UNKNOWN && // This would forbid private tags
269 element->getVR() != EVR_SQ) 386 element->getVR() != EVR_SQ)
270 { 387 {
271 AnswerDicomField(output, *element); 388 AnswerDicomField(output, *element, transferSyntax);
272 } 389 }
273 } 390 }
274 391
275 void ParsedDicomFile::SendPathValue(RestApiOutput& output, 392 void ParsedDicomFile::SendPathValue(RestApiOutput& output,
276 const UriComponents& uri) 393 const UriComponents& uri)
277 { 394 {
278 DcmItem* dicom = file_->getDataset(); 395 DcmItem* dicom = file_->getDataset();
396 E_TransferSyntax transferSyntax = file_->getDataset()->getOriginalXfer();
397
398 // Special case: Accessing the pixel data
399 if (uri.size() == 1 ||
400 uri.size() == 2)
401 {
402 DcmTagKey tag;
403 ParseTagAndGroup(tag, uri[0]);
404
405 if (tag.getGroup() == DICOM_TAG_PIXEL_DATA.GetGroup() &&
406 tag.getElement() == DICOM_TAG_PIXEL_DATA.GetElement())
407 {
408 AnswerPixelData(output, *dicom, transferSyntax, uri.size() == 1 ? NULL : &uri[1]);
409 return;
410 }
411 }
279 412
280 // Go down in the tag hierarchy according to the URI 413 // Go down in the tag hierarchy according to the URI
281 for (size_t pos = 0; pos < uri.size() / 2; pos++) 414 for (size_t pos = 0; pos < uri.size() / 2; pos++)
282 { 415 {
283 size_t index; 416 size_t index;
307 { 440 {
308 SendPathValueForDictionary(output, *dicom); 441 SendPathValueForDictionary(output, *dicom);
309 } 442 }
310 else 443 else
311 { 444 {
312 SendPathValueForLeaf(output, uri.back(), *dicom); 445 SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax);
313 } 446 }
314 } 447 }
315 448
316 449
317 450
621 privateTags.push_back(element); 754 privateTags.push_back(element);
622 } 755 }
623 } 756 }
624 757
625 for (Tags::iterator it = privateTags.begin(); 758 for (Tags::iterator it = privateTags.begin();
626 it != privateTags.end(); it++) 759 it != privateTags.end(); ++it)
627 { 760 {
628 DcmElement* tmp = dataset.remove(*it); 761 DcmElement* tmp = dataset.remove(*it);
629 if (tmp != NULL) 762 if (tmp != NULL)
630 { 763 {
631 delete tmp; 764 delete tmp;
698 void ParsedDicomFile::Answer(RestApiOutput& output) 831 void ParsedDicomFile::Answer(RestApiOutput& output)
699 { 832 {
700 std::string serialized; 833 std::string serialized;
701 if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset())) 834 if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset()))
702 { 835 {
703 output.AnswerBuffer(serialized, "application/octet-stream"); 836 output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM);
704 } 837 }
705 } 838 }
706 839
707 840
708 841
1541 1674
1542 1675
1543 void FromDcmtkBridge::Print(FILE* fp, const DicomMap& m) 1676 void FromDcmtkBridge::Print(FILE* fp, const DicomMap& m)
1544 { 1677 {
1545 for (DicomMap::Map::const_iterator 1678 for (DicomMap::Map::const_iterator
1546 it = m.map_.begin(); it != m.map_.end(); it++) 1679 it = m.map_.begin(); it != m.map_.end(); ++it)
1547 { 1680 {
1548 DicomTag t = it->first; 1681 DicomTag t = it->first;
1549 std::string s = it->second->AsString(); 1682 std::string s = it->second->AsString();
1550 fprintf(fp, "0x%04x 0x%04x (%s) [%s]\n", t.GetGroup(), t.GetElement(), GetName(t).c_str(), s.c_str()); 1683 fprintf(fp, "0x%04x 0x%04x (%s) [%s]\n", t.GetGroup(), t.GetElement(), GetName(t).c_str(), s.c_str());
1551 } 1684 }
1561 } 1694 }
1562 1695
1563 result.clear(); 1696 result.clear();
1564 1697
1565 for (DicomMap::Map::const_iterator 1698 for (DicomMap::Map::const_iterator
1566 it = values.map_.begin(); it != values.map_.end(); it++) 1699 it = values.map_.begin(); it != values.map_.end(); ++it)
1567 { 1700 {
1568 result[GetName(it->first)] = it->second->AsString(); 1701 result[GetName(it->first)] = it->second->AsString();
1569 } 1702 }
1570 } 1703 }
1571 1704
1602 // Determine the transfer syntax which shall be used to write the 1735 // Determine the transfer syntax which shall be used to write the
1603 // information to the file. We always switch to the Little Endian 1736 // information to the file. We always switch to the Little Endian
1604 // syntax, with explicit length. 1737 // syntax, with explicit length.
1605 1738
1606 // http://support.dcmtk.org/docs/dcxfer_8h-source.html 1739 // http://support.dcmtk.org/docs/dcxfer_8h-source.html
1607 E_TransferSyntax xfer = EXS_LittleEndianExplicit; 1740
1741
1742 /**
1743 * Note that up to Orthanc 0.7.1 (inclusive), the
1744 * "EXS_LittleEndianExplicit" was always used to save the DICOM
1745 * dataset into memory. We now keep the original transfer syntax
1746 * (if available).
1747 **/
1748 E_TransferSyntax xfer = dataSet->getOriginalXfer();
1749 if (xfer == EXS_Unknown)
1750 {
1751 // No information about the original transfer syntax: This is
1752 // most probably a DICOM dataset that was read from memory.
1753 xfer = EXS_LittleEndianExplicit;
1754 }
1755
1608 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength; 1756 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;
1609 1757
1610 uint32_t s = dataSet->getLength(xfer, encodingType); 1758 // Create the meta-header information
1611 1759 DcmFileFormat ff(dataSet);
1760 ff.validateMetaInfo(xfer);
1761
1762 // Create a memory buffer with the proper size
1763 uint32_t s = ff.calcElementLength(xfer, encodingType);
1612 buffer.resize(s); 1764 buffer.resize(s);
1613 DcmOutputBufferStream ob(&buffer[0], s); 1765 DcmOutputBufferStream ob(&buffer[0], s);
1614 1766
1615 dataSet->transferInit(); 1767 // Fill the memory buffer with the meta-header and the dataset
1616 1768 ff.transferInit();
1617 #if DCMTK_VERSION_NUMBER >= 360 1769 OFCondition c = ff.write(ob, xfer, encodingType, NULL,
1618 OFCondition c = dataSet->write(ob, xfer, encodingType, NULL, 1770 /*opt_groupLength*/ EGL_recalcGL,
1619 /*opt_groupLength*/ EGL_recalcGL, 1771 /*opt_paddingType*/ EPD_withoutPadding);
1620 /*opt_paddingType*/ EPD_withoutPadding); 1772 ff.transferEnd();
1621 #else 1773
1622 OFCondition c = dataSet->write(ob, xfer, encodingType, NULL); 1774 // Handle errors
1623 #endif
1624
1625 dataSet->transferEnd();
1626 if (c.good()) 1775 if (c.good())
1627 { 1776 {
1628 return true; 1777 return true;
1629 } 1778 }
1630 else 1779 else
1631 { 1780 {
1632 buffer.clear(); 1781 buffer.clear();
1633 return false; 1782 return false;
1634 } 1783 }
1635
1636 #if 0
1637 OFCondition cond = cbdata->dcmff->saveFile(fileName.c_str(), xfer,
1638 encodingType,
1639 /*opt_groupLength*/ EGL_recalcGL,
1640 /*opt_paddingType*/ EPD_withoutPadding,
1641 OFstatic_cast(Uint32, /*opt_filepad*/ 0),
1642 OFstatic_cast(Uint32, /*opt_itempad*/ 0),
1643 (opt_useMetaheader) ? EWM_fileformat : EWM_dataset);
1644 #endif
1645 } 1784 }
1646 } 1785 }