Mercurial > hg > orthanc
comparison Core/DicomParsing/FromDcmtkBridge.cpp @ 2382:7284093111b0
big reorganization to cleanly separate framework vs. server
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 29 Aug 2017 21:17:35 +0200 |
parents | OrthancServer/FromDcmtkBridge.cpp@b8969010b534 |
children | e4045b3c9772 |
comparison
equal
deleted
inserted
replaced
2381:b8969010b534 | 2382:7284093111b0 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017 Osimis, Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 #include "../PrecompiledHeaders.h" | |
35 | |
36 #ifndef NOMINMAX | |
37 #define NOMINMAX | |
38 #endif | |
39 | |
40 #include "FromDcmtkBridge.h" | |
41 #include "ToDcmtkBridge.h" | |
42 #include "../Logging.h" | |
43 #include "../SystemToolbox.h" | |
44 #include "../Toolbox.h" | |
45 #include "../TemporaryFile.h" | |
46 #include "../OrthancException.h" | |
47 | |
48 #include <list> | |
49 #include <limits> | |
50 | |
51 #include <boost/lexical_cast.hpp> | |
52 #include <boost/filesystem.hpp> | |
53 #include <boost/algorithm/string/predicate.hpp> | |
54 | |
55 #include <dcmtk/dcmdata/dcdeftag.h> | |
56 #include <dcmtk/dcmdata/dcdicent.h> | |
57 #include <dcmtk/dcmdata/dcdict.h> | |
58 #include <dcmtk/dcmdata/dcfilefo.h> | |
59 #include <dcmtk/dcmdata/dcostrmb.h> | |
60 #include <dcmtk/dcmdata/dcpixel.h> | |
61 #include <dcmtk/dcmdata/dcuid.h> | |
62 #include <dcmtk/dcmdata/dcistrmb.h> | |
63 | |
64 #include <dcmtk/dcmdata/dcvrae.h> | |
65 #include <dcmtk/dcmdata/dcvras.h> | |
66 #include <dcmtk/dcmdata/dcvrat.h> | |
67 #include <dcmtk/dcmdata/dcvrcs.h> | |
68 #include <dcmtk/dcmdata/dcvrda.h> | |
69 #include <dcmtk/dcmdata/dcvrds.h> | |
70 #include <dcmtk/dcmdata/dcvrdt.h> | |
71 #include <dcmtk/dcmdata/dcvrfd.h> | |
72 #include <dcmtk/dcmdata/dcvrfl.h> | |
73 #include <dcmtk/dcmdata/dcvris.h> | |
74 #include <dcmtk/dcmdata/dcvrlo.h> | |
75 #include <dcmtk/dcmdata/dcvrlt.h> | |
76 #include <dcmtk/dcmdata/dcvrpn.h> | |
77 #include <dcmtk/dcmdata/dcvrsh.h> | |
78 #include <dcmtk/dcmdata/dcvrsl.h> | |
79 #include <dcmtk/dcmdata/dcvrss.h> | |
80 #include <dcmtk/dcmdata/dcvrst.h> | |
81 #include <dcmtk/dcmdata/dcvrtm.h> | |
82 #include <dcmtk/dcmdata/dcvrui.h> | |
83 #include <dcmtk/dcmdata/dcvrul.h> | |
84 #include <dcmtk/dcmdata/dcvrus.h> | |
85 #include <dcmtk/dcmdata/dcvrut.h> | |
86 | |
87 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 | |
88 # include <EmbeddedResources.h> | |
89 #endif | |
90 | |
91 #if ORTHANC_ENABLE_DCMTK_JPEG == 1 | |
92 # include <dcmtk/dcmjpeg/djdecode.h> | |
93 #endif | |
94 | |
95 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1 | |
96 # include <dcmtk/dcmjpls/djdecode.h> | |
97 #endif | |
98 | |
99 | |
100 namespace Orthanc | |
101 { | |
102 static inline uint16_t GetCharValue(char c) | |
103 { | |
104 if (c >= '0' && c <= '9') | |
105 return c - '0'; | |
106 else if (c >= 'a' && c <= 'f') | |
107 return c - 'a' + 10; | |
108 else if (c >= 'A' && c <= 'F') | |
109 return c - 'A' + 10; | |
110 else | |
111 return 0; | |
112 } | |
113 | |
114 static inline uint16_t GetTagValue(const char* c) | |
115 { | |
116 return ((GetCharValue(c[0]) << 12) + | |
117 (GetCharValue(c[1]) << 8) + | |
118 (GetCharValue(c[2]) << 4) + | |
119 GetCharValue(c[3])); | |
120 } | |
121 | |
122 | |
123 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 | |
124 static void LoadEmbeddedDictionary(DcmDataDictionary& dictionary, | |
125 EmbeddedResources::FileResourceId resource) | |
126 { | |
127 std::string content; | |
128 EmbeddedResources::GetFileResource(content, resource); | |
129 | |
130 TemporaryFile tmp; | |
131 tmp.Write(content); | |
132 | |
133 if (!dictionary.loadDictionary(tmp.GetPath().c_str())) | |
134 { | |
135 LOG(ERROR) << "Cannot read embedded dictionary. Under Windows, make sure that " | |
136 << "your TEMP directory does not contain special characters."; | |
137 throw OrthancException(ErrorCode_InternalError); | |
138 } | |
139 } | |
140 | |
141 #else | |
142 static void LoadExternalDictionary(DcmDataDictionary& dictionary, | |
143 const std::string& directory, | |
144 const std::string& filename) | |
145 { | |
146 boost::filesystem::path p = directory; | |
147 p = p / filename; | |
148 | |
149 LOG(WARNING) << "Loading the external DICOM dictionary " << p; | |
150 | |
151 if (!dictionary.loadDictionary(p.string().c_str())) | |
152 { | |
153 throw OrthancException(ErrorCode_InternalError); | |
154 } | |
155 } | |
156 #endif | |
157 | |
158 | |
159 namespace | |
160 { | |
161 class DictionaryLocker | |
162 { | |
163 private: | |
164 DcmDataDictionary& dictionary_; | |
165 | |
166 public: | |
167 DictionaryLocker() : dictionary_(dcmDataDict.wrlock()) | |
168 { | |
169 } | |
170 | |
171 ~DictionaryLocker() | |
172 { | |
173 dcmDataDict.unlock(); | |
174 } | |
175 | |
176 DcmDataDictionary& operator*() | |
177 { | |
178 return dictionary_; | |
179 } | |
180 | |
181 DcmDataDictionary* operator->() | |
182 { | |
183 return &dictionary_; | |
184 } | |
185 }; | |
186 } | |
187 | |
188 | |
189 void FromDcmtkBridge::InitializeDictionary(bool loadPrivateDictionary) | |
190 { | |
191 LOG(INFO) << "Using DCTMK version: " << DCMTK_VERSION_NUMBER; | |
192 | |
193 { | |
194 DictionaryLocker locker; | |
195 | |
196 locker->clear(); | |
197 | |
198 #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 | |
199 LOG(WARNING) << "Loading the embedded dictionaries"; | |
200 /** | |
201 * Do not load DICONDE dictionary, it breaks the other tags. The | |
202 * command "strace storescu 2>&1 |grep dic" shows that DICONDE | |
203 * dictionary is not loaded by storescu. | |
204 **/ | |
205 //LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICONDE); | |
206 | |
207 LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICOM); | |
208 | |
209 if (loadPrivateDictionary) | |
210 { | |
211 LOG(INFO) << "Loading the embedded dictionary of private tags"; | |
212 LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_PRIVATE); | |
213 } | |
214 else | |
215 { | |
216 LOG(INFO) << "The dictionary of private tags has not been loaded"; | |
217 } | |
218 | |
219 #elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) | |
220 std::string path = DCMTK_DICTIONARY_DIR; | |
221 | |
222 const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE); | |
223 if (env != NULL) | |
224 { | |
225 path = std::string(env); | |
226 } | |
227 | |
228 LoadExternalDictionary(*locker, path, "dicom.dic"); | |
229 | |
230 if (loadPrivateDictionary) | |
231 { | |
232 LoadExternalDictionary(*locker, path, "private.dic"); | |
233 } | |
234 else | |
235 { | |
236 LOG(INFO) << "The dictionary of private tags has not been loaded"; | |
237 } | |
238 | |
239 #else | |
240 #error Support your platform here | |
241 #endif | |
242 } | |
243 | |
244 /* make sure data dictionary is loaded */ | |
245 if (!dcmDataDict.isDictionaryLoaded()) | |
246 { | |
247 LOG(ERROR) << "No DICOM dictionary loaded, check environment variable: " << DCM_DICT_ENVIRONMENT_VARIABLE; | |
248 throw OrthancException(ErrorCode_InternalError); | |
249 } | |
250 | |
251 { | |
252 // Test the dictionary with a simple DICOM tag | |
253 DcmTag key(0x0010, 0x1030); // This is PatientWeight | |
254 if (key.getEVR() != EVR_DS) | |
255 { | |
256 LOG(ERROR) << "The DICOM dictionary has not been correctly read"; | |
257 throw OrthancException(ErrorCode_InternalError); | |
258 } | |
259 } | |
260 } | |
261 | |
262 | |
263 void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag, | |
264 ValueRepresentation vr, | |
265 const std::string& name, | |
266 unsigned int minMultiplicity, | |
267 unsigned int maxMultiplicity, | |
268 const std::string& privateCreator) | |
269 { | |
270 if (minMultiplicity < 1) | |
271 { | |
272 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
273 } | |
274 | |
275 bool arbitrary = false; | |
276 if (maxMultiplicity == 0) | |
277 { | |
278 maxMultiplicity = DcmVariableVM; | |
279 arbitrary = true; | |
280 } | |
281 else if (maxMultiplicity < minMultiplicity) | |
282 { | |
283 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
284 } | |
285 | |
286 DcmEVR evr = ToDcmtkBridge::Convert(vr); | |
287 | |
288 LOG(INFO) << "Registering tag in dictionary: " << tag << " " << (DcmVR(evr).getValidVRName()) << " " | |
289 << name << " (multiplicity: " << minMultiplicity << "-" | |
290 << (arbitrary ? "n" : boost::lexical_cast<std::string>(maxMultiplicity)) << ")"; | |
291 | |
292 std::auto_ptr<DcmDictEntry> entry; | |
293 if (privateCreator.empty()) | |
294 { | |
295 if (tag.GetGroup() % 2 == 1) | |
296 { | |
297 char buf[128]; | |
298 sprintf(buf, "Warning: You are registering a private tag (%04x,%04x), " | |
299 "but no private creator was associated with it", | |
300 tag.GetGroup(), tag.GetElement()); | |
301 LOG(WARNING) << buf; | |
302 } | |
303 | |
304 entry.reset(new DcmDictEntry(tag.GetGroup(), | |
305 tag.GetElement(), | |
306 evr, name.c_str(), | |
307 static_cast<int>(minMultiplicity), | |
308 static_cast<int>(maxMultiplicity), | |
309 NULL /* version */, | |
310 OFTrue /* doCopyString */, | |
311 NULL /* private creator */)); | |
312 } | |
313 else | |
314 { | |
315 // "Private Data Elements have an odd Group Number that is not | |
316 // (0001,eeee), (0003,eeee), (0005,eeee), (0007,eeee), or | |
317 // (FFFF,eeee)." | |
318 if (tag.GetGroup() % 2 == 0 /* even */ || | |
319 tag.GetGroup() == 0x0001 || | |
320 tag.GetGroup() == 0x0003 || | |
321 tag.GetGroup() == 0x0005 || | |
322 tag.GetGroup() == 0x0007 || | |
323 tag.GetGroup() == 0xffff) | |
324 { | |
325 char buf[128]; | |
326 sprintf(buf, "Trying to register private tag (%04x,%04x), but it must have an odd group >= 0x0009", | |
327 tag.GetGroup(), tag.GetElement()); | |
328 LOG(ERROR) << buf; | |
329 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
330 } | |
331 | |
332 entry.reset(new DcmDictEntry(tag.GetGroup(), | |
333 tag.GetElement(), | |
334 evr, name.c_str(), | |
335 static_cast<int>(minMultiplicity), | |
336 static_cast<int>(maxMultiplicity), | |
337 "private" /* version */, | |
338 OFTrue /* doCopyString */, | |
339 privateCreator.c_str())); | |
340 } | |
341 | |
342 entry->setGroupRangeRestriction(DcmDictRange_Unspecified); | |
343 entry->setElementRangeRestriction(DcmDictRange_Unspecified); | |
344 | |
345 { | |
346 DictionaryLocker locker; | |
347 | |
348 if (locker->findEntry(name.c_str())) | |
349 { | |
350 LOG(ERROR) << "Cannot register two tags with the same symbolic name \"" << name << "\""; | |
351 throw OrthancException(ErrorCode_AlreadyExistingTag); | |
352 } | |
353 | |
354 locker->addEntry(entry.release()); | |
355 } | |
356 } | |
357 | |
358 | |
359 Encoding FromDcmtkBridge::DetectEncoding(DcmItem& dataset, | |
360 Encoding defaultEncoding) | |
361 { | |
362 Encoding encoding = defaultEncoding; | |
363 | |
364 OFString tmp; | |
365 if (dataset.findAndGetOFString(DCM_SpecificCharacterSet, tmp).good()) | |
366 { | |
367 std::string characterSet = Toolbox::StripSpaces(std::string(tmp.c_str())); | |
368 | |
369 if (characterSet.empty()) | |
370 { | |
371 // Empty specific character set tag: Use the default encoding | |
372 } | |
373 else if (GetDicomEncoding(encoding, characterSet.c_str())) | |
374 { | |
375 // The specific character set is supported by the Orthanc core | |
376 } | |
377 else | |
378 { | |
379 LOG(WARNING) << "Value of Specific Character Set (0008,0005) is not supported: " << characterSet | |
380 << ", fallback to ASCII (remove all special characters)"; | |
381 encoding = Encoding_Ascii; | |
382 } | |
383 } | |
384 else | |
385 { | |
386 // No specific character set tag: Use the default encoding | |
387 } | |
388 | |
389 return encoding; | |
390 } | |
391 | |
392 | |
393 void FromDcmtkBridge::ExtractDicomSummary(DicomMap& target, | |
394 DcmItem& dataset, | |
395 unsigned int maxStringLength, | |
396 Encoding defaultEncoding) | |
397 { | |
398 Encoding encoding = DetectEncoding(dataset, defaultEncoding); | |
399 | |
400 target.Clear(); | |
401 for (unsigned long i = 0; i < dataset.card(); i++) | |
402 { | |
403 DcmElement* element = dataset.getElement(i); | |
404 if (element && element->isLeaf()) | |
405 { | |
406 target.SetValue(element->getTag().getGTag(), | |
407 element->getTag().getETag(), | |
408 ConvertLeafElement(*element, DicomToJsonFlags_Default, maxStringLength, encoding)); | |
409 } | |
410 } | |
411 } | |
412 | |
413 | |
414 DicomTag FromDcmtkBridge::Convert(const DcmTag& tag) | |
415 { | |
416 return DicomTag(tag.getGTag(), tag.getETag()); | |
417 } | |
418 | |
419 | |
420 DicomTag FromDcmtkBridge::GetTag(const DcmElement& element) | |
421 { | |
422 return DicomTag(element.getGTag(), element.getETag()); | |
423 } | |
424 | |
425 | |
426 DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element, | |
427 DicomToJsonFlags flags, | |
428 unsigned int maxStringLength, | |
429 Encoding encoding) | |
430 { | |
431 if (!element.isLeaf()) | |
432 { | |
433 // This function is only applicable to leaf elements | |
434 throw OrthancException(ErrorCode_BadParameterType); | |
435 } | |
436 | |
437 char *c = NULL; | |
438 if (element.isaString() && | |
439 element.getString(c).good()) | |
440 { | |
441 if (c == NULL) // This case corresponds to the empty string | |
442 { | |
443 return new DicomValue("", false); | |
444 } | |
445 else | |
446 { | |
447 std::string s(c); | |
448 std::string utf8 = Toolbox::ConvertToUtf8(s, encoding); | |
449 | |
450 if (maxStringLength != 0 && | |
451 utf8.size() > maxStringLength) | |
452 { | |
453 return new DicomValue; // Too long, create a NULL value | |
454 } | |
455 else | |
456 { | |
457 return new DicomValue(utf8, false); | |
458 } | |
459 } | |
460 } | |
461 | |
462 | |
463 if (element.getVR() == EVR_UN) | |
464 { | |
465 // Unknown value representation: Lookup in the dictionary. This | |
466 // is notably the case for private tags registered with the | |
467 // "Dictionary" configuration option. | |
468 DictionaryLocker locker; | |
469 | |
470 const DcmDictEntry* entry = locker->findEntry(element.getTag().getXTag(), | |
471 element.getTag().getPrivateCreator()); | |
472 if (entry != NULL && | |
473 entry->getVR().isaString()) | |
474 { | |
475 Uint8* data = NULL; | |
476 | |
477 // At (*), we do not try and convert to UTF-8, as nothing says | |
478 // the encoding of the private tag is the same as that of the | |
479 // remaining of the DICOM dataset. Only go for ASCII strings. | |
480 | |
481 if (element.getUint8Array(data) == EC_Normal && | |
482 Toolbox::IsAsciiString(data, element.getLength())) // (*) | |
483 { | |
484 if (data == NULL) | |
485 { | |
486 return new DicomValue("", false); // Empty string | |
487 } | |
488 else if (maxStringLength != 0 && | |
489 element.getLength() > maxStringLength) | |
490 { | |
491 return new DicomValue; // Too long, create a NULL value | |
492 } | |
493 else | |
494 { | |
495 std::string s(reinterpret_cast<const char*>(data), element.getLength()); | |
496 return new DicomValue(s, false); | |
497 } | |
498 } | |
499 } | |
500 } | |
501 | |
502 | |
503 try | |
504 { | |
505 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
506 switch (element.getVR()) | |
507 { | |
508 | |
509 /** | |
510 * Deal with binary data (including PixelData). | |
511 **/ | |
512 | |
513 case EVR_OB: // other byte | |
514 case EVR_OF: // other float | |
515 case EVR_OW: // other word | |
516 case EVR_UN: // unknown value representation | |
517 case EVR_ox: // OB or OW depending on context | |
518 case EVR_DS: // decimal string | |
519 case EVR_IS: // integer string | |
520 case EVR_AS: // age string | |
521 case EVR_DA: // date string | |
522 case EVR_DT: // date time string | |
523 case EVR_TM: // time string | |
524 case EVR_AE: // application entity title | |
525 case EVR_CS: // code string | |
526 case EVR_SH: // short string | |
527 case EVR_LO: // long string | |
528 case EVR_ST: // short text | |
529 case EVR_LT: // long text | |
530 case EVR_UT: // unlimited text | |
531 case EVR_PN: // person name | |
532 case EVR_UI: // unique identifier | |
533 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
534 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
535 { | |
536 if (!(flags & DicomToJsonFlags_ConvertBinaryToNull)) | |
537 { | |
538 Uint8* data = NULL; | |
539 if (element.getUint8Array(data) == EC_Normal) | |
540 { | |
541 return new DicomValue(reinterpret_cast<const char*>(data), element.getLength(), true); | |
542 } | |
543 } | |
544 | |
545 return new DicomValue; | |
546 } | |
547 | |
548 /** | |
549 * Numeric types | |
550 **/ | |
551 | |
552 case EVR_SL: // signed long | |
553 { | |
554 Sint32 f; | |
555 if (dynamic_cast<DcmSignedLong&>(element).getSint32(f).good()) | |
556 return new DicomValue(boost::lexical_cast<std::string>(f), false); | |
557 else | |
558 return new DicomValue; | |
559 } | |
560 | |
561 case EVR_SS: // signed short | |
562 { | |
563 Sint16 f; | |
564 if (dynamic_cast<DcmSignedShort&>(element).getSint16(f).good()) | |
565 return new DicomValue(boost::lexical_cast<std::string>(f), false); | |
566 else | |
567 return new DicomValue; | |
568 } | |
569 | |
570 case EVR_UL: // unsigned long | |
571 { | |
572 Uint32 f; | |
573 if (dynamic_cast<DcmUnsignedLong&>(element).getUint32(f).good()) | |
574 return new DicomValue(boost::lexical_cast<std::string>(f), false); | |
575 else | |
576 return new DicomValue; | |
577 } | |
578 | |
579 case EVR_US: // unsigned short | |
580 { | |
581 Uint16 f; | |
582 if (dynamic_cast<DcmUnsignedShort&>(element).getUint16(f).good()) | |
583 return new DicomValue(boost::lexical_cast<std::string>(f), false); | |
584 else | |
585 return new DicomValue; | |
586 } | |
587 | |
588 case EVR_FL: // float single-precision | |
589 { | |
590 Float32 f; | |
591 if (dynamic_cast<DcmFloatingPointSingle&>(element).getFloat32(f).good()) | |
592 return new DicomValue(boost::lexical_cast<std::string>(f), false); | |
593 else | |
594 return new DicomValue; | |
595 } | |
596 | |
597 case EVR_FD: // float double-precision | |
598 { | |
599 Float64 f; | |
600 if (dynamic_cast<DcmFloatingPointDouble&>(element).getFloat64(f).good()) | |
601 return new DicomValue(boost::lexical_cast<std::string>(f), false); | |
602 else | |
603 return new DicomValue; | |
604 } | |
605 | |
606 | |
607 /** | |
608 * Attribute tag. | |
609 **/ | |
610 | |
611 case EVR_AT: | |
612 { | |
613 DcmTagKey tag; | |
614 if (dynamic_cast<DcmAttributeTag&>(element).getTagVal(tag, 0).good()) | |
615 { | |
616 DicomTag t(tag.getGroup(), tag.getElement()); | |
617 return new DicomValue(t.Format(), false); | |
618 } | |
619 else | |
620 { | |
621 return new DicomValue; | |
622 } | |
623 } | |
624 | |
625 | |
626 /** | |
627 * Sequence types, should never occur at this point because of | |
628 * "element.isLeaf()". | |
629 **/ | |
630 | |
631 case EVR_SQ: // sequence of items | |
632 return new DicomValue; | |
633 | |
634 | |
635 /** | |
636 * Internal to DCMTK. | |
637 **/ | |
638 | |
639 case EVR_xs: // SS or US depending on context | |
640 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
641 case EVR_na: // na="not applicable", for data which has no VR | |
642 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
643 case EVR_item: // used internally for items | |
644 case EVR_metainfo: // used internally for meta info datasets | |
645 case EVR_dataset: // used internally for datasets | |
646 case EVR_fileFormat: // used internally for DICOM files | |
647 case EVR_dicomDir: // used internally for DICOMDIR objects | |
648 case EVR_dirRecord: // used internally for DICOMDIR records | |
649 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
650 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
651 case EVR_PixelData: // used internally for uncompressed pixeld data | |
652 case EVR_OverlayData: // used internally for overlay data | |
653 return new DicomValue; | |
654 | |
655 | |
656 /** | |
657 * Default case. | |
658 **/ | |
659 | |
660 default: | |
661 return new DicomValue; | |
662 } | |
663 } | |
664 catch (boost::bad_lexical_cast) | |
665 { | |
666 return new DicomValue; | |
667 } | |
668 catch (std::bad_cast) | |
669 { | |
670 return new DicomValue; | |
671 } | |
672 } | |
673 | |
674 | |
675 static Json::Value& PrepareNode(Json::Value& parent, | |
676 DcmElement& element, | |
677 DicomToJsonFormat format) | |
678 { | |
679 assert(parent.type() == Json::objectValue); | |
680 | |
681 DicomTag tag(FromDcmtkBridge::GetTag(element)); | |
682 const std::string formattedTag = tag.Format(); | |
683 | |
684 if (format == DicomToJsonFormat_Short) | |
685 { | |
686 parent[formattedTag] = Json::nullValue; | |
687 return parent[formattedTag]; | |
688 } | |
689 | |
690 // This code gives access to the name of the private tags | |
691 std::string tagName = FromDcmtkBridge::GetTagName(element); | |
692 | |
693 switch (format) | |
694 { | |
695 case DicomToJsonFormat_Human: | |
696 parent[tagName] = Json::nullValue; | |
697 return parent[tagName]; | |
698 | |
699 case DicomToJsonFormat_Full: | |
700 { | |
701 parent[formattedTag] = Json::objectValue; | |
702 Json::Value& node = parent[formattedTag]; | |
703 | |
704 if (element.isLeaf()) | |
705 { | |
706 node["Name"] = tagName; | |
707 | |
708 if (element.getTag().getPrivateCreator() != NULL) | |
709 { | |
710 node["PrivateCreator"] = element.getTag().getPrivateCreator(); | |
711 } | |
712 | |
713 return node; | |
714 } | |
715 else | |
716 { | |
717 node["Name"] = tagName; | |
718 node["Type"] = "Sequence"; | |
719 node["Value"] = Json::nullValue; | |
720 return node["Value"]; | |
721 } | |
722 } | |
723 | |
724 default: | |
725 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
726 } | |
727 } | |
728 | |
729 | |
730 static void LeafValueToJson(Json::Value& target, | |
731 const DicomValue& value, | |
732 DicomToJsonFormat format, | |
733 DicomToJsonFlags flags, | |
734 unsigned int maxStringLength) | |
735 { | |
736 Json::Value* targetValue = NULL; | |
737 Json::Value* targetType = NULL; | |
738 | |
739 switch (format) | |
740 { | |
741 case DicomToJsonFormat_Short: | |
742 case DicomToJsonFormat_Human: | |
743 { | |
744 assert(target.type() == Json::nullValue); | |
745 targetValue = ⌖ | |
746 break; | |
747 } | |
748 | |
749 case DicomToJsonFormat_Full: | |
750 { | |
751 assert(target.type() == Json::objectValue); | |
752 target["Value"] = Json::nullValue; | |
753 target["Type"] = Json::nullValue; | |
754 targetType = &target["Type"]; | |
755 targetValue = &target["Value"]; | |
756 break; | |
757 } | |
758 | |
759 default: | |
760 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
761 } | |
762 | |
763 assert(targetValue != NULL); | |
764 assert(targetValue->type() == Json::nullValue); | |
765 assert(targetType == NULL || targetType->type() == Json::nullValue); | |
766 | |
767 if (value.IsNull()) | |
768 { | |
769 if (targetType != NULL) | |
770 { | |
771 *targetType = "Null"; | |
772 } | |
773 } | |
774 else if (value.IsBinary()) | |
775 { | |
776 if (flags & DicomToJsonFlags_ConvertBinaryToAscii) | |
777 { | |
778 *targetValue = Toolbox::ConvertToAscii(value.GetContent()); | |
779 } | |
780 else | |
781 { | |
782 std::string s; | |
783 value.FormatDataUriScheme(s); | |
784 *targetValue = s; | |
785 } | |
786 | |
787 if (targetType != NULL) | |
788 { | |
789 *targetType = "Binary"; | |
790 } | |
791 } | |
792 else if (maxStringLength == 0 || | |
793 value.GetContent().size() <= maxStringLength) | |
794 { | |
795 *targetValue = value.GetContent(); | |
796 | |
797 if (targetType != NULL) | |
798 { | |
799 *targetType = "String"; | |
800 } | |
801 } | |
802 else | |
803 { | |
804 if (targetType != NULL) | |
805 { | |
806 *targetType = "TooLong"; | |
807 } | |
808 } | |
809 } | |
810 | |
811 | |
812 void FromDcmtkBridge::ElementToJson(Json::Value& parent, | |
813 DcmElement& element, | |
814 DicomToJsonFormat format, | |
815 DicomToJsonFlags flags, | |
816 unsigned int maxStringLength, | |
817 Encoding encoding) | |
818 { | |
819 if (parent.type() == Json::nullValue) | |
820 { | |
821 parent = Json::objectValue; | |
822 } | |
823 | |
824 assert(parent.type() == Json::objectValue); | |
825 Json::Value& target = PrepareNode(parent, element, format); | |
826 | |
827 if (element.isLeaf()) | |
828 { | |
829 // The "0" below lets "LeafValueToJson()" take care of "TooLong" values | |
830 std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element, flags, 0, encoding)); | |
831 LeafValueToJson(target, *v, format, flags, maxStringLength); | |
832 } | |
833 else | |
834 { | |
835 assert(target.type() == Json::nullValue); | |
836 target = Json::arrayValue; | |
837 | |
838 // "All subclasses of DcmElement except for DcmSequenceOfItems | |
839 // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset | |
840 // etc. are not." The following dynamic_cast is thus OK. | |
841 DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(element); | |
842 | |
843 for (unsigned long i = 0; i < sequence.card(); i++) | |
844 { | |
845 DcmItem* child = sequence.getItem(i); | |
846 Json::Value& v = target.append(Json::objectValue); | |
847 DatasetToJson(v, *child, format, flags, maxStringLength, encoding); | |
848 } | |
849 } | |
850 } | |
851 | |
852 | |
853 void FromDcmtkBridge::DatasetToJson(Json::Value& parent, | |
854 DcmItem& item, | |
855 DicomToJsonFormat format, | |
856 DicomToJsonFlags flags, | |
857 unsigned int maxStringLength, | |
858 Encoding encoding) | |
859 { | |
860 assert(parent.type() == Json::objectValue); | |
861 | |
862 for (unsigned long i = 0; i < item.card(); i++) | |
863 { | |
864 DcmElement* element = item.getElement(i); | |
865 if (element == NULL) | |
866 { | |
867 throw OrthancException(ErrorCode_InternalError); | |
868 } | |
869 | |
870 DicomTag tag(FromDcmtkBridge::Convert(element->getTag())); | |
871 | |
872 /*element->getTag().isPrivate()*/ | |
873 if (tag.IsPrivate() && | |
874 !(flags & DicomToJsonFlags_IncludePrivateTags)) | |
875 { | |
876 continue; | |
877 } | |
878 | |
879 if (!(flags & DicomToJsonFlags_IncludeUnknownTags)) | |
880 { | |
881 DictionaryLocker locker; | |
882 if (locker->findEntry(element->getTag(), NULL) == NULL) | |
883 { | |
884 continue; | |
885 } | |
886 } | |
887 | |
888 DcmEVR evr = element->getTag().getEVR(); | |
889 if (evr == EVR_OB || | |
890 evr == EVR_OF || | |
891 evr == EVR_OW || | |
892 evr == EVR_UN || | |
893 evr == EVR_ox) | |
894 { | |
895 // This is a binary tag | |
896 if ((tag == DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludePixelData)) || | |
897 (tag != DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludeBinary))) | |
898 { | |
899 continue; | |
900 } | |
901 } | |
902 | |
903 FromDcmtkBridge::ElementToJson(parent, *element, format, flags, maxStringLength, encoding); | |
904 } | |
905 } | |
906 | |
907 | |
908 void FromDcmtkBridge::ExtractDicomAsJson(Json::Value& target, | |
909 DcmDataset& dataset, | |
910 DicomToJsonFormat format, | |
911 DicomToJsonFlags flags, | |
912 unsigned int maxStringLength, | |
913 Encoding defaultEncoding) | |
914 { | |
915 Encoding encoding = DetectEncoding(dataset, defaultEncoding); | |
916 | |
917 target = Json::objectValue; | |
918 DatasetToJson(target, dataset, format, flags, maxStringLength, encoding); | |
919 } | |
920 | |
921 | |
922 void FromDcmtkBridge::ExtractHeaderAsJson(Json::Value& target, | |
923 DcmMetaInfo& dataset, | |
924 DicomToJsonFormat format, | |
925 DicomToJsonFlags flags, | |
926 unsigned int maxStringLength) | |
927 { | |
928 target = Json::objectValue; | |
929 DatasetToJson(target, dataset, format, flags, maxStringLength, Encoding_Ascii); | |
930 } | |
931 | |
932 | |
933 | |
934 static std::string GetTagNameInternal(DcmTag& tag) | |
935 { | |
936 { | |
937 // Some patches for important tags because of different DICOM | |
938 // dictionaries between DCMTK versions | |
939 DicomTag tmp(tag.getGroup(), tag.getElement()); | |
940 std::string n = tmp.GetMainTagsName(); | |
941 if (n.size() != 0) | |
942 { | |
943 return n; | |
944 } | |
945 // End of patches | |
946 } | |
947 | |
948 #if 0 | |
949 // This version explicitly calls the dictionary | |
950 const DcmDataDictionary& dict = dcmDataDict.rdlock(); | |
951 const DcmDictEntry* entry = dict.findEntry(tag, NULL); | |
952 | |
953 std::string s(DcmTag_ERROR_TagName); | |
954 if (entry != NULL) | |
955 { | |
956 s = std::string(entry->getTagName()); | |
957 } | |
958 | |
959 dcmDataDict.unlock(); | |
960 return s; | |
961 #else | |
962 const char* name = tag.getTagName(); | |
963 if (name == NULL) | |
964 { | |
965 return DcmTag_ERROR_TagName; | |
966 } | |
967 else | |
968 { | |
969 return std::string(name); | |
970 } | |
971 #endif | |
972 } | |
973 | |
974 | |
975 std::string FromDcmtkBridge::GetTagName(const DicomTag& t, | |
976 const std::string& privateCreator) | |
977 { | |
978 DcmTag tag(t.GetGroup(), t.GetElement()); | |
979 | |
980 if (!privateCreator.empty()) | |
981 { | |
982 tag.setPrivateCreator(privateCreator.c_str()); | |
983 } | |
984 | |
985 return GetTagNameInternal(tag); | |
986 } | |
987 | |
988 | |
989 std::string FromDcmtkBridge::GetTagName(const DcmElement& element) | |
990 { | |
991 // Copy the tag to ensure const-correctness of DcmElement. Note | |
992 // that the private creator information is also copied. | |
993 DcmTag tag(element.getTag()); | |
994 | |
995 return GetTagNameInternal(tag); | |
996 } | |
997 | |
998 | |
999 | |
1000 DicomTag FromDcmtkBridge::ParseTag(const char* name) | |
1001 { | |
1002 if (strlen(name) == 9 && | |
1003 isxdigit(name[0]) && | |
1004 isxdigit(name[1]) && | |
1005 isxdigit(name[2]) && | |
1006 isxdigit(name[3]) && | |
1007 (name[4] == '-' || name[4] == ',') && | |
1008 isxdigit(name[5]) && | |
1009 isxdigit(name[6]) && | |
1010 isxdigit(name[7]) && | |
1011 isxdigit(name[8])) | |
1012 { | |
1013 uint16_t group = GetTagValue(name); | |
1014 uint16_t element = GetTagValue(name + 5); | |
1015 return DicomTag(group, element); | |
1016 } | |
1017 | |
1018 if (strlen(name) == 8 && | |
1019 isxdigit(name[0]) && | |
1020 isxdigit(name[1]) && | |
1021 isxdigit(name[2]) && | |
1022 isxdigit(name[3]) && | |
1023 isxdigit(name[4]) && | |
1024 isxdigit(name[5]) && | |
1025 isxdigit(name[6]) && | |
1026 isxdigit(name[7])) | |
1027 { | |
1028 uint16_t group = GetTagValue(name); | |
1029 uint16_t element = GetTagValue(name + 4); | |
1030 return DicomTag(group, element); | |
1031 } | |
1032 | |
1033 #if 0 | |
1034 const DcmDataDictionary& dict = dcmDataDict.rdlock(); | |
1035 const DcmDictEntry* entry = dict.findEntry(name); | |
1036 | |
1037 if (entry == NULL) | |
1038 { | |
1039 dcmDataDict.unlock(); | |
1040 throw OrthancException(ErrorCode_UnknownDicomTag); | |
1041 } | |
1042 else | |
1043 { | |
1044 DcmTagKey key = entry->getKey(); | |
1045 DicomTag tag(key.getGroup(), key.getElement()); | |
1046 dcmDataDict.unlock(); | |
1047 return tag; | |
1048 } | |
1049 #else | |
1050 DcmTag tag; | |
1051 if (DcmTag::findTagFromName(name, tag).good()) | |
1052 { | |
1053 return DicomTag(tag.getGTag(), tag.getETag()); | |
1054 } | |
1055 else | |
1056 { | |
1057 throw OrthancException(ErrorCode_UnknownDicomTag); | |
1058 } | |
1059 #endif | |
1060 } | |
1061 | |
1062 | |
1063 bool FromDcmtkBridge::IsUnknownTag(const DicomTag& tag) | |
1064 { | |
1065 DcmTag tmp(tag.GetGroup(), tag.GetElement()); | |
1066 return tmp.isUnknownVR(); | |
1067 } | |
1068 | |
1069 | |
1070 void FromDcmtkBridge::ToJson(Json::Value& result, | |
1071 const DicomMap& values, | |
1072 bool simplify) | |
1073 { | |
1074 if (result.type() != Json::objectValue) | |
1075 { | |
1076 throw OrthancException(ErrorCode_BadParameterType); | |
1077 } | |
1078 | |
1079 result.clear(); | |
1080 | |
1081 for (DicomMap::Map::const_iterator | |
1082 it = values.map_.begin(); it != values.map_.end(); ++it) | |
1083 { | |
1084 // TODO Inject PrivateCreator if some is available in the DicomMap? | |
1085 const std::string tagName = GetTagName(it->first, ""); | |
1086 | |
1087 if (simplify) | |
1088 { | |
1089 if (it->second->IsNull()) | |
1090 { | |
1091 result[tagName] = Json::nullValue; | |
1092 } | |
1093 else | |
1094 { | |
1095 // TODO IsBinary | |
1096 result[tagName] = it->second->GetContent(); | |
1097 } | |
1098 } | |
1099 else | |
1100 { | |
1101 Json::Value value = Json::objectValue; | |
1102 | |
1103 value["Name"] = tagName; | |
1104 | |
1105 if (it->second->IsNull()) | |
1106 { | |
1107 value["Type"] = "Null"; | |
1108 value["Value"] = Json::nullValue; | |
1109 } | |
1110 else | |
1111 { | |
1112 // TODO IsBinary | |
1113 value["Type"] = "String"; | |
1114 value["Value"] = it->second->GetContent(); | |
1115 } | |
1116 | |
1117 result[it->first.Format()] = value; | |
1118 } | |
1119 } | |
1120 } | |
1121 | |
1122 | |
1123 std::string FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType level) | |
1124 { | |
1125 char uid[100]; | |
1126 | |
1127 switch (level) | |
1128 { | |
1129 case ResourceType_Patient: | |
1130 // The "PatientID" field is of type LO (Long String), 64 | |
1131 // Bytes Maximum. An UUID is of length 36, thus it can be used | |
1132 // as a random PatientID. | |
1133 return SystemToolbox::GenerateUuid(); | |
1134 | |
1135 case ResourceType_Instance: | |
1136 return dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT); | |
1137 | |
1138 case ResourceType_Series: | |
1139 return dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT); | |
1140 | |
1141 case ResourceType_Study: | |
1142 return dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT); | |
1143 | |
1144 default: | |
1145 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
1146 } | |
1147 } | |
1148 | |
1149 bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, | |
1150 DcmDataset& dataSet) | |
1151 { | |
1152 // Determine the transfer syntax which shall be used to write the | |
1153 // information to the file. We always switch to the Little Endian | |
1154 // syntax, with explicit length. | |
1155 | |
1156 // http://support.dcmtk.org/docs/dcxfer_8h-source.html | |
1157 | |
1158 | |
1159 /** | |
1160 * Note that up to Orthanc 0.7.1 (inclusive), the | |
1161 * "EXS_LittleEndianExplicit" was always used to save the DICOM | |
1162 * dataset into memory. We now keep the original transfer syntax | |
1163 * (if available). | |
1164 **/ | |
1165 E_TransferSyntax xfer = dataSet.getOriginalXfer(); | |
1166 if (xfer == EXS_Unknown) | |
1167 { | |
1168 // No information about the original transfer syntax: This is | |
1169 // most probably a DICOM dataset that was read from memory. | |
1170 xfer = EXS_LittleEndianExplicit; | |
1171 } | |
1172 | |
1173 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength; | |
1174 | |
1175 // Create the meta-header information | |
1176 DcmFileFormat ff(&dataSet); | |
1177 ff.validateMetaInfo(xfer); | |
1178 ff.removeInvalidGroups(); | |
1179 | |
1180 // Create a memory buffer with the proper size | |
1181 { | |
1182 const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType); // (*) | |
1183 buffer.resize(estimatedSize); | |
1184 } | |
1185 | |
1186 DcmOutputBufferStream ob(&buffer[0], buffer.size()); | |
1187 | |
1188 // Fill the memory buffer with the meta-header and the dataset | |
1189 ff.transferInit(); | |
1190 OFCondition c = ff.write(ob, xfer, encodingType, NULL, | |
1191 /*opt_groupLength*/ EGL_recalcGL, | |
1192 /*opt_paddingType*/ EPD_withoutPadding); | |
1193 ff.transferEnd(); | |
1194 | |
1195 if (c.good()) | |
1196 { | |
1197 // The DICOM file is successfully written, truncate the target | |
1198 // buffer if its size was overestimated by (*) | |
1199 ob.flush(); | |
1200 | |
1201 size_t effectiveSize = static_cast<size_t>(ob.tell()); | |
1202 if (effectiveSize < buffer.size()) | |
1203 { | |
1204 buffer.resize(effectiveSize); | |
1205 } | |
1206 | |
1207 return true; | |
1208 } | |
1209 else | |
1210 { | |
1211 // Error | |
1212 buffer.clear(); | |
1213 return false; | |
1214 } | |
1215 } | |
1216 | |
1217 | |
1218 ValueRepresentation FromDcmtkBridge::LookupValueRepresentation(const DicomTag& tag) | |
1219 { | |
1220 DcmTag t(tag.GetGroup(), tag.GetElement()); | |
1221 return Convert(t.getEVR()); | |
1222 } | |
1223 | |
1224 ValueRepresentation FromDcmtkBridge::Convert(const DcmEVR vr) | |
1225 { | |
1226 switch (vr) | |
1227 { | |
1228 case EVR_AE: | |
1229 return ValueRepresentation_ApplicationEntity; | |
1230 | |
1231 case EVR_AS: | |
1232 return ValueRepresentation_AgeString; | |
1233 | |
1234 case EVR_AT: | |
1235 return ValueRepresentation_AttributeTag; | |
1236 | |
1237 case EVR_CS: | |
1238 return ValueRepresentation_CodeString; | |
1239 | |
1240 case EVR_DA: | |
1241 return ValueRepresentation_Date; | |
1242 | |
1243 case EVR_DS: | |
1244 return ValueRepresentation_DecimalString; | |
1245 | |
1246 case EVR_DT: | |
1247 return ValueRepresentation_DateTime; | |
1248 | |
1249 case EVR_FL: | |
1250 return ValueRepresentation_FloatingPointSingle; | |
1251 | |
1252 case EVR_FD: | |
1253 return ValueRepresentation_FloatingPointDouble; | |
1254 | |
1255 case EVR_IS: | |
1256 return ValueRepresentation_IntegerString; | |
1257 | |
1258 case EVR_LO: | |
1259 return ValueRepresentation_LongString; | |
1260 | |
1261 case EVR_LT: | |
1262 return ValueRepresentation_LongText; | |
1263 | |
1264 case EVR_OB: | |
1265 return ValueRepresentation_OtherByte; | |
1266 | |
1267 // Not supported as of DCMTK 3.6.0 | |
1268 /*case EVR_OD: | |
1269 return ValueRepresentation_OtherDouble;*/ | |
1270 | |
1271 case EVR_OF: | |
1272 return ValueRepresentation_OtherFloat; | |
1273 | |
1274 // Not supported as of DCMTK 3.6.0 | |
1275 /*case EVR_OL: | |
1276 return ValueRepresentation_OtherLong;*/ | |
1277 | |
1278 case EVR_OW: | |
1279 return ValueRepresentation_OtherWord; | |
1280 | |
1281 case EVR_PN: | |
1282 return ValueRepresentation_PersonName; | |
1283 | |
1284 case EVR_SH: | |
1285 return ValueRepresentation_ShortString; | |
1286 | |
1287 case EVR_SL: | |
1288 return ValueRepresentation_SignedLong; | |
1289 | |
1290 case EVR_SQ: | |
1291 return ValueRepresentation_Sequence; | |
1292 | |
1293 case EVR_SS: | |
1294 return ValueRepresentation_SignedShort; | |
1295 | |
1296 case EVR_ST: | |
1297 return ValueRepresentation_ShortText; | |
1298 | |
1299 case EVR_TM: | |
1300 return ValueRepresentation_Time; | |
1301 | |
1302 // Not supported as of DCMTK 3.6.0 | |
1303 /*case EVR_UC: | |
1304 return ValueRepresentation_UnlimitedCharacters;*/ | |
1305 | |
1306 case EVR_UI: | |
1307 return ValueRepresentation_UniqueIdentifier; | |
1308 | |
1309 case EVR_UL: | |
1310 return ValueRepresentation_UnsignedLong; | |
1311 | |
1312 case EVR_UN: | |
1313 return ValueRepresentation_Unknown; | |
1314 | |
1315 // Not supported as of DCMTK 3.6.0 | |
1316 /*case EVR_UR: | |
1317 return ValueRepresentation_UniversalResource;*/ | |
1318 | |
1319 case EVR_US: | |
1320 return ValueRepresentation_UnsignedShort; | |
1321 | |
1322 case EVR_UT: | |
1323 return ValueRepresentation_UnlimitedText; | |
1324 | |
1325 default: | |
1326 return ValueRepresentation_NotSupported; | |
1327 } | |
1328 } | |
1329 | |
1330 | |
1331 static bool IsBinaryTag(const DcmTag& key) | |
1332 { | |
1333 return (key.isUnknownVR() || | |
1334 key.getEVR() == EVR_OB || | |
1335 key.getEVR() == EVR_OF || | |
1336 key.getEVR() == EVR_OW || | |
1337 key.getEVR() == EVR_UN || | |
1338 key.getEVR() == EVR_ox); | |
1339 } | |
1340 | |
1341 | |
1342 DcmElement* FromDcmtkBridge::CreateElementForTag(const DicomTag& tag) | |
1343 { | |
1344 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
1345 | |
1346 if (tag.IsPrivate() || | |
1347 IsBinaryTag(key)) | |
1348 { | |
1349 return new DcmOtherByteOtherWord(key); | |
1350 } | |
1351 | |
1352 switch (key.getEVR()) | |
1353 { | |
1354 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
1355 | |
1356 /** | |
1357 * Binary types, handled above | |
1358 **/ | |
1359 | |
1360 case EVR_OB: // other byte | |
1361 case EVR_OF: // other float | |
1362 case EVR_OW: // other word | |
1363 case EVR_UN: // unknown value representation | |
1364 case EVR_ox: // OB or OW depending on context | |
1365 throw OrthancException(ErrorCode_InternalError); | |
1366 | |
1367 | |
1368 /** | |
1369 * String types. | |
1370 * http://support.dcmtk.org/docs/classDcmByteString.html | |
1371 **/ | |
1372 | |
1373 case EVR_AS: // age string | |
1374 return new DcmAgeString(key); | |
1375 | |
1376 case EVR_AE: // application entity title | |
1377 return new DcmApplicationEntity(key); | |
1378 | |
1379 case EVR_CS: // code string | |
1380 return new DcmCodeString(key); | |
1381 | |
1382 case EVR_DA: // date string | |
1383 return new DcmDate(key); | |
1384 | |
1385 case EVR_DT: // date time string | |
1386 return new DcmDateTime(key); | |
1387 | |
1388 case EVR_DS: // decimal string | |
1389 return new DcmDecimalString(key); | |
1390 | |
1391 case EVR_IS: // integer string | |
1392 return new DcmIntegerString(key); | |
1393 | |
1394 case EVR_TM: // time string | |
1395 return new DcmTime(key); | |
1396 | |
1397 case EVR_UI: // unique identifier | |
1398 return new DcmUniqueIdentifier(key); | |
1399 | |
1400 case EVR_ST: // short text | |
1401 return new DcmShortText(key); | |
1402 | |
1403 case EVR_LO: // long string | |
1404 return new DcmLongString(key); | |
1405 | |
1406 case EVR_LT: // long text | |
1407 return new DcmLongText(key); | |
1408 | |
1409 case EVR_UT: // unlimited text | |
1410 return new DcmUnlimitedText(key); | |
1411 | |
1412 case EVR_SH: // short string | |
1413 return new DcmShortString(key); | |
1414 | |
1415 case EVR_PN: // person name | |
1416 return new DcmPersonName(key); | |
1417 | |
1418 | |
1419 /** | |
1420 * Numerical types | |
1421 **/ | |
1422 | |
1423 case EVR_SL: // signed long | |
1424 return new DcmSignedLong(key); | |
1425 | |
1426 case EVR_SS: // signed short | |
1427 return new DcmSignedShort(key); | |
1428 | |
1429 case EVR_UL: // unsigned long | |
1430 return new DcmUnsignedLong(key); | |
1431 | |
1432 case EVR_US: // unsigned short | |
1433 return new DcmUnsignedShort(key); | |
1434 | |
1435 case EVR_FL: // float single-precision | |
1436 return new DcmFloatingPointSingle(key); | |
1437 | |
1438 case EVR_FD: // float double-precision | |
1439 return new DcmFloatingPointDouble(key); | |
1440 | |
1441 | |
1442 /** | |
1443 * Sequence types, should never occur at this point. | |
1444 **/ | |
1445 | |
1446 case EVR_SQ: // sequence of items | |
1447 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
1448 | |
1449 | |
1450 /** | |
1451 * TODO | |
1452 **/ | |
1453 | |
1454 case EVR_AT: // attribute tag | |
1455 throw OrthancException(ErrorCode_NotImplemented); | |
1456 | |
1457 | |
1458 /** | |
1459 * Internal to DCMTK. | |
1460 **/ | |
1461 | |
1462 case EVR_xs: // SS or US depending on context | |
1463 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
1464 case EVR_na: // na="not applicable", for data which has no VR | |
1465 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
1466 case EVR_item: // used internally for items | |
1467 case EVR_metainfo: // used internally for meta info datasets | |
1468 case EVR_dataset: // used internally for datasets | |
1469 case EVR_fileFormat: // used internally for DICOM files | |
1470 case EVR_dicomDir: // used internally for DICOMDIR objects | |
1471 case EVR_dirRecord: // used internally for DICOMDIR records | |
1472 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
1473 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
1474 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
1475 case EVR_PixelData: // used internally for uncompressed pixeld data | |
1476 case EVR_OverlayData: // used internally for overlay data | |
1477 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
1478 default: | |
1479 break; | |
1480 } | |
1481 | |
1482 throw OrthancException(ErrorCode_InternalError); | |
1483 } | |
1484 | |
1485 | |
1486 | |
1487 void FromDcmtkBridge::FillElementWithString(DcmElement& element, | |
1488 const DicomTag& tag, | |
1489 const std::string& utf8Value, | |
1490 bool decodeDataUriScheme, | |
1491 Encoding dicomEncoding) | |
1492 { | |
1493 std::string binary; | |
1494 const std::string* decoded = &utf8Value; | |
1495 | |
1496 if (decodeDataUriScheme && | |
1497 boost::starts_with(utf8Value, "data:application/octet-stream;base64,")) | |
1498 { | |
1499 std::string mime; | |
1500 if (!Toolbox::DecodeDataUriScheme(mime, binary, utf8Value)) | |
1501 { | |
1502 throw OrthancException(ErrorCode_BadFileFormat); | |
1503 } | |
1504 | |
1505 decoded = &binary; | |
1506 } | |
1507 else if (dicomEncoding != Encoding_Utf8) | |
1508 { | |
1509 binary = Toolbox::ConvertFromUtf8(utf8Value, dicomEncoding); | |
1510 decoded = &binary; | |
1511 } | |
1512 | |
1513 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
1514 | |
1515 if (tag.IsPrivate() || | |
1516 IsBinaryTag(key)) | |
1517 { | |
1518 if (element.putUint8Array((const Uint8*) decoded->c_str(), decoded->size()).good()) | |
1519 { | |
1520 return; | |
1521 } | |
1522 else | |
1523 { | |
1524 throw OrthancException(ErrorCode_InternalError); | |
1525 } | |
1526 } | |
1527 | |
1528 bool ok = false; | |
1529 | |
1530 try | |
1531 { | |
1532 switch (key.getEVR()) | |
1533 { | |
1534 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
1535 | |
1536 /** | |
1537 * TODO. | |
1538 **/ | |
1539 | |
1540 case EVR_OB: // other byte | |
1541 case EVR_OF: // other float | |
1542 case EVR_OW: // other word | |
1543 case EVR_AT: // attribute tag | |
1544 throw OrthancException(ErrorCode_NotImplemented); | |
1545 | |
1546 case EVR_UN: // unknown value representation | |
1547 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
1548 | |
1549 | |
1550 /** | |
1551 * String types. | |
1552 **/ | |
1553 | |
1554 case EVR_DS: // decimal string | |
1555 case EVR_IS: // integer string | |
1556 case EVR_AS: // age string | |
1557 case EVR_DA: // date string | |
1558 case EVR_DT: // date time string | |
1559 case EVR_TM: // time string | |
1560 case EVR_AE: // application entity title | |
1561 case EVR_CS: // code string | |
1562 case EVR_SH: // short string | |
1563 case EVR_LO: // long string | |
1564 case EVR_ST: // short text | |
1565 case EVR_LT: // long text | |
1566 case EVR_UT: // unlimited text | |
1567 case EVR_PN: // person name | |
1568 case EVR_UI: // unique identifier | |
1569 { | |
1570 ok = element.putString(decoded->c_str()).good(); | |
1571 break; | |
1572 } | |
1573 | |
1574 | |
1575 /** | |
1576 * Numerical types | |
1577 **/ | |
1578 | |
1579 case EVR_SL: // signed long | |
1580 { | |
1581 ok = element.putSint32(boost::lexical_cast<Sint32>(*decoded)).good(); | |
1582 break; | |
1583 } | |
1584 | |
1585 case EVR_SS: // signed short | |
1586 { | |
1587 ok = element.putSint16(boost::lexical_cast<Sint16>(*decoded)).good(); | |
1588 break; | |
1589 } | |
1590 | |
1591 case EVR_UL: // unsigned long | |
1592 { | |
1593 ok = element.putUint32(boost::lexical_cast<Uint32>(*decoded)).good(); | |
1594 break; | |
1595 } | |
1596 | |
1597 case EVR_US: // unsigned short | |
1598 { | |
1599 ok = element.putUint16(boost::lexical_cast<Uint16>(*decoded)).good(); | |
1600 break; | |
1601 } | |
1602 | |
1603 case EVR_FL: // float single-precision | |
1604 { | |
1605 ok = element.putFloat32(boost::lexical_cast<float>(*decoded)).good(); | |
1606 break; | |
1607 } | |
1608 | |
1609 case EVR_FD: // float double-precision | |
1610 { | |
1611 ok = element.putFloat64(boost::lexical_cast<double>(*decoded)).good(); | |
1612 break; | |
1613 } | |
1614 | |
1615 | |
1616 /** | |
1617 * Sequence types, should never occur at this point. | |
1618 **/ | |
1619 | |
1620 case EVR_SQ: // sequence of items | |
1621 { | |
1622 ok = false; | |
1623 break; | |
1624 } | |
1625 | |
1626 | |
1627 /** | |
1628 * Internal to DCMTK. | |
1629 **/ | |
1630 | |
1631 case EVR_ox: // OB or OW depending on context | |
1632 case EVR_xs: // SS or US depending on context | |
1633 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
1634 case EVR_na: // na="not applicable", for data which has no VR | |
1635 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
1636 case EVR_item: // used internally for items | |
1637 case EVR_metainfo: // used internally for meta info datasets | |
1638 case EVR_dataset: // used internally for datasets | |
1639 case EVR_fileFormat: // used internally for DICOM files | |
1640 case EVR_dicomDir: // used internally for DICOMDIR objects | |
1641 case EVR_dirRecord: // used internally for DICOMDIR records | |
1642 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
1643 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
1644 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
1645 case EVR_PixelData: // used internally for uncompressed pixeld data | |
1646 case EVR_OverlayData: // used internally for overlay data | |
1647 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
1648 default: | |
1649 break; | |
1650 } | |
1651 } | |
1652 catch (boost::bad_lexical_cast&) | |
1653 { | |
1654 ok = false; | |
1655 } | |
1656 | |
1657 if (!ok) | |
1658 { | |
1659 LOG(ERROR) << "While creating a DICOM instance, tag (" << tag.Format() | |
1660 << ") has out-of-range value: \"" << *decoded << "\""; | |
1661 throw OrthancException(ErrorCode_BadFileFormat); | |
1662 } | |
1663 } | |
1664 | |
1665 | |
1666 DcmElement* FromDcmtkBridge::FromJson(const DicomTag& tag, | |
1667 const Json::Value& value, | |
1668 bool decodeDataUriScheme, | |
1669 Encoding dicomEncoding) | |
1670 { | |
1671 std::auto_ptr<DcmElement> element; | |
1672 | |
1673 switch (value.type()) | |
1674 { | |
1675 case Json::stringValue: | |
1676 element.reset(CreateElementForTag(tag)); | |
1677 FillElementWithString(*element, tag, value.asString(), decodeDataUriScheme, dicomEncoding); | |
1678 break; | |
1679 | |
1680 case Json::nullValue: | |
1681 element.reset(CreateElementForTag(tag)); | |
1682 FillElementWithString(*element, tag, "", decodeDataUriScheme, dicomEncoding); | |
1683 break; | |
1684 | |
1685 case Json::arrayValue: | |
1686 { | |
1687 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
1688 if (key.getEVR() != EVR_SQ) | |
1689 { | |
1690 throw OrthancException(ErrorCode_BadParameterType); | |
1691 } | |
1692 | |
1693 DcmSequenceOfItems* sequence = new DcmSequenceOfItems(key); | |
1694 element.reset(sequence); | |
1695 | |
1696 for (Json::Value::ArrayIndex i = 0; i < value.size(); i++) | |
1697 { | |
1698 std::auto_ptr<DcmItem> item(new DcmItem); | |
1699 | |
1700 Json::Value::Members members = value[i].getMemberNames(); | |
1701 for (Json::Value::ArrayIndex j = 0; j < members.size(); j++) | |
1702 { | |
1703 item->insert(FromJson(ParseTag(members[j]), value[i][members[j]], decodeDataUriScheme, dicomEncoding)); | |
1704 } | |
1705 | |
1706 sequence->append(item.release()); | |
1707 } | |
1708 | |
1709 break; | |
1710 } | |
1711 | |
1712 default: | |
1713 throw OrthancException(ErrorCode_BadParameterType); | |
1714 } | |
1715 | |
1716 return element.release(); | |
1717 } | |
1718 | |
1719 | |
1720 DcmPixelSequence* FromDcmtkBridge::GetPixelSequence(DcmDataset& dataset) | |
1721 { | |
1722 DcmElement *element = NULL; | |
1723 if (!dataset.findAndGetElement(DCM_PixelData, element).good()) | |
1724 { | |
1725 throw OrthancException(ErrorCode_BadFileFormat); | |
1726 } | |
1727 | |
1728 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); | |
1729 DcmPixelSequence* pixelSequence = NULL; | |
1730 if (!pixelData.getEncapsulatedRepresentation | |
1731 (dataset.getOriginalXfer(), NULL, pixelSequence).good()) | |
1732 { | |
1733 return NULL; | |
1734 } | |
1735 else | |
1736 { | |
1737 return pixelSequence; | |
1738 } | |
1739 } | |
1740 | |
1741 | |
1742 Encoding FromDcmtkBridge::ExtractEncoding(const Json::Value& json, | |
1743 Encoding defaultEncoding) | |
1744 { | |
1745 if (json.type() != Json::objectValue) | |
1746 { | |
1747 throw OrthancException(ErrorCode_BadParameterType); | |
1748 } | |
1749 | |
1750 Encoding encoding = defaultEncoding; | |
1751 | |
1752 const Json::Value::Members tags = json.getMemberNames(); | |
1753 | |
1754 // Look for SpecificCharacterSet (0008,0005) in the JSON file | |
1755 for (size_t i = 0; i < tags.size(); i++) | |
1756 { | |
1757 DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]); | |
1758 if (tag == DICOM_TAG_SPECIFIC_CHARACTER_SET) | |
1759 { | |
1760 const Json::Value& value = json[tags[i]]; | |
1761 if (value.type() != Json::stringValue || | |
1762 (value.asString().length() != 0 && | |
1763 !GetDicomEncoding(encoding, value.asCString()))) | |
1764 { | |
1765 LOG(ERROR) << "Unknown encoding while creating DICOM from JSON: " << value; | |
1766 throw OrthancException(ErrorCode_BadRequest); | |
1767 } | |
1768 | |
1769 if (value.asString().length() == 0) | |
1770 { | |
1771 return defaultEncoding; | |
1772 } | |
1773 } | |
1774 } | |
1775 | |
1776 return encoding; | |
1777 } | |
1778 | |
1779 | |
1780 static void SetString(DcmDataset& target, | |
1781 const DcmTag& tag, | |
1782 const std::string& value) | |
1783 { | |
1784 if (!target.putAndInsertString(tag, value.c_str()).good()) | |
1785 { | |
1786 throw OrthancException(ErrorCode_InternalError); | |
1787 } | |
1788 } | |
1789 | |
1790 | |
1791 DcmDataset* FromDcmtkBridge::FromJson(const Json::Value& json, // Encoded using UTF-8 | |
1792 bool generateIdentifiers, | |
1793 bool decodeDataUriScheme, | |
1794 Encoding defaultEncoding) | |
1795 { | |
1796 std::auto_ptr<DcmDataset> result(new DcmDataset); | |
1797 Encoding encoding = ExtractEncoding(json, defaultEncoding); | |
1798 | |
1799 SetString(*result, DCM_SpecificCharacterSet, GetDicomSpecificCharacterSet(encoding)); | |
1800 | |
1801 const Json::Value::Members tags = json.getMemberNames(); | |
1802 | |
1803 bool hasPatientId = false; | |
1804 bool hasStudyInstanceUid = false; | |
1805 bool hasSeriesInstanceUid = false; | |
1806 bool hasSopInstanceUid = false; | |
1807 | |
1808 for (size_t i = 0; i < tags.size(); i++) | |
1809 { | |
1810 DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]); | |
1811 const Json::Value& value = json[tags[i]]; | |
1812 | |
1813 if (tag == DICOM_TAG_PATIENT_ID) | |
1814 { | |
1815 hasPatientId = true; | |
1816 } | |
1817 else if (tag == DICOM_TAG_STUDY_INSTANCE_UID) | |
1818 { | |
1819 hasStudyInstanceUid = true; | |
1820 } | |
1821 else if (tag == DICOM_TAG_SERIES_INSTANCE_UID) | |
1822 { | |
1823 hasSeriesInstanceUid = true; | |
1824 } | |
1825 else if (tag == DICOM_TAG_SOP_INSTANCE_UID) | |
1826 { | |
1827 hasSopInstanceUid = true; | |
1828 } | |
1829 | |
1830 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) | |
1831 { | |
1832 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, encoding)); | |
1833 const DcmTagKey& tag = element->getTag(); | |
1834 | |
1835 result->findAndDeleteElement(tag); | |
1836 | |
1837 DcmElement* tmp = element.release(); | |
1838 if (!result->insert(tmp, false, false).good()) | |
1839 { | |
1840 delete tmp; | |
1841 throw OrthancException(ErrorCode_InternalError); | |
1842 } | |
1843 } | |
1844 } | |
1845 | |
1846 if (!hasPatientId && | |
1847 generateIdentifiers) | |
1848 { | |
1849 SetString(*result, DCM_PatientID, GenerateUniqueIdentifier(ResourceType_Patient)); | |
1850 } | |
1851 | |
1852 if (!hasStudyInstanceUid && | |
1853 generateIdentifiers) | |
1854 { | |
1855 SetString(*result, DCM_StudyInstanceUID, GenerateUniqueIdentifier(ResourceType_Study)); | |
1856 } | |
1857 | |
1858 if (!hasSeriesInstanceUid && | |
1859 generateIdentifiers) | |
1860 { | |
1861 SetString(*result, DCM_SeriesInstanceUID, GenerateUniqueIdentifier(ResourceType_Series)); | |
1862 } | |
1863 | |
1864 if (!hasSopInstanceUid && | |
1865 generateIdentifiers) | |
1866 { | |
1867 SetString(*result, DCM_SOPInstanceUID, GenerateUniqueIdentifier(ResourceType_Instance)); | |
1868 } | |
1869 | |
1870 return result.release(); | |
1871 } | |
1872 | |
1873 | |
1874 DcmFileFormat* FromDcmtkBridge::LoadFromMemoryBuffer(const void* buffer, | |
1875 size_t size) | |
1876 { | |
1877 DcmInputBufferStream is; | |
1878 if (size > 0) | |
1879 { | |
1880 is.setBuffer(buffer, size); | |
1881 } | |
1882 is.setEos(); | |
1883 | |
1884 std::auto_ptr<DcmFileFormat> result(new DcmFileFormat); | |
1885 | |
1886 result->transferInit(); | |
1887 if (!result->read(is).good()) | |
1888 { | |
1889 throw OrthancException(ErrorCode_BadFileFormat); | |
1890 } | |
1891 | |
1892 result->loadAllDataIntoMemory(); | |
1893 result->transferEnd(); | |
1894 | |
1895 return result.release(); | |
1896 } | |
1897 | |
1898 | |
1899 void FromDcmtkBridge::FromJson(DicomMap& target, | |
1900 const Json::Value& source) | |
1901 { | |
1902 if (source.type() != Json::objectValue) | |
1903 { | |
1904 throw OrthancException(ErrorCode_BadFileFormat); | |
1905 } | |
1906 | |
1907 target.Clear(); | |
1908 | |
1909 Json::Value::Members members = source.getMemberNames(); | |
1910 | |
1911 for (size_t i = 0; i < members.size(); i++) | |
1912 { | |
1913 const Json::Value& value = source[members[i]]; | |
1914 | |
1915 if (value.type() != Json::stringValue) | |
1916 { | |
1917 throw OrthancException(ErrorCode_BadFileFormat); | |
1918 } | |
1919 | |
1920 target.SetValue(ParseTag(members[i]), value.asString(), false); | |
1921 } | |
1922 } | |
1923 | |
1924 | |
1925 void FromDcmtkBridge::ChangeStringEncoding(DcmItem& dataset, | |
1926 Encoding source, | |
1927 Encoding target) | |
1928 { | |
1929 // Recursive exploration of a dataset to change the encoding of | |
1930 // each string-like element | |
1931 | |
1932 if (source == target) | |
1933 { | |
1934 return; | |
1935 } | |
1936 | |
1937 for (unsigned long i = 0; i < dataset.card(); i++) | |
1938 { | |
1939 DcmElement* element = dataset.getElement(i); | |
1940 if (element) | |
1941 { | |
1942 if (element->isLeaf()) | |
1943 { | |
1944 char *c = NULL; | |
1945 if (element->isaString() && | |
1946 element->getString(c).good() && | |
1947 c != NULL) | |
1948 { | |
1949 std::string a = Toolbox::ConvertToUtf8(c, source); | |
1950 std::string b = Toolbox::ConvertFromUtf8(a, target); | |
1951 element->putString(b.c_str()); | |
1952 } | |
1953 } | |
1954 else | |
1955 { | |
1956 // "All subclasses of DcmElement except for DcmSequenceOfItems | |
1957 // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset | |
1958 // etc. are not." The following dynamic_cast is thus OK. | |
1959 DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(*element); | |
1960 | |
1961 for (unsigned long j = 0; j < sequence.card(); j++) | |
1962 { | |
1963 ChangeStringEncoding(*sequence.getItem(j), source, target); | |
1964 } | |
1965 } | |
1966 } | |
1967 } | |
1968 } | |
1969 | |
1970 | |
1971 bool FromDcmtkBridge::LookupTransferSyntax(std::string& result, | |
1972 DcmFileFormat& dicom) | |
1973 { | |
1974 const char* value = NULL; | |
1975 | |
1976 if (dicom.getMetaInfo() != NULL && | |
1977 dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() && | |
1978 value != NULL) | |
1979 { | |
1980 result.assign(value); | |
1981 return true; | |
1982 } | |
1983 else | |
1984 { | |
1985 return false; | |
1986 } | |
1987 } | |
1988 | |
1989 | |
1990 #if ORTHANC_ENABLE_LUA == 1 | |
1991 void FromDcmtkBridge::ExecuteToDicom(DicomMap& target, | |
1992 LuaFunctionCall& call) | |
1993 { | |
1994 Json::Value output; | |
1995 call.ExecuteToJson(output, true /* keep strings */); | |
1996 | |
1997 target.Clear(); | |
1998 | |
1999 if (output.type() == Json::arrayValue && | |
2000 output.size() == 0) | |
2001 { | |
2002 // This case happens for empty tables | |
2003 return; | |
2004 } | |
2005 | |
2006 if (output.type() != Json::objectValue) | |
2007 { | |
2008 LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table"; | |
2009 throw OrthancException(ErrorCode_LuaBadOutput); | |
2010 } | |
2011 | |
2012 Json::Value::Members members = output.getMemberNames(); | |
2013 | |
2014 for (size_t i = 0; i < members.size(); i++) | |
2015 { | |
2016 if (output[members[i]].type() != Json::stringValue) | |
2017 { | |
2018 LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table mapping names of DICOM tags to strings"; | |
2019 throw OrthancException(ErrorCode_LuaBadOutput); | |
2020 } | |
2021 | |
2022 DicomTag tag(ParseTag(members[i])); | |
2023 target.SetValue(tag, output[members[i]].asString(), false); | |
2024 } | |
2025 } | |
2026 #endif | |
2027 | |
2028 | |
2029 void FromDcmtkBridge::ExtractDicomSummary(DicomMap& target, | |
2030 DcmItem& dataset) | |
2031 { | |
2032 ExtractDicomSummary(target, dataset, | |
2033 ORTHANC_MAXIMUM_TAG_LENGTH, | |
2034 GetDefaultDicomEncoding()); | |
2035 } | |
2036 | |
2037 | |
2038 void FromDcmtkBridge::ExtractDicomAsJson(Json::Value& target, | |
2039 DcmDataset& dataset) | |
2040 { | |
2041 ExtractDicomAsJson(target, dataset, | |
2042 DicomToJsonFormat_Full, | |
2043 DicomToJsonFlags_Default, | |
2044 ORTHANC_MAXIMUM_TAG_LENGTH, | |
2045 GetDefaultDicomEncoding()); | |
2046 } | |
2047 | |
2048 | |
2049 void FromDcmtkBridge::InitializeCodecs() | |
2050 { | |
2051 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1 | |
2052 LOG(WARNING) << "Registering JPEG Lossless codecs in DCMTK"; | |
2053 DJLSDecoderRegistration::registerCodecs(); | |
2054 #endif | |
2055 | |
2056 #if ORTHANC_ENABLE_DCMTK_JPEG == 1 | |
2057 LOG(WARNING) << "Registering JPEG codecs in DCMTK"; | |
2058 DJDecoderRegistration::registerCodecs(); | |
2059 #endif | |
2060 } | |
2061 | |
2062 | |
2063 void FromDcmtkBridge::FinalizeCodecs() | |
2064 { | |
2065 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1 | |
2066 // Unregister JPEG-LS codecs | |
2067 DJLSDecoderRegistration::cleanup(); | |
2068 #endif | |
2069 | |
2070 #if ORTHANC_ENABLE_DCMTK_JPEG == 1 | |
2071 // Unregister JPEG codecs | |
2072 DJDecoderRegistration::cleanup(); | |
2073 #endif | |
2074 } | |
2075 } |