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