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 }