comparison Core/DicomFormat/DicomMap.cpp @ 2007:655489d9165d

DicomMap::ParseDicomMetaInformation()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 09 Jun 2016 15:46:33 +0200
parents b1291df2f780
children dc82c754dcaa
comparison
equal deleted inserted replaced
2006:6301bbcbcaed 2007:655489d9165d
34 #include "DicomMap.h" 34 #include "DicomMap.h"
35 35
36 #include <stdio.h> 36 #include <stdio.h>
37 #include <memory> 37 #include <memory>
38 #include "DicomArray.h" 38 #include "DicomArray.h"
39 #include "../Endianness.h"
39 #include "../OrthancException.h" 40 #include "../OrthancException.h"
40 41
41 42
42 namespace Orthanc 43 namespace Orthanc
43 { 44 {
290 { 291 {
291 result.Clear(); 292 result.Clear();
292 293
293 for (size_t i = 0; i < count; i++) 294 for (size_t i = 0; i < count; i++)
294 { 295 {
295 result.SetValue(tags[i], ""); 296 result.SetValue(tags[i], "", false);
296 } 297 }
297 } 298 }
298 299
299 void DicomMap::SetupFindPatientTemplate(DicomMap& result) 300 void DicomMap::SetupFindPatientTemplate(DicomMap& result)
300 { 301 {
302 } 303 }
303 304
304 void DicomMap::SetupFindStudyTemplate(DicomMap& result) 305 void DicomMap::SetupFindStudyTemplate(DicomMap& result)
305 { 306 {
306 SetupFindTemplate(result, studyTags, sizeof(studyTags) / sizeof(DicomTag)); 307 SetupFindTemplate(result, studyTags, sizeof(studyTags) / sizeof(DicomTag));
307 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); 308 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
308 result.SetValue(DICOM_TAG_PATIENT_ID, ""); 309 result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
309 310
310 // These main DICOM tags are only indirectly related to the 311 // These main DICOM tags are only indirectly related to the
311 // General Study Module, remove them 312 // General Study Module, remove them
312 result.Remove(DICOM_TAG_INSTITUTION_NAME); 313 result.Remove(DICOM_TAG_INSTITUTION_NAME);
313 result.Remove(DICOM_TAG_REQUESTING_PHYSICIAN); 314 result.Remove(DICOM_TAG_REQUESTING_PHYSICIAN);
315 } 316 }
316 317
317 void DicomMap::SetupFindSeriesTemplate(DicomMap& result) 318 void DicomMap::SetupFindSeriesTemplate(DicomMap& result)
318 { 319 {
319 SetupFindTemplate(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag)); 320 SetupFindTemplate(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag));
320 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); 321 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
321 result.SetValue(DICOM_TAG_PATIENT_ID, ""); 322 result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
322 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, ""); 323 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
323 324
324 // These tags are considered as "main" by Orthanc, but are not in the Series module 325 // These tags are considered as "main" by Orthanc, but are not in the Series module
325 result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer 326 result.Remove(DicomTag(0x0008, 0x0070)); // Manufacturer
326 result.Remove(DicomTag(0x0008, 0x1010)); // Station name 327 result.Remove(DicomTag(0x0008, 0x1010)); // Station name
327 result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name 328 result.Remove(DicomTag(0x0018, 0x0024)); // Sequence name
337 } 338 }
338 339
339 void DicomMap::SetupFindInstanceTemplate(DicomMap& result) 340 void DicomMap::SetupFindInstanceTemplate(DicomMap& result)
340 { 341 {
341 SetupFindTemplate(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag)); 342 SetupFindTemplate(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag));
342 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, ""); 343 result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
343 result.SetValue(DICOM_TAG_PATIENT_ID, ""); 344 result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
344 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, ""); 345 result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
345 result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, ""); 346 result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false);
346 } 347 }
347 348
348 349
349 void DicomMap::CopyTagIfExists(const DicomMap& source, 350 void DicomMap::CopyTagIfExists(const DicomMap& source,
350 const DicomTag& tag) 351 const DicomTag& tag)
477 it != map_.end(); ++it) 478 it != map_.end(); ++it)
478 { 479 {
479 tags.insert(it->first); 480 tags.insert(it->first);
480 } 481 }
481 } 482 }
483
484
485 static uint16_t ReadUnsignedInteger16(const char* dicom)
486 {
487 return le16toh(*reinterpret_cast<const uint16_t*>(dicom));
488 }
489
490
491 static uint32_t ReadUnsignedInteger32(const char* dicom)
492 {
493 return le32toh(*reinterpret_cast<const uint32_t*>(dicom));
494 }
495
496
497 static bool ValidateTag(const ValueRepresentation& vr,
498 const std::string& value)
499 {
500 switch (vr)
501 {
502 case ValueRepresentation_ApplicationEntity:
503 return value.size() <= 16;
504
505 case ValueRepresentation_AgeString:
506 return (value.size() == 4 &&
507 isdigit(value[0]) &&
508 isdigit(value[1]) &&
509 isdigit(value[2]) &&
510 (value[3] == 'D' || value[3] == 'W' || value[3] == 'M' || value[3] == 'Y'));
511
512 case ValueRepresentation_AttributeTag:
513 return value.size() == 4;
514
515 case ValueRepresentation_CodeString:
516 return value.size() <= 16;
517
518 case ValueRepresentation_Date:
519 return value.size() <= 18;
520
521 case ValueRepresentation_DecimalString:
522 return value.size() <= 16;
523
524 case ValueRepresentation_DateTime:
525 return value.size() <= 54;
526
527 case ValueRepresentation_FloatingPointSingle:
528 return value.size() == 4;
529
530 case ValueRepresentation_FloatingPointDouble:
531 return value.size() == 8;
532
533 case ValueRepresentation_IntegerString:
534 return value.size() <= 12;
535
536 case ValueRepresentation_LongString:
537 return value.size() <= 64;
538
539 case ValueRepresentation_LongText:
540 return value.size() <= 10240;
541
542 case ValueRepresentation_OtherByte:
543 return true;
544
545 case ValueRepresentation_OtherDouble:
546 return value.size() <= static_cast<size_t>((1llu << 32) - 8);
547
548 case ValueRepresentation_OtherFloat:
549 return value.size() <= static_cast<size_t>((1llu << 32) - 4);
550
551 case ValueRepresentation_OtherLong:
552 return true;
553
554 case ValueRepresentation_OtherWord:
555 return true;
556
557 case ValueRepresentation_PersonName:
558 return true;
559
560 case ValueRepresentation_ShortString:
561 return value.size() <= 16;
562
563 case ValueRepresentation_SignedLong:
564 return value.size() == 4;
565
566 case ValueRepresentation_Sequence:
567 return true;
568
569 case ValueRepresentation_SignedShort:
570 return value.size() == 2;
571
572 case ValueRepresentation_ShortText:
573 return value.size() <= 1024;
574
575 case ValueRepresentation_Time:
576 return value.size() <= 28;
577
578 case ValueRepresentation_UnlimitedCharacters:
579 return value.size() <= static_cast<size_t>((1llu << 32) - 2);
580
581 case ValueRepresentation_UniqueIdentifier:
582 return value.size() <= 64;
583
584 case ValueRepresentation_UnsignedLong:
585 return value.size() == 4;
586
587 case ValueRepresentation_Unknown:
588 return true;
589
590 case ValueRepresentation_UniversalResource:
591 return value.size() <= static_cast<size_t>((1llu << 32) - 2);
592
593 case ValueRepresentation_UnsignedShort:
594 return value.size() == 2;
595
596 case ValueRepresentation_UnlimitedText:
597 return value.size() <= static_cast<size_t>((1llu << 32) - 2);
598
599 default:
600 // Assume unsupported tags are OK
601 return true;
602 }
603 }
604
605
606 static void RemoveTagPadding(std::string& value,
607 const ValueRepresentation& vr)
608 {
609 /**
610 * Remove padding from character strings, if need be. For the time
611 * being, only the UI VR is supported.
612 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
613 **/
614
615 switch (vr)
616 {
617 case ValueRepresentation_UniqueIdentifier:
618 {
619 /**
620 * "Values with a VR of UI shall be padded with a single
621 * trailing NULL (00H) character when necessary to achieve even
622 * length."
623 **/
624
625 if (!value.empty() &&
626 value[value.size() - 1] == '\0')
627 {
628 value.resize(value.size() - 1);
629 }
630
631 break;
632 }
633
634 /**
635 * TODO implement other VR
636 **/
637
638 default:
639 // No padding is applicable to this VR
640 break;
641 }
642 }
643
644
645 static bool ReadNextTag(DicomTag& tag,
646 ValueRepresentation& vr,
647 std::string& value,
648 const char* dicom,
649 size_t size,
650 size_t& position)
651 {
652 /**
653 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2
654 * This function reads a data element with Explicit VR encoded using Little-Endian.
655 **/
656
657 if (position + 6 > size)
658 {
659 return false;
660 }
661
662 tag = DicomTag(ReadUnsignedInteger16(dicom + position),
663 ReadUnsignedInteger16(dicom + position + 2));
664
665 vr = StringToValueRepresentation(std::string(dicom + position + 4, 2), true);
666 if (vr == ValueRepresentation_NotSupported)
667 {
668 return false;
669 }
670
671 if (vr == ValueRepresentation_OtherByte ||
672 vr == ValueRepresentation_OtherDouble ||
673 vr == ValueRepresentation_OtherFloat ||
674 vr == ValueRepresentation_OtherLong ||
675 vr == ValueRepresentation_OtherWord ||
676 vr == ValueRepresentation_Sequence ||
677 vr == ValueRepresentation_UnlimitedCharacters ||
678 vr == ValueRepresentation_UniversalResource ||
679 vr == ValueRepresentation_UnlimitedText ||
680 vr == ValueRepresentation_Unknown) // Note that "UN" should never appear in the Meta Information
681 {
682 if (position + 12 > size)
683 {
684 return false;
685 }
686
687 uint32_t length = ReadUnsignedInteger32(dicom + position + 8);
688
689 if (position + 12 + length > size)
690 {
691 return false;
692 }
693
694 value.assign(dicom + position + 12, length);
695 position += (12 + length);
696 }
697 else
698 {
699 if (position + 8 > size)
700 {
701 return false;
702 }
703
704 uint16_t length = ReadUnsignedInteger16(dicom + position + 6);
705
706 if (position + 8 + length > size)
707 {
708 return false;
709 }
710
711 value.assign(dicom + position + 8, length);
712 position += (8 + length);
713 }
714
715 if (!ValidateTag(vr, value))
716 {
717 return false;
718 }
719
720 RemoveTagPadding(value, vr);
721
722 return true;
723 }
724
725
726 bool DicomMap::ParseDicomMetaInformation(DicomMap& result,
727 const char* dicom,
728 size_t size)
729 {
730 /**
731 * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html
732 * According to Table 7.1-1, besides the "DICM" DICOM prefix, the
733 * file preamble (i.e. dicom[0..127]) should not be taken into
734 * account to determine whether the file is or is not a DICOM file.
735 **/
736
737 if (size < 132 ||
738 dicom[128] != 'D' ||
739 dicom[129] != 'I' ||
740 dicom[130] != 'C' ||
741 dicom[131] != 'M')
742 {
743 return false;
744 }
745
746
747 /**
748 * The DICOM File Meta Information must be encoded using the
749 * Explicit VR Little Endian Transfer Syntax
750 * (UID=1.2.840.10008.1.2.1).
751 **/
752
753 result.Clear();
754
755 // First, we read the "File Meta Information Group Length" tag
756 // (0002,0000) to know where to stop reading the meta header
757 size_t position = 132;
758
759 DicomTag tag(0x0000, 0x0000); // Dummy initialization
760 ValueRepresentation vr;
761 std::string value;
762 if (!ReadNextTag(tag, vr, value, dicom, size, position) ||
763 tag.GetGroup() != 0x0002 ||
764 tag.GetElement() != 0x0000 ||
765 vr != ValueRepresentation_UnsignedLong ||
766 value.size() != 4)
767 {
768 return false;
769 }
770
771 size_t stopPosition = position + ReadUnsignedInteger32(value.c_str());
772 if (stopPosition > size)
773 {
774 return false;
775 }
776
777 while (position < stopPosition)
778 {
779 if (ReadNextTag(tag, vr, value, dicom, size, position))
780 {
781 result.SetValue(tag, value, IsBinaryValueRepresentation(vr));
782 }
783 else
784 {
785 return false;
786 }
787 }
788
789 return true;
790 }
482 } 791 }