Mercurial > hg > orthanc-wsi
annotate Resources/Orthanc/Core/DicomFormat/DicomMap.cpp @ 98:ff0ef01c332c
shared copyright with osimis
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 04 Jan 2017 16:43:25 +0100 |
parents | 7a3853d51c45 |
children | a18bfe1fdd62 |
rev | line source |
---|---|
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 | |
98
ff0ef01c332c
shared copyright with osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
59
diff
changeset
|
5 * Copyright (C) 2017 Osimis, Belgium |
1 | 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 #include "../Endianness.h" | |
40 #include "../OrthancException.h" | |
41 | |
42 | |
43 namespace Orthanc | |
44 { | |
45 static DicomTag patientTags[] = | |
46 { | |
47 //DicomTag(0x0010, 0x1010), // PatientAge | |
48 //DicomTag(0x0010, 0x1040) // PatientAddress | |
49 DicomTag(0x0010, 0x0010), // PatientName | |
50 DicomTag(0x0010, 0x0030), // PatientBirthDate | |
51 DicomTag(0x0010, 0x0040), // PatientSex | |
52 DicomTag(0x0010, 0x1000), // OtherPatientIDs | |
53 DICOM_TAG_PATIENT_ID | |
54 }; | |
55 | |
56 static DicomTag studyTags[] = | |
57 { | |
58 //DicomTag(0x0010, 0x1020), // PatientSize | |
59 //DicomTag(0x0010, 0x1030) // PatientWeight | |
60 DICOM_TAG_STUDY_DATE, | |
61 DicomTag(0x0008, 0x0030), // StudyTime | |
62 DicomTag(0x0020, 0x0010), // StudyID | |
63 DICOM_TAG_STUDY_DESCRIPTION, | |
64 DICOM_TAG_ACCESSION_NUMBER, | |
65 DICOM_TAG_STUDY_INSTANCE_UID, | |
66 DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION, // New in db v6 | |
67 DICOM_TAG_INSTITUTION_NAME, // New in db v6 | |
68 DICOM_TAG_REQUESTING_PHYSICIAN, // New in db v6 | |
69 DICOM_TAG_REFERRING_PHYSICIAN_NAME // New in db v6 | |
70 }; | |
71 | |
72 static DicomTag seriesTags[] = | |
73 { | |
74 //DicomTag(0x0010, 0x1080), // MilitaryRank | |
75 DicomTag(0x0008, 0x0021), // SeriesDate | |
76 DicomTag(0x0008, 0x0031), // SeriesTime | |
77 DICOM_TAG_MODALITY, | |
78 DicomTag(0x0008, 0x0070), // Manufacturer | |
79 DicomTag(0x0008, 0x1010), // StationName | |
80 DICOM_TAG_SERIES_DESCRIPTION, | |
81 DicomTag(0x0018, 0x0015), // BodyPartExamined | |
82 DicomTag(0x0018, 0x0024), // SequenceName | |
83 DicomTag(0x0018, 0x1030), // ProtocolName | |
84 DicomTag(0x0020, 0x0011), // SeriesNumber | |
85 DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES, | |
86 DICOM_TAG_IMAGES_IN_ACQUISITION, | |
87 DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, | |
88 DICOM_TAG_NUMBER_OF_SLICES, | |
89 DICOM_TAG_NUMBER_OF_TIME_SLICES, | |
90 DICOM_TAG_SERIES_INSTANCE_UID, | |
91 DICOM_TAG_IMAGE_ORIENTATION_PATIENT, // New in db v6 | |
92 DICOM_TAG_SERIES_TYPE, // New in db v6 | |
93 DICOM_TAG_OPERATOR_NAME, // New in db v6 | |
94 DICOM_TAG_PERFORMED_PROCEDURE_STEP_DESCRIPTION, // New in db v6 | |
95 DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION, // New in db v6 | |
96 DICOM_TAG_CONTRAST_BOLUS_AGENT // New in db v6 | |
97 }; | |
98 | |
99 static DicomTag instanceTags[] = | |
100 { | |
101 DicomTag(0x0008, 0x0012), // InstanceCreationDate | |
102 DicomTag(0x0008, 0x0013), // InstanceCreationTime | |
103 DicomTag(0x0020, 0x0012), // AcquisitionNumber | |
104 DICOM_TAG_IMAGE_INDEX, | |
105 DICOM_TAG_INSTANCE_NUMBER, | |
106 DICOM_TAG_NUMBER_OF_FRAMES, | |
107 DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER, | |
108 DICOM_TAG_SOP_INSTANCE_UID, | |
109 DICOM_TAG_IMAGE_POSITION_PATIENT, // New in db v6 | |
110 DICOM_TAG_IMAGE_COMMENTS // New in db v6 | |
111 }; | |
112 | |
113 | |
114 void DicomMap::LoadMainDicomTags(const DicomTag*& tags, | |
115 size_t& size, | |
116 ResourceType level) | |
117 { | |
118 switch (level) | |
119 { | |
120 case ResourceType_Patient: | |
121 tags = patientTags; | |
122 size = sizeof(patientTags) / sizeof(DicomTag); | |
123 break; | |
124 | |
125 case ResourceType_Study: | |
126 tags = studyTags; | |
127 size = sizeof(studyTags) / sizeof(DicomTag); | |
128 break; | |
129 | |
130 case ResourceType_Series: | |
131 tags = seriesTags; | |
132 size = sizeof(seriesTags) / sizeof(DicomTag); | |
133 break; | |
134 | |
135 case ResourceType_Instance: | |
136 tags = instanceTags; | |
137 size = sizeof(instanceTags) / sizeof(DicomTag); | |
138 break; | |
139 | |
140 default: | |
141 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
142 } | |
143 } | |
144 | |
145 | |
146 void DicomMap::SetValue(uint16_t group, | |
147 uint16_t element, | |
148 DicomValue* value) | |
149 { | |
150 DicomTag tag(group, element); | |
151 Map::iterator it = map_.find(tag); | |
152 | |
153 if (it != map_.end()) | |
154 { | |
155 delete it->second; | |
156 it->second = value; | |
157 } | |
158 else | |
159 { | |
160 map_.insert(std::make_pair(tag, value)); | |
161 } | |
162 } | |
163 | |
164 void DicomMap::SetValue(DicomTag tag, | |
165 DicomValue* value) | |
166 { | |
167 SetValue(tag.GetGroup(), tag.GetElement(), value); | |
168 } | |
169 | |
170 | |
171 | |
172 | |
173 void DicomMap::Clear() | |
174 { | |
175 for (Map::iterator it = map_.begin(); it != map_.end(); ++it) | |
176 { | |
177 delete it->second; | |
178 } | |
179 | |
180 map_.clear(); | |
181 } | |
182 | |
183 | |
184 void DicomMap::ExtractTags(DicomMap& result, | |
185 const DicomTag* tags, | |
186 size_t count) const | |
187 { | |
188 result.Clear(); | |
189 | |
190 for (unsigned int i = 0; i < count; i++) | |
191 { | |
192 Map::const_iterator it = map_.find(tags[i]); | |
193 if (it != map_.end()) | |
194 { | |
195 result.SetValue(it->first, it->second->Clone()); | |
196 } | |
197 } | |
198 } | |
199 | |
200 | |
201 void DicomMap::ExtractPatientInformation(DicomMap& result) const | |
202 { | |
203 ExtractTags(result, patientTags, sizeof(patientTags) / sizeof(DicomTag)); | |
204 } | |
205 | |
206 void DicomMap::ExtractStudyInformation(DicomMap& result) const | |
207 { | |
208 ExtractTags(result, studyTags, sizeof(studyTags) / sizeof(DicomTag)); | |
209 } | |
210 | |
211 void DicomMap::ExtractSeriesInformation(DicomMap& result) const | |
212 { | |
213 ExtractTags(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag)); | |
214 } | |
215 | |
216 void DicomMap::ExtractInstanceInformation(DicomMap& result) const | |
217 { | |
218 ExtractTags(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag)); | |
219 } | |
220 | |
221 | |
222 | |
223 DicomMap* DicomMap::Clone() const | |
224 { | |
225 std::auto_ptr<DicomMap> result(new DicomMap); | |
226 | |
227 for (Map::const_iterator it = map_.begin(); it != map_.end(); ++it) | |
228 { | |
229 result->map_.insert(std::make_pair(it->first, it->second->Clone())); | |
230 } | |
231 | |
232 return result.release(); | |
233 } | |
234 | |
235 | |
236 void DicomMap::Assign(const DicomMap& other) | |
237 { | |
238 Clear(); | |
239 | |
240 for (Map::const_iterator it = other.map_.begin(); it != other.map_.end(); ++it) | |
241 { | |
242 map_.insert(std::make_pair(it->first, it->second->Clone())); | |
243 } | |
244 } | |
245 | |
246 | |
247 const DicomValue& DicomMap::GetValue(const DicomTag& tag) const | |
248 { | |
249 const DicomValue* value = TestAndGetValue(tag); | |
250 | |
251 if (value) | |
252 { | |
253 return *value; | |
254 } | |
255 else | |
256 { | |
257 throw OrthancException(ErrorCode_InexistentTag); | |
258 } | |
259 } | |
260 | |
261 | |
262 const DicomValue* DicomMap::TestAndGetValue(const DicomTag& tag) const | |
263 { | |
264 Map::const_iterator it = map_.find(tag); | |
265 | |
266 if (it == map_.end()) | |
267 { | |
268 return NULL; | |
269 } | |
270 else | |
271 { | |
272 return it->second; | |
273 } | |
274 } | |
275 | |
276 | |
277 void DicomMap::Remove(const DicomTag& tag) | |
278 { | |
279 Map::iterator it = map_.find(tag); | |
280 if (it != map_.end()) | |
281 { | |
282 delete it->second; | |
283 map_.erase(it); | |
284 } | |
285 } | |
286 | |
287 | |
288 static void SetupFindTemplate(DicomMap& result, | |
289 const DicomTag* tags, | |
290 size_t count) | |
291 { | |
292 result.Clear(); | |
293 | |
294 for (size_t i = 0; i < count; i++) | |
295 { | |
296 result.SetValue(tags[i], "", false); | |
297 } | |
298 } | |
299 | |
300 void DicomMap::SetupFindPatientTemplate(DicomMap& result) | |
301 { | |
302 SetupFindTemplate(result, patientTags, sizeof(patientTags) / sizeof(DicomTag)); | |
303 } | |
304 | |
305 void DicomMap::SetupFindStudyTemplate(DicomMap& result) | |
306 { | |
307 SetupFindTemplate(result, studyTags, sizeof(studyTags) / sizeof(DicomTag)); | |
308 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false); | |
309 result.SetValue(DICOM_TAG_PATIENT_ID, "", false); | |
310 | |
311 // These main DICOM tags are only indirectly related to the | |
312 // General Study Module, remove them | |
313 result.Remove(DICOM_TAG_INSTITUTION_NAME); | |
314 result.Remove(DICOM_TAG_REQUESTING_PHYSICIAN); | |
315 result.Remove(DICOM_TAG_REQUESTED_PROCEDURE_DESCRIPTION); | |
316 } | |
317 | |
318 void DicomMap::SetupFindSeriesTemplate(DicomMap& result) | |
319 { | |
320 SetupFindTemplate(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag)); | |
321 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false); | |
322 result.SetValue(DICOM_TAG_PATIENT_ID, "", false); | |
323 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false); | |
324 | |
325 // These tags are considered as "main" by Orthanc, but are not in the Series module | |
326 result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer | |
327 result.Remove(DicomTag(0x0008, 0x1010)); // Station name | |
328 result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name | |
329 result.Remove(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES); | |
330 result.Remove(DICOM_TAG_IMAGES_IN_ACQUISITION); | |
331 result.Remove(DICOM_TAG_NUMBER_OF_SLICES); | |
332 result.Remove(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS); | |
333 result.Remove(DICOM_TAG_NUMBER_OF_TIME_SLICES); | |
334 result.Remove(DICOM_TAG_IMAGE_ORIENTATION_PATIENT); | |
335 result.Remove(DICOM_TAG_SERIES_TYPE); | |
336 result.Remove(DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION); | |
337 result.Remove(DICOM_TAG_CONTRAST_BOLUS_AGENT); | |
338 } | |
339 | |
340 void DicomMap::SetupFindInstanceTemplate(DicomMap& result) | |
341 { | |
342 SetupFindTemplate(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag)); | |
343 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false); | |
344 result.SetValue(DICOM_TAG_PATIENT_ID, "", false); | |
345 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false); | |
346 result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false); | |
347 } | |
348 | |
349 | |
350 void DicomMap::CopyTagIfExists(const DicomMap& source, | |
351 const DicomTag& tag) | |
352 { | |
353 if (source.HasTag(tag)) | |
354 { | |
355 SetValue(tag, source.GetValue(tag)); | |
356 } | |
357 } | |
358 | |
359 | |
360 bool DicomMap::IsMainDicomTag(const DicomTag& tag, ResourceType level) | |
361 { | |
362 DicomTag *tags = NULL; | |
363 size_t size; | |
364 | |
365 switch (level) | |
366 { | |
367 case ResourceType_Patient: | |
368 tags = patientTags; | |
369 size = sizeof(patientTags) / sizeof(DicomTag); | |
370 break; | |
371 | |
372 case ResourceType_Study: | |
373 tags = studyTags; | |
374 size = sizeof(studyTags) / sizeof(DicomTag); | |
375 break; | |
376 | |
377 case ResourceType_Series: | |
378 tags = seriesTags; | |
379 size = sizeof(seriesTags) / sizeof(DicomTag); | |
380 break; | |
381 | |
382 case ResourceType_Instance: | |
383 tags = instanceTags; | |
384 size = sizeof(instanceTags) / sizeof(DicomTag); | |
385 break; | |
386 | |
387 default: | |
388 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
389 } | |
390 | |
391 for (size_t i = 0; i < size; i++) | |
392 { | |
393 if (tags[i] == tag) | |
394 { | |
395 return true; | |
396 } | |
397 } | |
398 | |
399 return false; | |
400 } | |
401 | |
402 bool DicomMap::IsMainDicomTag(const DicomTag& tag) | |
403 { | |
404 return (IsMainDicomTag(tag, ResourceType_Patient) || | |
405 IsMainDicomTag(tag, ResourceType_Study) || | |
406 IsMainDicomTag(tag, ResourceType_Series) || | |
407 IsMainDicomTag(tag, ResourceType_Instance)); | |
408 } | |
409 | |
410 | |
411 void DicomMap::GetMainDicomTagsInternal(std::set<DicomTag>& result, ResourceType level) | |
412 { | |
413 DicomTag *tags = NULL; | |
414 size_t size; | |
415 | |
416 switch (level) | |
417 { | |
418 case ResourceType_Patient: | |
419 tags = patientTags; | |
420 size = sizeof(patientTags) / sizeof(DicomTag); | |
421 break; | |
422 | |
423 case ResourceType_Study: | |
424 tags = studyTags; | |
425 size = sizeof(studyTags) / sizeof(DicomTag); | |
426 break; | |
427 | |
428 case ResourceType_Series: | |
429 tags = seriesTags; | |
430 size = sizeof(seriesTags) / sizeof(DicomTag); | |
431 break; | |
432 | |
433 case ResourceType_Instance: | |
434 tags = instanceTags; | |
435 size = sizeof(instanceTags) / sizeof(DicomTag); | |
436 break; | |
437 | |
438 default: | |
439 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
440 } | |
441 | |
442 for (size_t i = 0; i < size; i++) | |
443 { | |
444 result.insert(tags[i]); | |
445 } | |
446 } | |
447 | |
448 | |
449 void DicomMap::GetMainDicomTags(std::set<DicomTag>& result, ResourceType level) | |
450 { | |
451 result.clear(); | |
452 GetMainDicomTagsInternal(result, level); | |
453 } | |
454 | |
455 | |
456 void DicomMap::GetMainDicomTags(std::set<DicomTag>& result) | |
457 { | |
458 result.clear(); | |
459 GetMainDicomTagsInternal(result, ResourceType_Patient); | |
460 GetMainDicomTagsInternal(result, ResourceType_Study); | |
461 GetMainDicomTagsInternal(result, ResourceType_Series); | |
462 GetMainDicomTagsInternal(result, ResourceType_Instance); | |
463 } | |
464 | |
465 | |
466 void DicomMap::GetTags(std::set<DicomTag>& tags) const | |
467 { | |
468 tags.clear(); | |
469 | |
470 for (Map::const_iterator it = map_.begin(); | |
471 it != map_.end(); ++it) | |
472 { | |
473 tags.insert(it->first); | |
474 } | |
475 } | |
476 | |
477 | |
478 static uint16_t ReadUnsignedInteger16(const char* dicom) | |
479 { | |
480 return le16toh(*reinterpret_cast<const uint16_t*>(dicom)); | |
481 } | |
482 | |
483 | |
484 static uint32_t ReadUnsignedInteger32(const char* dicom) | |
485 { | |
486 return le32toh(*reinterpret_cast<const uint32_t*>(dicom)); | |
487 } | |
488 | |
489 | |
490 static bool ValidateTag(const ValueRepresentation& vr, | |
491 const std::string& value) | |
492 { | |
493 switch (vr) | |
494 { | |
495 case ValueRepresentation_ApplicationEntity: | |
496 return value.size() <= 16; | |
497 | |
498 case ValueRepresentation_AgeString: | |
499 return (value.size() == 4 && | |
500 isdigit(value[0]) && | |
501 isdigit(value[1]) && | |
502 isdigit(value[2]) && | |
503 (value[3] == 'D' || value[3] == 'W' || value[3] == 'M' || value[3] == 'Y')); | |
504 | |
505 case ValueRepresentation_AttributeTag: | |
506 return value.size() == 4; | |
507 | |
508 case ValueRepresentation_CodeString: | |
509 return value.size() <= 16; | |
510 | |
511 case ValueRepresentation_Date: | |
512 return value.size() <= 18; | |
513 | |
514 case ValueRepresentation_DecimalString: | |
515 return value.size() <= 16; | |
516 | |
517 case ValueRepresentation_DateTime: | |
518 return value.size() <= 54; | |
519 | |
520 case ValueRepresentation_FloatingPointSingle: | |
521 return value.size() == 4; | |
522 | |
523 case ValueRepresentation_FloatingPointDouble: | |
524 return value.size() == 8; | |
525 | |
526 case ValueRepresentation_IntegerString: | |
527 return value.size() <= 12; | |
528 | |
529 case ValueRepresentation_LongString: | |
530 return value.size() <= 64; | |
531 | |
532 case ValueRepresentation_LongText: | |
533 return value.size() <= 10240; | |
534 | |
535 case ValueRepresentation_OtherByte: | |
536 return true; | |
537 | |
538 case ValueRepresentation_OtherDouble: | |
539 return value.size() <= (static_cast<uint64_t>(1) << 32) - 8; | |
540 | |
541 case ValueRepresentation_OtherFloat: | |
542 return value.size() <= (static_cast<uint64_t>(1) << 32) - 4; | |
543 | |
544 case ValueRepresentation_OtherLong: | |
545 return true; | |
546 | |
547 case ValueRepresentation_OtherWord: | |
548 return true; | |
549 | |
550 case ValueRepresentation_PersonName: | |
551 return true; | |
552 | |
553 case ValueRepresentation_ShortString: | |
554 return value.size() <= 16; | |
555 | |
556 case ValueRepresentation_SignedLong: | |
557 return value.size() == 4; | |
558 | |
559 case ValueRepresentation_Sequence: | |
560 return true; | |
561 | |
562 case ValueRepresentation_SignedShort: | |
563 return value.size() == 2; | |
564 | |
565 case ValueRepresentation_ShortText: | |
566 return value.size() <= 1024; | |
567 | |
568 case ValueRepresentation_Time: | |
569 return value.size() <= 28; | |
570 | |
571 case ValueRepresentation_UnlimitedCharacters: | |
572 return value.size() <= (static_cast<uint64_t>(1) << 32) - 2; | |
573 | |
574 case ValueRepresentation_UniqueIdentifier: | |
575 return value.size() <= 64; | |
576 | |
577 case ValueRepresentation_UnsignedLong: | |
578 return value.size() == 4; | |
579 | |
580 case ValueRepresentation_Unknown: | |
581 return true; | |
582 | |
583 case ValueRepresentation_UniversalResource: | |
584 return value.size() <= (static_cast<uint64_t>(1) << 32) - 2; | |
585 | |
586 case ValueRepresentation_UnsignedShort: | |
587 return value.size() == 2; | |
588 | |
589 case ValueRepresentation_UnlimitedText: | |
590 return value.size() <= (static_cast<uint64_t>(1) << 32) - 2; | |
591 | |
592 default: | |
593 // Assume unsupported tags are OK | |
594 return true; | |
595 } | |
596 } | |
597 | |
598 | |
599 static void RemoveTagPadding(std::string& value, | |
600 const ValueRepresentation& vr) | |
601 { | |
602 /** | |
603 * Remove padding from character strings, if need be. For the time | |
604 * being, only the UI VR is supported. | |
605 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html | |
606 **/ | |
607 | |
608 switch (vr) | |
609 { | |
610 case ValueRepresentation_UniqueIdentifier: | |
611 { | |
612 /** | |
613 * "Values with a VR of UI shall be padded with a single | |
614 * trailing NULL (00H) character when necessary to achieve even | |
615 * length." | |
616 **/ | |
617 | |
618 if (!value.empty() && | |
619 value[value.size() - 1] == '\0') | |
620 { | |
621 value.resize(value.size() - 1); | |
622 } | |
623 | |
624 break; | |
625 } | |
626 | |
627 /** | |
628 * TODO implement other VR | |
629 **/ | |
630 | |
631 default: | |
632 // No padding is applicable to this VR | |
633 break; | |
634 } | |
635 } | |
636 | |
637 | |
638 static bool ReadNextTag(DicomTag& tag, | |
639 ValueRepresentation& vr, | |
640 std::string& value, | |
641 const char* dicom, | |
642 size_t size, | |
643 size_t& position) | |
644 { | |
645 /** | |
646 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2 | |
647 * This function reads a data element with Explicit VR encoded using Little-Endian. | |
648 **/ | |
649 | |
650 if (position + 6 > size) | |
651 { | |
652 return false; | |
653 } | |
654 | |
655 tag = DicomTag(ReadUnsignedInteger16(dicom + position), | |
656 ReadUnsignedInteger16(dicom + position + 2)); | |
657 | |
658 vr = StringToValueRepresentation(std::string(dicom + position + 4, 2), true); | |
659 if (vr == ValueRepresentation_NotSupported) | |
660 { | |
661 return false; | |
662 } | |
663 | |
664 if (vr == ValueRepresentation_OtherByte || | |
665 vr == ValueRepresentation_OtherDouble || | |
666 vr == ValueRepresentation_OtherFloat || | |
667 vr == ValueRepresentation_OtherLong || | |
668 vr == ValueRepresentation_OtherWord || | |
669 vr == ValueRepresentation_Sequence || | |
670 vr == ValueRepresentation_UnlimitedCharacters || | |
671 vr == ValueRepresentation_UniversalResource || | |
672 vr == ValueRepresentation_UnlimitedText || | |
673 vr == ValueRepresentation_Unknown) // Note that "UN" should never appear in the Meta Information | |
674 { | |
675 if (position + 12 > size) | |
676 { | |
677 return false; | |
678 } | |
679 | |
680 uint32_t length = ReadUnsignedInteger32(dicom + position + 8); | |
681 | |
682 if (position + 12 + length > size) | |
683 { | |
684 return false; | |
685 } | |
686 | |
687 value.assign(dicom + position + 12, length); | |
688 position += (12 + length); | |
689 } | |
690 else | |
691 { | |
692 if (position + 8 > size) | |
693 { | |
694 return false; | |
695 } | |
696 | |
697 uint16_t length = ReadUnsignedInteger16(dicom + position + 6); | |
698 | |
699 if (position + 8 + length > size) | |
700 { | |
701 return false; | |
702 } | |
703 | |
704 value.assign(dicom + position + 8, length); | |
705 position += (8 + length); | |
706 } | |
707 | |
708 if (!ValidateTag(vr, value)) | |
709 { | |
710 return false; | |
711 } | |
712 | |
713 RemoveTagPadding(value, vr); | |
714 | |
715 return true; | |
716 } | |
717 | |
718 | |
719 bool DicomMap::ParseDicomMetaInformation(DicomMap& result, | |
720 const char* dicom, | |
721 size_t size) | |
722 { | |
723 /** | |
724 * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html | |
725 * According to Table 7.1-1, besides the "DICM" DICOM prefix, the | |
726 * file preamble (i.e. dicom[0..127]) should not be taken into | |
727 * account to determine whether the file is or is not a DICOM file. | |
728 **/ | |
729 | |
730 if (size < 132 || | |
731 dicom[128] != 'D' || | |
732 dicom[129] != 'I' || | |
733 dicom[130] != 'C' || | |
734 dicom[131] != 'M') | |
735 { | |
736 return false; | |
737 } | |
738 | |
739 | |
740 /** | |
741 * The DICOM File Meta Information must be encoded using the | |
742 * Explicit VR Little Endian Transfer Syntax | |
743 * (UID=1.2.840.10008.1.2.1). | |
744 **/ | |
745 | |
746 result.Clear(); | |
747 | |
748 // First, we read the "File Meta Information Group Length" tag | |
749 // (0002,0000) to know where to stop reading the meta header | |
750 size_t position = 132; | |
751 | |
752 DicomTag tag(0x0000, 0x0000); // Dummy initialization | |
753 ValueRepresentation vr; | |
754 std::string value; | |
755 if (!ReadNextTag(tag, vr, value, dicom, size, position) || | |
756 tag.GetGroup() != 0x0002 || | |
757 tag.GetElement() != 0x0000 || | |
758 vr != ValueRepresentation_UnsignedLong || | |
759 value.size() != 4) | |
760 { | |
761 return false; | |
762 } | |
763 | |
764 size_t stopPosition = position + ReadUnsignedInteger32(value.c_str()); | |
765 if (stopPosition > size) | |
766 { | |
767 return false; | |
768 } | |
769 | |
770 while (position < stopPosition) | |
771 { | |
772 if (ReadNextTag(tag, vr, value, dicom, size, position)) | |
773 { | |
774 result.SetValue(tag, value, IsBinaryValueRepresentation(vr)); | |
775 } | |
776 else | |
777 { | |
778 return false; | |
779 } | |
780 } | |
781 | |
782 return true; | |
783 } | |
784 } |