Mercurial > hg > orthanc
comparison OrthancFramework/Sources/DicomFormat/DicomMap.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | Core/DicomFormat/DicomMap.cpp@e7003b2203a7 |
children | bf7b9edf6b81 |
comparison
equal
deleted
inserted
replaced
4043:6c6239aec462 | 4044:d25f4c0fa160 |
---|---|
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-2020 Osimis S.A., 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 #include "DicomMap.h" | |
36 | |
37 #include <stdio.h> | |
38 #include <memory> | |
39 | |
40 #include "../Compatibility.h" | |
41 #include "../Endianness.h" | |
42 #include "../Logging.h" | |
43 #include "../OrthancException.h" | |
44 #include "../Toolbox.h" | |
45 #include "DicomArray.h" | |
46 | |
47 | |
48 namespace Orthanc | |
49 { | |
50 namespace | |
51 { | |
52 struct MainDicomTag | |
53 { | |
54 const DicomTag tag_; | |
55 const char* name_; | |
56 }; | |
57 } | |
58 | |
59 static const MainDicomTag PATIENT_MAIN_DICOM_TAGS[] = | |
60 { | |
61 // { DicomTag(0x0010, 0x1010), "PatientAge" }, | |
62 // { DicomTag(0x0010, 0x1040), "PatientAddress" }, | |
63 { DicomTag(0x0010, 0x0010), "PatientName" }, | |
64 { DicomTag(0x0010, 0x0030), "PatientBirthDate" }, | |
65 { DicomTag(0x0010, 0x0040), "PatientSex" }, | |
66 { DicomTag(0x0010, 0x1000), "OtherPatientIDs" }, | |
67 { DICOM_TAG_PATIENT_ID, "PatientID" } | |
68 }; | |
69 | |
70 static const MainDicomTag STUDY_MAIN_DICOM_TAGS[] = | |
71 { | |
72 // { DicomTag(0x0010, 0x1020), "PatientSize" }, | |
73 // { DicomTag(0x0010, 0x1030), "PatientWeight" }, | |
74 { DICOM_TAG_STUDY_DATE, "StudyDate" }, | |
75 { DicomTag(0x0008, 0x0030), "StudyTime" }, | |
76 { DicomTag(0x0020, 0x0010), "StudyID" }, | |
77 { DICOM_TAG_STUDY_DESCRIPTION, "StudyDescription" }, | |
78 { DICOM_TAG_ACCESSION_NUMBER, "AccessionNumber" }, | |
79 { DICOM_TAG_STUDY_INSTANCE_UID, "StudyInstanceUID" }, | |
80 | |
81 // New in db v6 | |
82 { DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION, "RequestedProcedureDescription" }, | |
83 { DICOM_TAG_INSTITUTION_NAME, "InstitutionName" }, | |
84 { DICOM_TAG_REQUESTING_PHYSICIAN, "RequestingPhysician" }, | |
85 { DICOM_TAG_REFERRING_PHYSICIAN_NAME, "ReferringPhysicianName" } | |
86 }; | |
87 | |
88 static const MainDicomTag SERIES_MAIN_DICOM_TAGS[] = | |
89 { | |
90 // { DicomTag(0x0010, 0x1080), "MilitaryRank" }, | |
91 { DicomTag(0x0008, 0x0021), "SeriesDate" }, | |
92 { DicomTag(0x0008, 0x0031), "SeriesTime" }, | |
93 { DICOM_TAG_MODALITY, "Modality" }, | |
94 { DicomTag(0x0008, 0x0070), "Manufacturer" }, | |
95 { DicomTag(0x0008, 0x1010), "StationName" }, | |
96 { DICOM_TAG_SERIES_DESCRIPTION, "SeriesDescription" }, | |
97 { DicomTag(0x0018, 0x0015), "BodyPartExamined" }, | |
98 { DicomTag(0x0018, 0x0024), "SequenceName" }, | |
99 { DicomTag(0x0018, 0x1030), "ProtocolName" }, | |
100 { DicomTag(0x0020, 0x0011), "SeriesNumber" }, | |
101 { DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES, "CardiacNumberOfImages" }, | |
102 { DICOM_TAG_IMAGES_IN_ACQUISITION, "ImagesInAcquisition" }, | |
103 { DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "NumberOfTemporalPositions" }, | |
104 { DICOM_TAG_NUMBER_OF_SLICES, "NumberOfSlices" }, | |
105 { DICOM_TAG_NUMBER_OF_TIME_SLICES, "NumberOfTimeSlices" }, | |
106 { DICOM_TAG_SERIES_INSTANCE_UID, "SeriesInstanceUID" }, | |
107 | |
108 // New in db v6 | |
109 { DICOM_TAG_IMAGE_ORIENTATION_PATIENT, "ImageOrientationPatient" }, | |
110 { DICOM_TAG_SERIES_TYPE, "SeriesType" }, | |
111 { DICOM_TAG_OPERATOR_NAME, "OperatorsName" }, | |
112 { DICOM_TAG_PERFORMED_PROCEDURE_STEP_DESCRIPTION, "PerformedProcedureStepDescription" }, | |
113 { DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION, "AcquisitionDeviceProcessingDescription" }, | |
114 { DICOM_TAG_CONTRAST_BOLUS_AGENT, "ContrastBolusAgent" } | |
115 }; | |
116 | |
117 static const MainDicomTag INSTANCE_MAIN_DICOM_TAGS[] = | |
118 { | |
119 { DicomTag(0x0008, 0x0012), "InstanceCreationDate" }, | |
120 { DicomTag(0x0008, 0x0013), "InstanceCreationTime" }, | |
121 { DicomTag(0x0020, 0x0012), "AcquisitionNumber" }, | |
122 { DICOM_TAG_IMAGE_INDEX, "ImageIndex" }, | |
123 { DICOM_TAG_INSTANCE_NUMBER, "InstanceNumber" }, | |
124 { DICOM_TAG_NUMBER_OF_FRAMES, "NumberOfFrames" }, | |
125 { DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER, "TemporalPositionIdentifier" }, | |
126 { DICOM_TAG_SOP_INSTANCE_UID, "SOPInstanceUID" }, | |
127 | |
128 // New in db v6 | |
129 { DICOM_TAG_IMAGE_POSITION_PATIENT, "ImagePositionPatient" }, | |
130 { DICOM_TAG_IMAGE_COMMENTS, "ImageComments" }, | |
131 | |
132 /** | |
133 * Main DICOM tags that are not part of any release of the | |
134 * database schema yet, and that will be part of future db v7. In | |
135 * the meantime, the user must call "/tools/reconstruct" once to | |
136 * access these tags if the corresponding DICOM files where | |
137 * indexed in the database by an older version of Orthanc. | |
138 **/ | |
139 { DICOM_TAG_IMAGE_ORIENTATION_PATIENT, "ImageOrientationPatient" } // New in Orthanc 1.4.2 | |
140 }; | |
141 | |
142 | |
143 static void LoadMainDicomTags(const MainDicomTag*& tags, | |
144 size_t& size, | |
145 ResourceType level) | |
146 { | |
147 switch (level) | |
148 { | |
149 case ResourceType_Patient: | |
150 tags = PATIENT_MAIN_DICOM_TAGS; | |
151 size = sizeof(PATIENT_MAIN_DICOM_TAGS) / sizeof(MainDicomTag); | |
152 break; | |
153 | |
154 case ResourceType_Study: | |
155 tags = STUDY_MAIN_DICOM_TAGS; | |
156 size = sizeof(STUDY_MAIN_DICOM_TAGS) / sizeof(MainDicomTag); | |
157 break; | |
158 | |
159 case ResourceType_Series: | |
160 tags = SERIES_MAIN_DICOM_TAGS; | |
161 size = sizeof(SERIES_MAIN_DICOM_TAGS) / sizeof(MainDicomTag); | |
162 break; | |
163 | |
164 case ResourceType_Instance: | |
165 tags = INSTANCE_MAIN_DICOM_TAGS; | |
166 size = sizeof(INSTANCE_MAIN_DICOM_TAGS) / sizeof(MainDicomTag); | |
167 break; | |
168 | |
169 default: | |
170 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
171 } | |
172 } | |
173 | |
174 | |
175 static void LoadMainDicomTags(std::map<DicomTag, std::string>& target, | |
176 ResourceType level) | |
177 { | |
178 const MainDicomTag* tags = NULL; | |
179 size_t size; | |
180 LoadMainDicomTags(tags, size, level); | |
181 | |
182 assert(tags != NULL && | |
183 size != 0); | |
184 | |
185 for (size_t i = 0; i < size; i++) | |
186 { | |
187 assert(target.find(tags[i].tag_) == target.end()); | |
188 | |
189 target[tags[i].tag_] = tags[i].name_; | |
190 } | |
191 } | |
192 | |
193 | |
194 namespace | |
195 { | |
196 class DicomTag2 : public DicomTag | |
197 { | |
198 public: | |
199 DicomTag2() : | |
200 DicomTag(0, 0) // To make std::map<> happy | |
201 { | |
202 } | |
203 | |
204 DicomTag2(const DicomTag& tag) : | |
205 DicomTag(tag) | |
206 { | |
207 } | |
208 }; | |
209 } | |
210 | |
211 | |
212 static void LoadMainDicomTags(std::map<std::string, DicomTag2>& target, | |
213 ResourceType level) | |
214 { | |
215 const MainDicomTag* tags = NULL; | |
216 size_t size; | |
217 LoadMainDicomTags(tags, size, level); | |
218 | |
219 assert(tags != NULL && | |
220 size != 0); | |
221 | |
222 for (size_t i = 0; i < size; i++) | |
223 { | |
224 assert(target.find(tags[i].name_) == target.end()); | |
225 | |
226 target[tags[i].name_] = tags[i].tag_; | |
227 } | |
228 } | |
229 | |
230 | |
231 void DicomMap::SetValueInternal(uint16_t group, | |
232 uint16_t element, | |
233 DicomValue* value) | |
234 { | |
235 DicomTag tag(group, element); | |
236 Content::iterator it = content_.find(tag); | |
237 | |
238 if (it != content_.end()) | |
239 { | |
240 delete it->second; | |
241 it->second = value; | |
242 } | |
243 else | |
244 { | |
245 content_.insert(std::make_pair(tag, value)); | |
246 } | |
247 } | |
248 | |
249 | |
250 void DicomMap::Clear() | |
251 { | |
252 for (Content::iterator it = content_.begin(); it != content_.end(); ++it) | |
253 { | |
254 assert(it->second != NULL); | |
255 delete it->second; | |
256 } | |
257 | |
258 content_.clear(); | |
259 } | |
260 | |
261 | |
262 static void ExtractTags(DicomMap& result, | |
263 const DicomMap::Content& source, | |
264 const MainDicomTag* tags, | |
265 size_t count) | |
266 { | |
267 result.Clear(); | |
268 | |
269 for (unsigned int i = 0; i < count; i++) | |
270 { | |
271 DicomMap::Content::const_iterator it = source.find(tags[i].tag_); | |
272 if (it != source.end()) | |
273 { | |
274 result.SetValue(it->first, *it->second /* value will be cloned */); | |
275 } | |
276 } | |
277 } | |
278 | |
279 | |
280 void DicomMap::ExtractPatientInformation(DicomMap& result) const | |
281 { | |
282 ExtractTags(result, content_, PATIENT_MAIN_DICOM_TAGS, sizeof(PATIENT_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
283 } | |
284 | |
285 void DicomMap::ExtractStudyInformation(DicomMap& result) const | |
286 { | |
287 ExtractTags(result, content_, STUDY_MAIN_DICOM_TAGS, sizeof(STUDY_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
288 } | |
289 | |
290 void DicomMap::ExtractSeriesInformation(DicomMap& result) const | |
291 { | |
292 ExtractTags(result, content_, SERIES_MAIN_DICOM_TAGS, sizeof(SERIES_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
293 } | |
294 | |
295 void DicomMap::ExtractInstanceInformation(DicomMap& result) const | |
296 { | |
297 ExtractTags(result, content_, INSTANCE_MAIN_DICOM_TAGS, sizeof(INSTANCE_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
298 } | |
299 | |
300 | |
301 | |
302 DicomMap* DicomMap::Clone() const | |
303 { | |
304 std::unique_ptr<DicomMap> result(new DicomMap); | |
305 | |
306 for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) | |
307 { | |
308 result->content_.insert(std::make_pair(it->first, it->second->Clone())); | |
309 } | |
310 | |
311 return result.release(); | |
312 } | |
313 | |
314 | |
315 void DicomMap::Assign(const DicomMap& other) | |
316 { | |
317 Clear(); | |
318 | |
319 for (Content::const_iterator it = other.content_.begin(); it != other.content_.end(); ++it) | |
320 { | |
321 content_.insert(std::make_pair(it->first, it->second->Clone())); | |
322 } | |
323 } | |
324 | |
325 | |
326 const DicomValue& DicomMap::GetValue(const DicomTag& tag) const | |
327 { | |
328 const DicomValue* value = TestAndGetValue(tag); | |
329 | |
330 if (value) | |
331 { | |
332 return *value; | |
333 } | |
334 else | |
335 { | |
336 throw OrthancException(ErrorCode_InexistentTag); | |
337 } | |
338 } | |
339 | |
340 | |
341 const DicomValue* DicomMap::TestAndGetValue(const DicomTag& tag) const | |
342 { | |
343 Content::const_iterator it = content_.find(tag); | |
344 | |
345 if (it == content_.end()) | |
346 { | |
347 return NULL; | |
348 } | |
349 else | |
350 { | |
351 return it->second; | |
352 } | |
353 } | |
354 | |
355 | |
356 void DicomMap::Remove(const DicomTag& tag) | |
357 { | |
358 Content::iterator it = content_.find(tag); | |
359 if (it != content_.end()) | |
360 { | |
361 delete it->second; | |
362 content_.erase(it); | |
363 } | |
364 } | |
365 | |
366 | |
367 static void SetupFindTemplate(DicomMap& result, | |
368 const MainDicomTag* tags, | |
369 size_t count) | |
370 { | |
371 result.Clear(); | |
372 | |
373 for (size_t i = 0; i < count; i++) | |
374 { | |
375 result.SetValue(tags[i].tag_, "", false); | |
376 } | |
377 } | |
378 | |
379 void DicomMap::SetupFindPatientTemplate(DicomMap& result) | |
380 { | |
381 SetupFindTemplate(result, PATIENT_MAIN_DICOM_TAGS, sizeof(PATIENT_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
382 } | |
383 | |
384 void DicomMap::SetupFindStudyTemplate(DicomMap& result) | |
385 { | |
386 SetupFindTemplate(result, STUDY_MAIN_DICOM_TAGS, sizeof(STUDY_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
387 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false); | |
388 result.SetValue(DICOM_TAG_PATIENT_ID, "", false); | |
389 | |
390 // These main DICOM tags are only indirectly related to the | |
391 // General Study Module, remove them | |
392 result.Remove(DICOM_TAG_INSTITUTION_NAME); | |
393 result.Remove(DICOM_TAG_REQUESTING_PHYSICIAN); | |
394 result.Remove(DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION); | |
395 } | |
396 | |
397 void DicomMap::SetupFindSeriesTemplate(DicomMap& result) | |
398 { | |
399 SetupFindTemplate(result, SERIES_MAIN_DICOM_TAGS, sizeof(SERIES_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
400 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false); | |
401 result.SetValue(DICOM_TAG_PATIENT_ID, "", false); | |
402 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false); | |
403 | |
404 // These tags are considered as "main" by Orthanc, but are not in the Series module | |
405 result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer | |
406 result.Remove(DicomTag(0x0008, 0x1010)); // Station name | |
407 result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name | |
408 result.Remove(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES); | |
409 result.Remove(DICOM_TAG_IMAGES_IN_ACQUISITION); | |
410 result.Remove(DICOM_TAG_NUMBER_OF_SLICES); | |
411 result.Remove(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS); | |
412 result.Remove(DICOM_TAG_NUMBER_OF_TIME_SLICES); | |
413 result.Remove(DICOM_TAG_IMAGE_ORIENTATION_PATIENT); | |
414 result.Remove(DICOM_TAG_SERIES_TYPE); | |
415 result.Remove(DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION); | |
416 result.Remove(DICOM_TAG_CONTRAST_BOLUS_AGENT); | |
417 } | |
418 | |
419 void DicomMap::SetupFindInstanceTemplate(DicomMap& result) | |
420 { | |
421 SetupFindTemplate(result, INSTANCE_MAIN_DICOM_TAGS, sizeof(INSTANCE_MAIN_DICOM_TAGS) / sizeof(MainDicomTag)); | |
422 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false); | |
423 result.SetValue(DICOM_TAG_PATIENT_ID, "", false); | |
424 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false); | |
425 result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false); | |
426 } | |
427 | |
428 | |
429 void DicomMap::CopyTagIfExists(const DicomMap& source, | |
430 const DicomTag& tag) | |
431 { | |
432 if (source.HasTag(tag)) | |
433 { | |
434 SetValue(tag, source.GetValue(tag)); | |
435 } | |
436 } | |
437 | |
438 | |
439 bool DicomMap::IsMainDicomTag(const DicomTag& tag, ResourceType level) | |
440 { | |
441 const MainDicomTag *tags = NULL; | |
442 size_t size; | |
443 LoadMainDicomTags(tags, size, level); | |
444 | |
445 for (size_t i = 0; i < size; i++) | |
446 { | |
447 if (tags[i].tag_ == tag) | |
448 { | |
449 return true; | |
450 } | |
451 } | |
452 | |
453 return false; | |
454 } | |
455 | |
456 bool DicomMap::IsMainDicomTag(const DicomTag& tag) | |
457 { | |
458 return (IsMainDicomTag(tag, ResourceType_Patient) || | |
459 IsMainDicomTag(tag, ResourceType_Study) || | |
460 IsMainDicomTag(tag, ResourceType_Series) || | |
461 IsMainDicomTag(tag, ResourceType_Instance)); | |
462 } | |
463 | |
464 | |
465 void DicomMap::GetMainDicomTagsInternal(std::set<DicomTag>& result, ResourceType level) | |
466 { | |
467 const MainDicomTag *tags = NULL; | |
468 size_t size; | |
469 LoadMainDicomTags(tags, size, level); | |
470 | |
471 for (size_t i = 0; i < size; i++) | |
472 { | |
473 result.insert(tags[i].tag_); | |
474 } | |
475 } | |
476 | |
477 | |
478 void DicomMap::GetMainDicomTags(std::set<DicomTag>& result, ResourceType level) | |
479 { | |
480 result.clear(); | |
481 GetMainDicomTagsInternal(result, level); | |
482 } | |
483 | |
484 | |
485 void DicomMap::GetMainDicomTags(std::set<DicomTag>& result) | |
486 { | |
487 result.clear(); | |
488 GetMainDicomTagsInternal(result, ResourceType_Patient); | |
489 GetMainDicomTagsInternal(result, ResourceType_Study); | |
490 GetMainDicomTagsInternal(result, ResourceType_Series); | |
491 GetMainDicomTagsInternal(result, ResourceType_Instance); | |
492 } | |
493 | |
494 | |
495 void DicomMap::GetTags(std::set<DicomTag>& tags) const | |
496 { | |
497 tags.clear(); | |
498 | |
499 for (Content::const_iterator it = content_.begin(); | |
500 it != content_.end(); ++it) | |
501 { | |
502 tags.insert(it->first); | |
503 } | |
504 } | |
505 | |
506 | |
507 static uint16_t ReadUnsignedInteger16(const char* dicom) | |
508 { | |
509 return le16toh(*reinterpret_cast<const uint16_t*>(dicom)); | |
510 } | |
511 | |
512 | |
513 static uint32_t ReadUnsignedInteger32(const char* dicom) | |
514 { | |
515 return le32toh(*reinterpret_cast<const uint32_t*>(dicom)); | |
516 } | |
517 | |
518 | |
519 static bool ValidateTag(const ValueRepresentation& vr, | |
520 const std::string& value) | |
521 { | |
522 switch (vr) | |
523 { | |
524 case ValueRepresentation_ApplicationEntity: | |
525 return value.size() <= 16; | |
526 | |
527 case ValueRepresentation_AgeString: | |
528 return (value.size() == 4 && | |
529 isdigit(value[0]) && | |
530 isdigit(value[1]) && | |
531 isdigit(value[2]) && | |
532 (value[3] == 'D' || value[3] == 'W' || value[3] == 'M' || value[3] == 'Y')); | |
533 | |
534 case ValueRepresentation_AttributeTag: | |
535 return value.size() == 4; | |
536 | |
537 case ValueRepresentation_CodeString: | |
538 return value.size() <= 16; | |
539 | |
540 case ValueRepresentation_Date: | |
541 return value.size() <= 18; | |
542 | |
543 case ValueRepresentation_DecimalString: | |
544 return value.size() <= 16; | |
545 | |
546 case ValueRepresentation_DateTime: | |
547 return value.size() <= 54; | |
548 | |
549 case ValueRepresentation_FloatingPointSingle: | |
550 return value.size() == 4; | |
551 | |
552 case ValueRepresentation_FloatingPointDouble: | |
553 return value.size() == 8; | |
554 | |
555 case ValueRepresentation_IntegerString: | |
556 return value.size() <= 12; | |
557 | |
558 case ValueRepresentation_LongString: | |
559 return value.size() <= 64; | |
560 | |
561 case ValueRepresentation_LongText: | |
562 return value.size() <= 10240; | |
563 | |
564 case ValueRepresentation_OtherByte: | |
565 return true; | |
566 | |
567 case ValueRepresentation_OtherDouble: | |
568 return value.size() <= (static_cast<uint64_t>(1) << 32) - 8; | |
569 | |
570 case ValueRepresentation_OtherFloat: | |
571 return value.size() <= (static_cast<uint64_t>(1) << 32) - 4; | |
572 | |
573 case ValueRepresentation_OtherLong: | |
574 return true; | |
575 | |
576 case ValueRepresentation_OtherWord: | |
577 return true; | |
578 | |
579 case ValueRepresentation_PersonName: | |
580 return true; | |
581 | |
582 case ValueRepresentation_ShortString: | |
583 return value.size() <= 16; | |
584 | |
585 case ValueRepresentation_SignedLong: | |
586 return value.size() == 4; | |
587 | |
588 case ValueRepresentation_Sequence: | |
589 return true; | |
590 | |
591 case ValueRepresentation_SignedShort: | |
592 return value.size() == 2; | |
593 | |
594 case ValueRepresentation_ShortText: | |
595 return value.size() <= 1024; | |
596 | |
597 case ValueRepresentation_Time: | |
598 return value.size() <= 28; | |
599 | |
600 case ValueRepresentation_UnlimitedCharacters: | |
601 return value.size() <= (static_cast<uint64_t>(1) << 32) - 2; | |
602 | |
603 case ValueRepresentation_UniqueIdentifier: | |
604 return value.size() <= 64; | |
605 | |
606 case ValueRepresentation_UnsignedLong: | |
607 return value.size() == 4; | |
608 | |
609 case ValueRepresentation_Unknown: | |
610 return true; | |
611 | |
612 case ValueRepresentation_UniversalResource: | |
613 return value.size() <= (static_cast<uint64_t>(1) << 32) - 2; | |
614 | |
615 case ValueRepresentation_UnsignedShort: | |
616 return value.size() == 2; | |
617 | |
618 case ValueRepresentation_UnlimitedText: | |
619 return value.size() <= (static_cast<uint64_t>(1) << 32) - 2; | |
620 | |
621 default: | |
622 // Assume unsupported tags are OK | |
623 return true; | |
624 } | |
625 } | |
626 | |
627 | |
628 static void RemoveTagPadding(std::string& value, | |
629 const ValueRepresentation& vr) | |
630 { | |
631 /** | |
632 * Remove padding from character strings, if need be. For the time | |
633 * being, only the UI VR is supported. | |
634 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html | |
635 **/ | |
636 | |
637 switch (vr) | |
638 { | |
639 case ValueRepresentation_UniqueIdentifier: | |
640 { | |
641 /** | |
642 * "Values with a VR of UI shall be padded with a single | |
643 * trailing NULL (00H) character when necessary to achieve even | |
644 * length." | |
645 **/ | |
646 | |
647 if (!value.empty() && | |
648 value[value.size() - 1] == '\0') | |
649 { | |
650 value.resize(value.size() - 1); | |
651 } | |
652 | |
653 break; | |
654 } | |
655 | |
656 /** | |
657 * TODO implement other VR | |
658 **/ | |
659 | |
660 default: | |
661 // No padding is applicable to this VR | |
662 break; | |
663 } | |
664 } | |
665 | |
666 | |
667 static bool ReadNextTag(DicomTag& tag, | |
668 ValueRepresentation& vr, | |
669 std::string& value, | |
670 const char* dicom, | |
671 size_t size, | |
672 size_t& position) | |
673 { | |
674 /** | |
675 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2 | |
676 * This function reads a data element with Explicit VR encoded using Little-Endian. | |
677 **/ | |
678 | |
679 if (position + 6 > size) | |
680 { | |
681 return false; | |
682 } | |
683 | |
684 tag = DicomTag(ReadUnsignedInteger16(dicom + position), | |
685 ReadUnsignedInteger16(dicom + position + 2)); | |
686 | |
687 vr = StringToValueRepresentation(std::string(dicom + position + 4, 2), true); | |
688 if (vr == ValueRepresentation_NotSupported) | |
689 { | |
690 return false; | |
691 } | |
692 | |
693 if (vr == ValueRepresentation_OtherByte || | |
694 vr == ValueRepresentation_OtherDouble || | |
695 vr == ValueRepresentation_OtherFloat || | |
696 vr == ValueRepresentation_OtherLong || | |
697 vr == ValueRepresentation_OtherWord || | |
698 vr == ValueRepresentation_Sequence || | |
699 vr == ValueRepresentation_UnlimitedCharacters || | |
700 vr == ValueRepresentation_UniversalResource || | |
701 vr == ValueRepresentation_UnlimitedText || | |
702 vr == ValueRepresentation_Unknown) // Note that "UN" should never appear in the Meta Information | |
703 { | |
704 if (position + 12 > size) | |
705 { | |
706 return false; | |
707 } | |
708 | |
709 uint32_t length = ReadUnsignedInteger32(dicom + position + 8); | |
710 | |
711 if (position + 12 + length > size) | |
712 { | |
713 return false; | |
714 } | |
715 | |
716 value.assign(dicom + position + 12, length); | |
717 position += (12 + length); | |
718 } | |
719 else | |
720 { | |
721 if (position + 8 > size) | |
722 { | |
723 return false; | |
724 } | |
725 | |
726 uint16_t length = ReadUnsignedInteger16(dicom + position + 6); | |
727 | |
728 if (position + 8 + length > size) | |
729 { | |
730 return false; | |
731 } | |
732 | |
733 value.assign(dicom + position + 8, length); | |
734 position += (8 + length); | |
735 } | |
736 | |
737 if (!ValidateTag(vr, value)) | |
738 { | |
739 return false; | |
740 } | |
741 | |
742 RemoveTagPadding(value, vr); | |
743 | |
744 return true; | |
745 } | |
746 | |
747 | |
748 bool DicomMap::IsDicomFile(const void* dicom, | |
749 size_t size) | |
750 { | |
751 /** | |
752 * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html | |
753 * According to Table 7.1-1, besides the "DICM" DICOM prefix, the | |
754 * file preamble (i.e. dicom[0..127]) should not be taken into | |
755 * account to determine whether the file is or is not a DICOM file. | |
756 **/ | |
757 | |
758 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom); | |
759 | |
760 return (size >= 132 && | |
761 p[128] == 'D' && | |
762 p[129] == 'I' && | |
763 p[130] == 'C' && | |
764 p[131] == 'M'); | |
765 } | |
766 | |
767 | |
768 bool DicomMap::ParseDicomMetaInformation(DicomMap& result, | |
769 const void* dicom, | |
770 size_t size) | |
771 { | |
772 if (!IsDicomFile(dicom, size)) | |
773 { | |
774 return false; | |
775 } | |
776 | |
777 | |
778 /** | |
779 * The DICOM File Meta Information must be encoded using the | |
780 * Explicit VR Little Endian Transfer Syntax | |
781 * (UID=1.2.840.10008.1.2.1). | |
782 **/ | |
783 | |
784 result.Clear(); | |
785 | |
786 // First, we read the "File Meta Information Group Length" tag | |
787 // (0002,0000) to know where to stop reading the meta header | |
788 size_t position = 132; | |
789 | |
790 DicomTag tag(0x0000, 0x0000); // Dummy initialization | |
791 ValueRepresentation vr; | |
792 std::string value; | |
793 if (!ReadNextTag(tag, vr, value, reinterpret_cast<const char*>(dicom), size, position) || | |
794 tag.GetGroup() != 0x0002 || | |
795 tag.GetElement() != 0x0000 || | |
796 vr != ValueRepresentation_UnsignedLong || | |
797 value.size() != 4) | |
798 { | |
799 return false; | |
800 } | |
801 | |
802 size_t stopPosition = position + ReadUnsignedInteger32(value.c_str()); | |
803 if (stopPosition > size) | |
804 { | |
805 return false; | |
806 } | |
807 | |
808 while (position < stopPosition) | |
809 { | |
810 if (ReadNextTag(tag, vr, value, reinterpret_cast<const char*>(dicom), size, position)) | |
811 { | |
812 result.SetValue(tag, value, IsBinaryValueRepresentation(vr)); | |
813 } | |
814 else | |
815 { | |
816 return false; | |
817 } | |
818 } | |
819 | |
820 return true; | |
821 } | |
822 | |
823 | |
824 static std::string ValueAsString(const DicomMap& summary, | |
825 const DicomTag& tag) | |
826 { | |
827 const DicomValue& value = summary.GetValue(tag); | |
828 if (value.IsNull()) | |
829 { | |
830 return "(null)"; | |
831 } | |
832 else | |
833 { | |
834 return value.GetContent(); | |
835 } | |
836 } | |
837 | |
838 | |
839 void DicomMap::LogMissingTagsForStore() const | |
840 { | |
841 std::string s, t; | |
842 | |
843 if (HasTag(DICOM_TAG_PATIENT_ID)) | |
844 { | |
845 if (t.size() > 0) | |
846 t += ", "; | |
847 t += "PatientID=" + ValueAsString(*this, DICOM_TAG_PATIENT_ID); | |
848 } | |
849 else | |
850 { | |
851 if (s.size() > 0) | |
852 s += ", "; | |
853 s += "PatientID"; | |
854 } | |
855 | |
856 if (HasTag(DICOM_TAG_STUDY_INSTANCE_UID)) | |
857 { | |
858 if (t.size() > 0) | |
859 t += ", "; | |
860 t += "StudyInstanceUID=" + ValueAsString(*this, DICOM_TAG_STUDY_INSTANCE_UID); | |
861 } | |
862 else | |
863 { | |
864 if (s.size() > 0) | |
865 s += ", "; | |
866 s += "StudyInstanceUID"; | |
867 } | |
868 | |
869 if (HasTag(DICOM_TAG_SERIES_INSTANCE_UID)) | |
870 { | |
871 if (t.size() > 0) | |
872 t += ", "; | |
873 t += "SeriesInstanceUID=" + ValueAsString(*this, DICOM_TAG_SERIES_INSTANCE_UID); | |
874 } | |
875 else | |
876 { | |
877 if (s.size() > 0) | |
878 s += ", "; | |
879 s += "SeriesInstanceUID"; | |
880 } | |
881 | |
882 if (HasTag(DICOM_TAG_SOP_INSTANCE_UID)) | |
883 { | |
884 if (t.size() > 0) | |
885 t += ", "; | |
886 t += "SOPInstanceUID=" + ValueAsString(*this, DICOM_TAG_SOP_INSTANCE_UID); | |
887 } | |
888 else | |
889 { | |
890 if (s.size() > 0) | |
891 s += ", "; | |
892 s += "SOPInstanceUID"; | |
893 } | |
894 | |
895 if (t.size() == 0) | |
896 { | |
897 LOG(ERROR) << "Store has failed because all the required tags (" << s << ") are missing (is it a DICOMDIR file?)"; | |
898 } | |
899 else | |
900 { | |
901 LOG(ERROR) << "Store has failed because required tags (" << s << ") are missing for the following instance: " << t; | |
902 } | |
903 } | |
904 | |
905 | |
906 bool DicomMap::LookupStringValue(std::string& result, | |
907 const DicomTag& tag, | |
908 bool allowBinary) const | |
909 { | |
910 const DicomValue* value = TestAndGetValue(tag); | |
911 | |
912 if (value == NULL) | |
913 { | |
914 return false; | |
915 } | |
916 else | |
917 { | |
918 return value->CopyToString(result, allowBinary); | |
919 } | |
920 } | |
921 | |
922 bool DicomMap::ParseInteger32(int32_t& result, | |
923 const DicomTag& tag) const | |
924 { | |
925 const DicomValue* value = TestAndGetValue(tag); | |
926 | |
927 if (value == NULL) | |
928 { | |
929 return false; | |
930 } | |
931 else | |
932 { | |
933 return value->ParseInteger32(result); | |
934 } | |
935 } | |
936 | |
937 bool DicomMap::ParseInteger64(int64_t& result, | |
938 const DicomTag& tag) const | |
939 { | |
940 const DicomValue* value = TestAndGetValue(tag); | |
941 | |
942 if (value == NULL) | |
943 { | |
944 return false; | |
945 } | |
946 else | |
947 { | |
948 return value->ParseInteger64(result); | |
949 } | |
950 } | |
951 | |
952 bool DicomMap::ParseUnsignedInteger32(uint32_t& result, | |
953 const DicomTag& tag) const | |
954 { | |
955 const DicomValue* value = TestAndGetValue(tag); | |
956 | |
957 if (value == NULL) | |
958 { | |
959 return false; | |
960 } | |
961 else | |
962 { | |
963 return value->ParseUnsignedInteger32(result); | |
964 } | |
965 } | |
966 | |
967 bool DicomMap::ParseUnsignedInteger64(uint64_t& result, | |
968 const DicomTag& tag) const | |
969 { | |
970 const DicomValue* value = TestAndGetValue(tag); | |
971 | |
972 if (value == NULL) | |
973 { | |
974 return false; | |
975 } | |
976 else | |
977 { | |
978 return value->ParseUnsignedInteger64(result); | |
979 } | |
980 } | |
981 | |
982 bool DicomMap::ParseFloat(float& result, | |
983 const DicomTag& tag) const | |
984 { | |
985 const DicomValue* value = TestAndGetValue(tag); | |
986 | |
987 if (value == NULL) | |
988 { | |
989 return false; | |
990 } | |
991 else | |
992 { | |
993 return value->ParseFloat(result); | |
994 } | |
995 } | |
996 | |
997 bool DicomMap::ParseFirstFloat(float& result, | |
998 const DicomTag& tag) const | |
999 { | |
1000 const DicomValue* value = TestAndGetValue(tag); | |
1001 | |
1002 if (value == NULL) | |
1003 { | |
1004 return false; | |
1005 } | |
1006 else | |
1007 { | |
1008 return value->ParseFirstFloat(result); | |
1009 } | |
1010 } | |
1011 | |
1012 bool DicomMap::ParseDouble(double& result, | |
1013 const DicomTag& tag) const | |
1014 { | |
1015 const DicomValue* value = TestAndGetValue(tag); | |
1016 | |
1017 if (value == NULL) | |
1018 { | |
1019 return false; | |
1020 } | |
1021 else | |
1022 { | |
1023 return value->ParseDouble(result); | |
1024 } | |
1025 } | |
1026 | |
1027 | |
1028 void DicomMap::FromDicomAsJson(const Json::Value& dicomAsJson) | |
1029 { | |
1030 if (dicomAsJson.type() != Json::objectValue) | |
1031 { | |
1032 throw OrthancException(ErrorCode_BadFileFormat); | |
1033 } | |
1034 | |
1035 Clear(); | |
1036 | |
1037 Json::Value::Members tags = dicomAsJson.getMemberNames(); | |
1038 for (Json::Value::Members::const_iterator | |
1039 it = tags.begin(); it != tags.end(); ++it) | |
1040 { | |
1041 DicomTag tag(0, 0); | |
1042 if (!DicomTag::ParseHexadecimal(tag, it->c_str())) | |
1043 { | |
1044 throw OrthancException(ErrorCode_CorruptedFile); | |
1045 } | |
1046 | |
1047 const Json::Value& value = dicomAsJson[*it]; | |
1048 | |
1049 if (value.type() != Json::objectValue || | |
1050 !value.isMember("Type") || | |
1051 !value.isMember("Value") || | |
1052 value["Type"].type() != Json::stringValue) | |
1053 { | |
1054 throw OrthancException(ErrorCode_CorruptedFile); | |
1055 } | |
1056 | |
1057 if (value["Type"] == "String") | |
1058 { | |
1059 if (value["Value"].type() != Json::stringValue) | |
1060 { | |
1061 throw OrthancException(ErrorCode_CorruptedFile); | |
1062 } | |
1063 else | |
1064 { | |
1065 SetValue(tag, value["Value"].asString(), false /* not binary */); | |
1066 } | |
1067 } | |
1068 } | |
1069 } | |
1070 | |
1071 | |
1072 void DicomMap::Merge(const DicomMap& other) | |
1073 { | |
1074 for (Content::const_iterator it = other.content_.begin(); | |
1075 it != other.content_.end(); ++it) | |
1076 { | |
1077 assert(it->second != NULL); | |
1078 | |
1079 if (content_.find(it->first) == content_.end()) | |
1080 { | |
1081 content_[it->first] = it->second->Clone(); | |
1082 } | |
1083 } | |
1084 } | |
1085 | |
1086 | |
1087 void DicomMap::MergeMainDicomTags(const DicomMap& other, | |
1088 ResourceType level) | |
1089 { | |
1090 const MainDicomTag* tags = NULL; | |
1091 size_t size = 0; | |
1092 | |
1093 LoadMainDicomTags(tags, size, level); | |
1094 assert(tags != NULL && size > 0); | |
1095 | |
1096 for (size_t i = 0; i < size; i++) | |
1097 { | |
1098 Content::const_iterator found = other.content_.find(tags[i].tag_); | |
1099 | |
1100 if (found != other.content_.end() && | |
1101 content_.find(tags[i].tag_) == content_.end()) | |
1102 { | |
1103 assert(found->second != NULL); | |
1104 content_[tags[i].tag_] = found->second->Clone(); | |
1105 } | |
1106 } | |
1107 } | |
1108 | |
1109 | |
1110 void DicomMap::ExtractMainDicomTags(const DicomMap& other) | |
1111 { | |
1112 Clear(); | |
1113 MergeMainDicomTags(other, ResourceType_Patient); | |
1114 MergeMainDicomTags(other, ResourceType_Study); | |
1115 MergeMainDicomTags(other, ResourceType_Series); | |
1116 MergeMainDicomTags(other, ResourceType_Instance); | |
1117 } | |
1118 | |
1119 | |
1120 bool DicomMap::HasOnlyMainDicomTags() const | |
1121 { | |
1122 // TODO - Speed up possible by making this std::set a global variable | |
1123 | |
1124 std::set<DicomTag> mainDicomTags; | |
1125 GetMainDicomTags(mainDicomTags); | |
1126 | |
1127 for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) | |
1128 { | |
1129 if (mainDicomTags.find(it->first) == mainDicomTags.end()) | |
1130 { | |
1131 return false; | |
1132 } | |
1133 } | |
1134 | |
1135 return true; | |
1136 } | |
1137 | |
1138 | |
1139 void DicomMap::Serialize(Json::Value& target) const | |
1140 { | |
1141 target = Json::objectValue; | |
1142 | |
1143 for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) | |
1144 { | |
1145 assert(it->second != NULL); | |
1146 | |
1147 std::string tag = it->first.Format(); | |
1148 | |
1149 Json::Value value; | |
1150 it->second->Serialize(value); | |
1151 | |
1152 target[tag] = value; | |
1153 } | |
1154 } | |
1155 | |
1156 | |
1157 void DicomMap::Unserialize(const Json::Value& source) | |
1158 { | |
1159 Clear(); | |
1160 | |
1161 if (source.type() != Json::objectValue) | |
1162 { | |
1163 throw OrthancException(ErrorCode_BadFileFormat); | |
1164 } | |
1165 | |
1166 Json::Value::Members tags = source.getMemberNames(); | |
1167 | |
1168 for (size_t i = 0; i < tags.size(); i++) | |
1169 { | |
1170 DicomTag tag(0, 0); | |
1171 | |
1172 if (!DicomTag::ParseHexadecimal(tag, tags[i].c_str()) || | |
1173 content_.find(tag) != content_.end()) | |
1174 { | |
1175 throw OrthancException(ErrorCode_BadFileFormat); | |
1176 } | |
1177 | |
1178 std::unique_ptr<DicomValue> value(new DicomValue); | |
1179 value->Unserialize(source[tags[i]]); | |
1180 | |
1181 content_[tag] = value.release(); | |
1182 } | |
1183 } | |
1184 | |
1185 | |
1186 void DicomMap::FromDicomWeb(const Json::Value& source) | |
1187 { | |
1188 static const char* const ALPHABETIC = "Alphabetic"; | |
1189 static const char* const IDEOGRAPHIC = "Ideographic"; | |
1190 static const char* const INLINE_BINARY = "InlineBinary"; | |
1191 static const char* const PHONETIC = "Phonetic"; | |
1192 static const char* const VALUE = "Value"; | |
1193 static const char* const VR = "vr"; | |
1194 | |
1195 Clear(); | |
1196 | |
1197 if (source.type() != Json::objectValue) | |
1198 { | |
1199 throw OrthancException(ErrorCode_BadFileFormat); | |
1200 } | |
1201 | |
1202 Json::Value::Members tags = source.getMemberNames(); | |
1203 | |
1204 for (size_t i = 0; i < tags.size(); i++) | |
1205 { | |
1206 const Json::Value& item = source[tags[i]]; | |
1207 DicomTag tag(0, 0); | |
1208 | |
1209 if (item.type() != Json::objectValue || | |
1210 !item.isMember(VR) || | |
1211 item[VR].type() != Json::stringValue || | |
1212 !DicomTag::ParseHexadecimal(tag, tags[i].c_str())) | |
1213 { | |
1214 throw OrthancException(ErrorCode_BadFileFormat); | |
1215 } | |
1216 | |
1217 ValueRepresentation vr = StringToValueRepresentation(item[VR].asString(), false); | |
1218 | |
1219 if (item.isMember(INLINE_BINARY)) | |
1220 { | |
1221 const Json::Value& value = item[INLINE_BINARY]; | |
1222 | |
1223 if (value.type() == Json::stringValue) | |
1224 { | |
1225 std::string decoded; | |
1226 Toolbox::DecodeBase64(decoded, value.asString()); | |
1227 SetValue(tag, decoded, true /* binary data */); | |
1228 } | |
1229 } | |
1230 else if (!item.isMember(VALUE)) | |
1231 { | |
1232 // Tag is present, but it has a null value | |
1233 SetValue(tag, "", false /* not binary */); | |
1234 } | |
1235 else | |
1236 { | |
1237 const Json::Value& value = item[VALUE]; | |
1238 | |
1239 if (value.type() == Json::arrayValue) | |
1240 { | |
1241 bool supported = true; | |
1242 | |
1243 std::string s; | |
1244 for (Json::Value::ArrayIndex i = 0; i < value.size() && supported; i++) | |
1245 { | |
1246 if (!s.empty()) | |
1247 { | |
1248 s += '\\'; | |
1249 } | |
1250 | |
1251 switch (value[i].type()) | |
1252 { | |
1253 case Json::objectValue: | |
1254 if (vr == ValueRepresentation_PersonName && | |
1255 value[i].type() == Json::objectValue) | |
1256 { | |
1257 if (value[i].isMember(ALPHABETIC) && | |
1258 value[i][ALPHABETIC].type() == Json::stringValue) | |
1259 { | |
1260 s += value[i][ALPHABETIC].asString(); | |
1261 } | |
1262 | |
1263 bool hasIdeographic = false; | |
1264 | |
1265 if (value[i].isMember(IDEOGRAPHIC) && | |
1266 value[i][IDEOGRAPHIC].type() == Json::stringValue) | |
1267 { | |
1268 s += '=' + value[i][IDEOGRAPHIC].asString(); | |
1269 hasIdeographic = true; | |
1270 } | |
1271 | |
1272 if (value[i].isMember(PHONETIC) && | |
1273 value[i][PHONETIC].type() == Json::stringValue) | |
1274 { | |
1275 if (!hasIdeographic) | |
1276 { | |
1277 s += '='; | |
1278 } | |
1279 | |
1280 s += '=' + value[i][PHONETIC].asString(); | |
1281 } | |
1282 } | |
1283 else | |
1284 { | |
1285 // This is the case of sequences | |
1286 supported = false; | |
1287 } | |
1288 | |
1289 break; | |
1290 | |
1291 case Json::stringValue: | |
1292 s += value[i].asString(); | |
1293 break; | |
1294 | |
1295 case Json::intValue: | |
1296 s += boost::lexical_cast<std::string>(value[i].asInt()); | |
1297 break; | |
1298 | |
1299 case Json::uintValue: | |
1300 s += boost::lexical_cast<std::string>(value[i].asUInt()); | |
1301 break; | |
1302 | |
1303 case Json::realValue: | |
1304 s += boost::lexical_cast<std::string>(value[i].asDouble()); | |
1305 break; | |
1306 | |
1307 default: | |
1308 break; | |
1309 } | |
1310 } | |
1311 | |
1312 if (supported) | |
1313 { | |
1314 SetValue(tag, s, false /* not binary */); | |
1315 } | |
1316 } | |
1317 } | |
1318 } | |
1319 } | |
1320 | |
1321 | |
1322 std::string DicomMap::GetStringValue(const DicomTag& tag, | |
1323 const std::string& defaultValue, | |
1324 bool allowBinary) const | |
1325 { | |
1326 std::string s; | |
1327 if (LookupStringValue(s, tag, allowBinary)) | |
1328 { | |
1329 return s; | |
1330 } | |
1331 else | |
1332 { | |
1333 return defaultValue; | |
1334 } | |
1335 } | |
1336 | |
1337 | |
1338 void DicomMap::RemoveBinaryTags() | |
1339 { | |
1340 Content kept; | |
1341 | |
1342 for (Content::iterator it = content_.begin(); it != content_.end(); ++it) | |
1343 { | |
1344 assert(it->second != NULL); | |
1345 | |
1346 if (!it->second->IsBinary() && | |
1347 !it->second->IsNull()) | |
1348 { | |
1349 kept[it->first] = it->second; | |
1350 } | |
1351 else | |
1352 { | |
1353 delete it->second; | |
1354 } | |
1355 } | |
1356 | |
1357 content_ = kept; | |
1358 } | |
1359 | |
1360 | |
1361 void DicomMap::DumpMainDicomTags(Json::Value& target, | |
1362 ResourceType level) const | |
1363 { | |
1364 std::map<DicomTag, std::string> mainTags; // TODO - Create a singleton to hold this map | |
1365 LoadMainDicomTags(mainTags, level); | |
1366 | |
1367 target = Json::objectValue; | |
1368 | |
1369 for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) | |
1370 { | |
1371 assert(it->second != NULL); | |
1372 | |
1373 if (!it->second->IsBinary() && | |
1374 !it->second->IsNull()) | |
1375 { | |
1376 std::map<DicomTag, std::string>::const_iterator found = mainTags.find(it->first); | |
1377 | |
1378 if (found != mainTags.end()) | |
1379 { | |
1380 target[found->second] = it->second->GetContent(); | |
1381 } | |
1382 } | |
1383 } | |
1384 } | |
1385 | |
1386 | |
1387 void DicomMap::ParseMainDicomTags(const Json::Value& source, | |
1388 ResourceType level) | |
1389 { | |
1390 if (source.type() != Json::objectValue) | |
1391 { | |
1392 throw OrthancException(ErrorCode_BadFileFormat); | |
1393 } | |
1394 | |
1395 std::map<std::string, DicomTag2> mainTags; // TODO - Create a singleton to hold this map | |
1396 LoadMainDicomTags(mainTags, level); | |
1397 | |
1398 Json::Value::Members members = source.getMemberNames(); | |
1399 for (size_t i = 0; i < members.size(); i++) | |
1400 { | |
1401 std::map<std::string, DicomTag2>::const_iterator found = mainTags.find(members[i]); | |
1402 | |
1403 if (found != mainTags.end()) | |
1404 { | |
1405 const Json::Value& value = source[members[i]]; | |
1406 if (value.type() != Json::stringValue) | |
1407 { | |
1408 throw OrthancException(ErrorCode_BadFileFormat); | |
1409 } | |
1410 else | |
1411 { | |
1412 SetValue(found->second, value.asString(), false); | |
1413 } | |
1414 } | |
1415 } | |
1416 } | |
1417 | |
1418 | |
1419 void DicomMap::Print(FILE* fp) const | |
1420 { | |
1421 DicomArray a(*this); | |
1422 a.Print(fp); | |
1423 } | |
1424 } |