Mercurial > hg > orthanc
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 } |