Mercurial > hg > orthanc
comparison OrthancServer/ParsedDicomFile.cpp @ 1982:b5d4f9c156ad
Modification of instances can now replace PixelData
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 22 Apr 2016 10:28:55 +0200 |
parents | 4b545a8b1f95 |
children | 0ae26237569a |
comparison
equal
deleted
inserted
replaced
1981:4b545a8b1f95 | 1982:b5d4f9c156ad |
---|---|
579 if (pimpl_->file_->getDataset()->tagExists(ToDcmtkBridge::Convert(tag))) | 579 if (pimpl_->file_->getDataset()->tagExists(ToDcmtkBridge::Convert(tag))) |
580 { | 580 { |
581 throw OrthancException(ErrorCode_AlreadyExistingTag); | 581 throw OrthancException(ErrorCode_AlreadyExistingTag); |
582 } | 582 } |
583 | 583 |
584 if (decodeDataUriScheme && | |
585 value.type() == Json::stringValue && | |
586 (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT || | |
587 tag == DICOM_TAG_PIXEL_DATA)) | |
588 { | |
589 if (EmbedContentInternal(value.asString())) | |
590 { | |
591 return; | |
592 } | |
593 } | |
594 | |
584 InvalidateCache(); | 595 InvalidateCache(); |
585 | |
586 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding())); | 596 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding())); |
587 InsertInternal(*pimpl_->file_->getDataset(), element.release()); | 597 InsertInternal(*pimpl_->file_->getDataset(), element.release()); |
588 } | 598 } |
589 | 599 |
590 | 600 |
591 static bool IsReplaceAllowed(DcmDataset& dicom, | 601 static bool CanReplaceProceed(DcmDataset& dicom, |
592 const DcmTagKey& tag, | 602 const DcmTagKey& tag, |
593 DicomReplaceMode mode) | 603 DicomReplaceMode mode) |
594 { | 604 { |
595 if (dicom.findAndDeleteElement(tag).good()) | 605 if (dicom.findAndDeleteElement(tag).good()) |
596 { | 606 { |
597 // This tag was existing, it has been deleted | 607 // This tag was existing, it has been deleted |
598 return true; | 608 return true; |
661 * option. | 671 * option. |
662 **/ | 672 **/ |
663 | 673 |
664 if (tag == DICOM_TAG_SOP_CLASS_UID) | 674 if (tag == DICOM_TAG_SOP_CLASS_UID) |
665 { | 675 { |
666 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, *decoded, DicomReplaceMode_InsertIfAbsent); | 676 ReplacePlainString(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, *decoded); |
667 } | 677 } |
668 | 678 |
669 if (tag == DICOM_TAG_SOP_INSTANCE_UID) | 679 if (tag == DICOM_TAG_SOP_INSTANCE_UID) |
670 { | 680 { |
671 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, *decoded, DicomReplaceMode_InsertIfAbsent); | 681 ReplacePlainString(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, *decoded); |
672 } | 682 } |
673 } | 683 } |
674 | 684 |
675 | 685 |
676 void ParsedDicomFile::Replace(const DicomTag& tag, | 686 void ParsedDicomFile::Replace(const DicomTag& tag, |
677 const std::string& utf8Value, | 687 const std::string& utf8Value, |
688 bool decodeDataUriScheme, | |
678 DicomReplaceMode mode) | 689 DicomReplaceMode mode) |
679 { | 690 { |
680 InvalidateCache(); | 691 InvalidateCache(); |
681 | 692 |
682 std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag)); | |
683 FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, false, GetEncoding()); | |
684 | |
685 DcmDataset& dicom = *pimpl_->file_->getDataset(); | 693 DcmDataset& dicom = *pimpl_->file_->getDataset(); |
686 if (IsReplaceAllowed(dicom, element->getTag(), mode)) | 694 if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode)) |
687 { | 695 { |
688 // Either the tag was previously existing, or the replace mode | 696 // Either the tag was previously existing (and now removed), or |
689 // was set to "InsertIfAbsent" | 697 // the replace mode was set to "InsertIfAbsent" |
698 | |
699 if (decodeDataUriScheme && | |
700 (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT || | |
701 tag == DICOM_TAG_PIXEL_DATA)) | |
702 { | |
703 if (EmbedContentInternal(utf8Value)) | |
704 { | |
705 return; | |
706 } | |
707 } | |
708 | |
709 std::auto_ptr<DcmElement> element(FromDcmtkBridge::CreateElementForTag(tag)); | |
710 FromDcmtkBridge::FillElementWithString(*element, tag, utf8Value, decodeDataUriScheme, GetEncoding()); | |
711 | |
690 InsertInternal(dicom, element.release()); | 712 InsertInternal(dicom, element.release()); |
691 UpdateStorageUid(tag, utf8Value, false); | 713 UpdateStorageUid(tag, utf8Value, false); |
692 } | 714 } |
693 } | 715 } |
694 | 716 |
698 bool decodeDataUriScheme, | 720 bool decodeDataUriScheme, |
699 DicomReplaceMode mode) | 721 DicomReplaceMode mode) |
700 { | 722 { |
701 InvalidateCache(); | 723 InvalidateCache(); |
702 | 724 |
703 std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding())); | |
704 | |
705 DcmDataset& dicom = *pimpl_->file_->getDataset(); | 725 DcmDataset& dicom = *pimpl_->file_->getDataset(); |
706 if (IsReplaceAllowed(dicom, element->getTag(), mode)) | 726 if (CanReplaceProceed(dicom, ToDcmtkBridge::Convert(tag), mode)) |
707 { | 727 { |
708 // Either the tag was previously existing, or the replace mode | 728 // Either the tag was previously existing (and now removed), or |
709 // was set to "InsertIfAbsent" | 729 // the replace mode was set to "InsertIfAbsent" |
710 InsertInternal(dicom, element.release()); | 730 |
731 if (decodeDataUriScheme && | |
732 value.type() == Json::stringValue && | |
733 (tag == DICOM_TAG_ENCAPSULATED_DOCUMENT || | |
734 tag == DICOM_TAG_PIXEL_DATA)) | |
735 { | |
736 if (EmbedContentInternal(value.asString())) | |
737 { | |
738 return; | |
739 } | |
740 } | |
741 | |
742 InsertInternal(dicom, FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding())); | |
711 | 743 |
712 if (tag == DICOM_TAG_SOP_CLASS_UID || | 744 if (tag == DICOM_TAG_SOP_CLASS_UID || |
713 tag == DICOM_TAG_SOP_INSTANCE_UID) | 745 tag == DICOM_TAG_SOP_INSTANCE_UID) |
714 { | 746 { |
715 if (value.type() != Json::stringValue) | 747 if (value.type() != Json::stringValue) |
831 { | 863 { |
832 pimpl_->file_.reset(new DcmFileFormat); | 864 pimpl_->file_.reset(new DcmFileFormat); |
833 | 865 |
834 if (createIdentifiers) | 866 if (createIdentifiers) |
835 { | 867 { |
836 Replace(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient)); | 868 ReplacePlainString(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient)); |
837 Replace(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)); | 869 ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)); |
838 Replace(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series)); | 870 ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series)); |
839 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); | 871 ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); |
840 } | 872 } |
841 } | 873 } |
842 | 874 |
843 | 875 |
844 ParsedDicomFile::ParsedDicomFile(const DicomMap& map) : pimpl_(new PImpl) | 876 ParsedDicomFile::ParsedDicomFile(const DicomMap& map) : pimpl_(new PImpl) |
876 pimpl_(new PImpl) | 908 pimpl_(new PImpl) |
877 { | 909 { |
878 pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone())); | 910 pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone())); |
879 | 911 |
880 // Create a new instance-level identifier | 912 // Create a new instance-level identifier |
881 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); | 913 ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); |
882 } | 914 } |
883 | 915 |
884 | 916 |
885 ParsedDicomFile::ParsedDicomFile(DcmDataset& dicom) : pimpl_(new PImpl) | 917 ParsedDicomFile::ParsedDicomFile(DcmDataset& dicom) : pimpl_(new PImpl) |
886 { | 918 { |
910 { | 942 { |
911 return new ParsedDicomFile(*this); | 943 return new ParsedDicomFile(*this); |
912 } | 944 } |
913 | 945 |
914 | 946 |
915 void ParsedDicomFile::EmbedContent(const std::string& dataUriScheme) | 947 bool ParsedDicomFile::EmbedContentInternal(const std::string& dataUriScheme) |
916 { | 948 { |
917 std::string mime, content; | 949 std::string mime, content; |
918 if (!Toolbox::DecodeDataUriScheme(mime, content, dataUriScheme)) | 950 if (!Toolbox::DecodeDataUriScheme(mime, content, dataUriScheme)) |
919 { | 951 { |
920 throw OrthancException(ErrorCode_BadFileFormat); | 952 return false; |
921 } | 953 } |
922 | 954 |
923 Toolbox::ToLowerCase(mime); | 955 Toolbox::ToLowerCase(mime); |
924 | 956 |
925 if (mime == "image/png" || | 957 if (mime == "image/png" || |
934 else | 966 else |
935 { | 967 { |
936 LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file: " << mime; | 968 LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file: " << mime; |
937 throw OrthancException(ErrorCode_NotImplemented); | 969 throw OrthancException(ErrorCode_NotImplemented); |
938 } | 970 } |
971 | |
972 return true; | |
973 } | |
974 | |
975 | |
976 void ParsedDicomFile::EmbedContent(const std::string& dataUriScheme) | |
977 { | |
978 if (!EmbedContentInternal(dataUriScheme)) | |
979 { | |
980 throw OrthancException(ErrorCode_BadFileFormat); | |
981 } | |
939 } | 982 } |
940 | 983 |
941 | 984 |
942 void ParsedDicomFile::EmbedImage(const std::string& mime, | 985 void ParsedDicomFile::EmbedImage(const std::string& mime, |
943 const std::string& content) | 986 const std::string& content) |
944 { | 987 { |
945 InvalidateCache(); | |
946 | |
947 if (mime == "image/png") | 988 if (mime == "image/png") |
948 { | 989 { |
949 PngReader reader; | 990 PngReader reader; |
950 reader.ReadFromMemory(content); | 991 reader.ReadFromMemory(content); |
951 EmbedImage(reader); | 992 EmbedImage(reader); |
972 accessor.GetFormat() != PixelFormat_RGBA32) | 1013 accessor.GetFormat() != PixelFormat_RGBA32) |
973 { | 1014 { |
974 throw OrthancException(ErrorCode_NotImplemented); | 1015 throw OrthancException(ErrorCode_NotImplemented); |
975 } | 1016 } |
976 | 1017 |
1018 InvalidateCache(); | |
1019 | |
977 if (accessor.GetFormat() == PixelFormat_RGBA32) | 1020 if (accessor.GetFormat() == PixelFormat_RGBA32) |
978 { | 1021 { |
979 LOG(WARNING) << "Getting rid of the alpha channel when embedding a RGBA image inside DICOM"; | 1022 LOG(WARNING) << "Getting rid of the alpha channel when embedding a RGBA image inside DICOM"; |
980 } | 1023 } |
981 | 1024 |
982 // http://dicomiseasy.blogspot.be/2012/08/chapter-12-pixel-data.html | 1025 // http://dicomiseasy.blogspot.be/2012/08/chapter-12-pixel-data.html |
983 | 1026 |
984 Remove(DICOM_TAG_PIXEL_DATA); | 1027 Remove(DICOM_TAG_PIXEL_DATA); |
985 Replace(DICOM_TAG_COLUMNS, boost::lexical_cast<std::string>(accessor.GetWidth())); | 1028 ReplacePlainString(DICOM_TAG_COLUMNS, boost::lexical_cast<std::string>(accessor.GetWidth())); |
986 Replace(DICOM_TAG_ROWS, boost::lexical_cast<std::string>(accessor.GetHeight())); | 1029 ReplacePlainString(DICOM_TAG_ROWS, boost::lexical_cast<std::string>(accessor.GetHeight())); |
987 Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "1"); | 1030 ReplacePlainString(DICOM_TAG_SAMPLES_PER_PIXEL, "1"); |
988 Replace(DICOM_TAG_NUMBER_OF_FRAMES, "1"); | 1031 ReplacePlainString(DICOM_TAG_NUMBER_OF_FRAMES, "1"); |
989 | 1032 |
990 if (accessor.GetFormat() == PixelFormat_SignedGrayscale16) | 1033 if (accessor.GetFormat() == PixelFormat_SignedGrayscale16) |
991 { | 1034 { |
992 Replace(DICOM_TAG_PIXEL_REPRESENTATION, "1"); | 1035 ReplacePlainString(DICOM_TAG_PIXEL_REPRESENTATION, "1"); |
993 } | 1036 } |
994 else | 1037 else |
995 { | 1038 { |
996 Replace(DICOM_TAG_PIXEL_REPRESENTATION, "0"); // Unsigned pixels | 1039 ReplacePlainString(DICOM_TAG_PIXEL_REPRESENTATION, "0"); // Unsigned pixels |
997 } | 1040 } |
998 | 1041 |
999 Replace(DICOM_TAG_PLANAR_CONFIGURATION, "0"); // Color channels are interleaved | 1042 ReplacePlainString(DICOM_TAG_PLANAR_CONFIGURATION, "0"); // Color channels are interleaved |
1000 Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2"); | 1043 ReplacePlainString(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2"); |
1001 | 1044 |
1002 unsigned int bytesPerPixel = 0; | 1045 unsigned int bytesPerPixel = 0; |
1003 | 1046 |
1004 switch (accessor.GetFormat()) | 1047 switch (accessor.GetFormat()) |
1005 { | 1048 { |
1006 case PixelFormat_Grayscale8: | 1049 case PixelFormat_Grayscale8: |
1007 Replace(DICOM_TAG_BITS_ALLOCATED, "8"); | 1050 ReplacePlainString(DICOM_TAG_BITS_ALLOCATED, "8"); |
1008 Replace(DICOM_TAG_BITS_STORED, "8"); | 1051 ReplacePlainString(DICOM_TAG_BITS_STORED, "8"); |
1009 Replace(DICOM_TAG_HIGH_BIT, "7"); | 1052 ReplacePlainString(DICOM_TAG_HIGH_BIT, "7"); |
1010 bytesPerPixel = 1; | 1053 bytesPerPixel = 1; |
1011 break; | 1054 break; |
1012 | 1055 |
1013 case PixelFormat_RGB24: | 1056 case PixelFormat_RGB24: |
1014 case PixelFormat_RGBA32: | 1057 case PixelFormat_RGBA32: |
1015 Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "RGB"); | 1058 ReplacePlainString(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "RGB"); |
1016 Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "3"); | 1059 ReplacePlainString(DICOM_TAG_SAMPLES_PER_PIXEL, "3"); |
1017 Replace(DICOM_TAG_BITS_ALLOCATED, "8"); | 1060 ReplacePlainString(DICOM_TAG_BITS_ALLOCATED, "8"); |
1018 Replace(DICOM_TAG_BITS_STORED, "8"); | 1061 ReplacePlainString(DICOM_TAG_BITS_STORED, "8"); |
1019 Replace(DICOM_TAG_HIGH_BIT, "7"); | 1062 ReplacePlainString(DICOM_TAG_HIGH_BIT, "7"); |
1020 bytesPerPixel = 3; | 1063 bytesPerPixel = 3; |
1021 break; | 1064 break; |
1022 | 1065 |
1023 case PixelFormat_Grayscale16: | 1066 case PixelFormat_Grayscale16: |
1024 case PixelFormat_SignedGrayscale16: | 1067 case PixelFormat_SignedGrayscale16: |
1025 Replace(DICOM_TAG_BITS_ALLOCATED, "16"); | 1068 ReplacePlainString(DICOM_TAG_BITS_ALLOCATED, "16"); |
1026 Replace(DICOM_TAG_BITS_STORED, "16"); | 1069 ReplacePlainString(DICOM_TAG_BITS_STORED, "16"); |
1027 Replace(DICOM_TAG_HIGH_BIT, "15"); | 1070 ReplacePlainString(DICOM_TAG_HIGH_BIT, "15"); |
1028 bytesPerPixel = 2; | 1071 bytesPerPixel = 2; |
1029 break; | 1072 break; |
1030 | 1073 |
1031 default: | 1074 default: |
1032 throw OrthancException(ErrorCode_NotImplemented); | 1075 throw OrthancException(ErrorCode_NotImplemented); |
1098 // DICOM standard. Do not set the SpecificCharacterSet tag. | 1141 // DICOM standard. Do not set the SpecificCharacterSet tag. |
1099 return; | 1142 return; |
1100 } | 1143 } |
1101 | 1144 |
1102 std::string s = GetDicomSpecificCharacterSet(encoding); | 1145 std::string s = GetDicomSpecificCharacterSet(encoding); |
1103 Replace(DICOM_TAG_SPECIFIC_CHARACTER_SET, s, DicomReplaceMode_InsertIfAbsent); | 1146 ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, s); |
1104 } | 1147 } |
1105 | 1148 |
1106 void ParsedDicomFile::ToJson(Json::Value& target, | 1149 void ParsedDicomFile::ToJson(Json::Value& target, |
1107 DicomToJsonFormat format, | 1150 DicomToJsonFormat format, |
1108 DicomToJsonFlags flags, | 1151 DicomToJsonFlags flags, |
1135 { | 1178 { |
1136 LOG(ERROR) << "Not a PDF file"; | 1179 LOG(ERROR) << "Not a PDF file"; |
1137 throw OrthancException(ErrorCode_BadFileFormat); | 1180 throw OrthancException(ErrorCode_BadFileFormat); |
1138 } | 1181 } |
1139 | 1182 |
1140 Replace(DICOM_TAG_SOP_CLASS_UID, UID_EncapsulatedPDFStorage); | 1183 InvalidateCache(); |
1141 Replace(FromDcmtkBridge::Convert(DCM_Modality), "OT"); | 1184 |
1142 Replace(FromDcmtkBridge::Convert(DCM_ConversionType), "WSD"); | 1185 ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, UID_EncapsulatedPDFStorage); |
1143 Replace(FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument), "application/pdf"); | 1186 ReplacePlainString(FromDcmtkBridge::Convert(DCM_Modality), "OT"); |
1144 //Replace(FromDcmtkBridge::Convert(DCM_SeriesNumber), "1"); | 1187 ReplacePlainString(FromDcmtkBridge::Convert(DCM_ConversionType), "WSD"); |
1188 ReplacePlainString(FromDcmtkBridge::Convert(DCM_MIMETypeOfEncapsulatedDocument), "application/pdf"); | |
1189 //ReplacePlainString(FromDcmtkBridge::Convert(DCM_SeriesNumber), "1"); | |
1145 | 1190 |
1146 std::auto_ptr<DcmPolymorphOBOW> element(new DcmPolymorphOBOW(DCM_EncapsulatedDocument)); | 1191 std::auto_ptr<DcmPolymorphOBOW> element(new DcmPolymorphOBOW(DCM_EncapsulatedDocument)); |
1147 | 1192 |
1148 size_t s = pdf.size(); | 1193 size_t s = pdf.size(); |
1149 if (s & 1) | 1194 if (s & 1) |
1247 result->EmbedContent(value.asString()); | 1292 result->EmbedContent(value.asString()); |
1248 } | 1293 } |
1249 } | 1294 } |
1250 else if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) | 1295 else if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) |
1251 { | 1296 { |
1252 result->Replace(tag, value, decodeDataUriScheme); | 1297 result->Replace(tag, value, decodeDataUriScheme, DicomReplaceMode_InsertIfAbsent); |
1253 } | 1298 } |
1254 } | 1299 } |
1255 | 1300 |
1256 return result.release(); | 1301 return result.release(); |
1257 } | 1302 } |