Mercurial > hg > orthanc
comparison OrthancServer/Sources/ServerContext.cpp @ 4514:5b929e6b3c36
removal of "dicom-as-json" attachments
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 16 Feb 2021 12:18:41 +0100 |
parents | 1f455b86b054 |
children | a3c6678aa7b1 |
comparison
equal
deleted
inserted
replaced
4513:1f455b86b054 | 4514:5b929e6b3c36 |
---|---|
581 FileContentType_Dicom, compression, storeMD5_); | 581 FileContentType_Dicom, compression, storeMD5_); |
582 | 582 |
583 ServerIndex::Attachments attachments; | 583 ServerIndex::Attachments attachments; |
584 attachments.push_back(dicomInfo); | 584 attachments.push_back(dicomInfo); |
585 | 585 |
586 FileInfo jsonInfo; | 586 FileInfo dicomUntilPixelData; |
587 if (true /* TODO - !area_.HasReadRange() || !hasPixelDataOffset || compression != CompressionType_DicomAsJson */) | 587 if (hasPixelDataOffset && |
588 { | 588 (!area_.HasReadRange() || |
589 jsonInfo = accessor.Write(dicomAsJson.toStyledString(), | 589 compression != CompressionType_None)) |
590 FileContentType_DicomAsJson, compression, storeMD5_); | 590 { |
591 attachments.push_back(jsonInfo); | 591 dicomUntilPixelData = accessor.Write(dicom.GetBufferData(), pixelDataOffset, |
592 FileContentType_DicomUntilPixelData, compression, storeMD5_); | |
593 attachments.push_back(dicomUntilPixelData); | |
592 } | 594 } |
593 | 595 |
594 typedef std::map<MetadataType, std::string> InstanceMetadata; | 596 typedef std::map<MetadataType, std::string> InstanceMetadata; |
595 InstanceMetadata instanceMetadata; | 597 InstanceMetadata instanceMetadata; |
596 StoreStatus status = index_.Store( | 598 StoreStatus status = index_.Store( |
608 | 610 |
609 if (status != StoreStatus_Success) | 611 if (status != StoreStatus_Success) |
610 { | 612 { |
611 accessor.Remove(dicomInfo); | 613 accessor.Remove(dicomInfo); |
612 | 614 |
613 if (jsonInfo.IsValid()) | 615 if (dicomUntilPixelData.IsValid()) |
614 { | 616 { |
615 accessor.Remove(jsonInfo); | 617 accessor.Remove(dicomUntilPixelData); |
616 } | 618 } |
617 } | 619 } |
618 | 620 |
619 switch (status) | 621 switch (status) |
620 { | 622 { |
805 throw; | 807 throw; |
806 } | 808 } |
807 } | 809 } |
808 | 810 |
809 | 811 |
812 static void InjectEmptyPixelData(Json::Value& dicomAsJson) | |
813 { | |
814 // This is for backward compatibility with Orthanc <= 1.9.0 | |
815 Json::Value pixelData = Json::objectValue; | |
816 pixelData["Name"] = "PixelData"; | |
817 pixelData["Type"] = "Null"; | |
818 pixelData["Value"] = Json::nullValue; | |
819 | |
820 dicomAsJson["7fe0,0010"] = pixelData; | |
821 } | |
822 | |
823 | |
810 void ServerContext::ReadDicomAsJson(Json::Value& result, | 824 void ServerContext::ReadDicomAsJson(Json::Value& result, |
811 const std::string& instancePublicId, | 825 const std::string& instancePublicId, |
812 const std::set<DicomTag>& ignoreTagLength) | 826 const std::set<DicomTag>& ignoreTagLength) |
813 { | 827 { |
814 if (ignoreTagLength.empty()) | 828 /** |
815 { | 829 * CASE 1: The DICOM file, truncated at pixel data, is available |
816 std::string tmp; | 830 * as an attachment (it was created either because the storage |
817 | 831 * area does not support range reads, or it "StorageCompression" |
818 { | 832 * is enabled). Simply return this attachment. |
819 FileInfo attachment; | 833 **/ |
820 if (index_.LookupAttachment(attachment, instancePublicId, FileContentType_DicomAsJson)) | |
821 { | |
822 StorageAccessor accessor(area_, GetMetricsRegistry()); | |
823 accessor.Read(tmp, attachment); | |
824 } | |
825 else | |
826 { | |
827 // The "DICOM as JSON" summary is not available from the Orthanc | |
828 // store (most probably deleted), reconstruct it from the DICOM file | |
829 std::string dicom; | |
830 ReadDicom(dicom, instancePublicId); | |
831 | |
832 LOG(INFO) << "Reconstructing the missing DICOM-as-JSON summary for instance: " | |
833 << instancePublicId; | |
834 | 834 |
835 ParsedDicomFile parsed(dicom); | 835 FileInfo attachment; |
836 | 836 |
837 Json::Value summary; | 837 if (index_.LookupAttachment(attachment, instancePublicId, FileContentType_DicomUntilPixelData)) |
838 OrthancConfiguration::DefaultDicomDatasetToJson(summary, parsed); | 838 { |
839 | |
840 tmp = summary.toStyledString(); | |
841 | |
842 if (!AddAttachment(instancePublicId, FileContentType_DicomAsJson, | |
843 tmp.c_str(), tmp.size())) | |
844 { | |
845 throw OrthancException(ErrorCode_InternalError, | |
846 "Cannot associate the DICOM-as-JSON summary to instance: " + instancePublicId); | |
847 } | |
848 } | |
849 } | |
850 | |
851 if (!Toolbox::ReadJson(result, tmp)) | |
852 { | |
853 throw OrthancException(ErrorCode_CorruptedFile); | |
854 } | |
855 } | |
856 else | |
857 { | |
858 // The "DicomAsJson" attachment might have stored some tags as | |
859 // "too long". We are forced to re-parse the DICOM file. | |
860 std::string dicom; | 839 std::string dicom; |
861 ReadDicom(dicom, instancePublicId); | 840 |
841 { | |
842 StorageAccessor accessor(area_, GetMetricsRegistry()); | |
843 accessor.Read(dicom, attachment); | |
844 } | |
862 | 845 |
863 ParsedDicomFile parsed(dicom); | 846 ParsedDicomFile parsed(dicom); |
864 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength); | 847 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength); |
848 InjectEmptyPixelData(result); | |
849 } | |
850 else | |
851 { | |
852 /** | |
853 * The truncated DICOM file is not stored as a standalone | |
854 * attachment. Lookup whether the pixel data offset has already | |
855 * been computed for this instance. | |
856 **/ | |
857 | |
858 bool hasPixelDataOffset; | |
859 uint64_t pixelDataOffset; | |
860 | |
861 { | |
862 std::string s; | |
863 if (index_.LookupMetadata(s, instancePublicId, ResourceType_Instance, | |
864 MetadataType_Instance_PixelDataOffset)) | |
865 { | |
866 hasPixelDataOffset = false; | |
867 | |
868 if (!s.empty()) | |
869 { | |
870 try | |
871 { | |
872 pixelDataOffset = boost::lexical_cast<uint64_t>(s); | |
873 hasPixelDataOffset = true; | |
874 } | |
875 catch (boost::bad_lexical_cast&) | |
876 { | |
877 } | |
878 } | |
879 | |
880 if (!hasPixelDataOffset) | |
881 { | |
882 LOG(ERROR) << "Metadata \"PixelDataOffset\" is corrupted for instance: " << instancePublicId; | |
883 } | |
884 } | |
885 else | |
886 { | |
887 // This instance was created by a version of Orthanc <= 1.9.0 | |
888 hasPixelDataOffset = false; | |
889 } | |
890 } | |
891 | |
892 | |
893 if (hasPixelDataOffset && | |
894 area_.HasReadRange() && | |
895 index_.LookupAttachment(attachment, instancePublicId, FileContentType_Dicom) && | |
896 attachment.GetCompressionType() == CompressionType_None) | |
897 { | |
898 /** | |
899 * CASE 2: The pixel data offset is known, AND that a range read | |
900 * can be used to retrieve the truncated DICOM file. Note that | |
901 * this case cannot be used if "StorageCompression" option is | |
902 * "true". | |
903 **/ | |
904 | |
905 StorageAccessor accessor(area_, GetMetricsRegistry()); | |
906 std::unique_ptr<IMemoryBuffer> dicom( | |
907 area_.ReadRange(attachment.GetUuid(), FileContentType_Dicom, 0, pixelDataOffset)); | |
908 | |
909 if (dicom.get() == NULL) | |
910 { | |
911 throw OrthancException(ErrorCode_InternalError); | |
912 } | |
913 else | |
914 { | |
915 assert(dicom->GetSize() == pixelDataOffset); | |
916 ParsedDicomFile parsed(dicom->GetData(), dicom->GetSize()); | |
917 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength); | |
918 InjectEmptyPixelData(result); | |
919 } | |
920 } | |
921 else if (ignoreTagLength.empty() && | |
922 index_.LookupAttachment(attachment, instancePublicId, FileContentType_DicomAsJson)) | |
923 { | |
924 /** | |
925 * CASE 3: This instance was created using Orthanc <= | |
926 * 1.9.0. Reuse the old "DICOM-as-JSON" attachment if available. | |
927 * This is for backward compatibility: A call to | |
928 * "/tools/invalidate-tags" or to one flavors of | |
929 * "/.../.../reconstruct" will disable this case. | |
930 **/ | |
931 | |
932 std::string dicomAsJson; | |
933 | |
934 { | |
935 StorageAccessor accessor(area_, GetMetricsRegistry()); | |
936 accessor.Read(dicomAsJson, attachment); | |
937 } | |
938 | |
939 if (!Toolbox::ReadJson(result, dicomAsJson)) | |
940 { | |
941 throw OrthancException(ErrorCode_CorruptedFile, | |
942 "Corrupted DICOM-as-JSON attachment of instance: " + instancePublicId); | |
943 } | |
944 } | |
945 else | |
946 { | |
947 /** | |
948 * CASE 4: Neither the truncated DICOM file is accessible, nor | |
949 * the DICOM-as-JSON summary. We have to retrieve the full DICOM | |
950 * file from the storage area. | |
951 **/ | |
952 | |
953 std::string dicom; | |
954 ReadDicom(dicom, instancePublicId); | |
955 | |
956 ParsedDicomFile parsed(dicom); | |
957 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength); | |
958 | |
959 if (!hasPixelDataOffset) | |
960 { | |
961 /** | |
962 * The pixel data offset was never computed for this | |
963 * instance, which indicates that it was created using | |
964 * Orthanc <= 1.9.0, or that calls to | |
965 * "LookupPixelDataOffset()" from earlier versions of | |
966 * Orthanc have failed. Try again this precomputation now | |
967 * for future calls. | |
968 **/ | |
969 if (DicomStreamReader::LookupPixelDataOffset(pixelDataOffset, dicom) && | |
970 pixelDataOffset < dicom.size()) | |
971 { | |
972 index_.SetMetadata(instancePublicId, MetadataType_Instance_PixelDataOffset, | |
973 boost::lexical_cast<std::string>(pixelDataOffset)); | |
974 | |
975 if (!area_.HasReadRange() || | |
976 compressionEnabled_ != CompressionType_None) | |
977 { | |
978 AddAttachment(instancePublicId, FileContentType_DicomUntilPixelData, | |
979 dicom.empty() ? NULL: dicom.c_str(), pixelDataOffset); | |
980 } | |
981 } | |
982 } | |
983 } | |
865 } | 984 } |
866 } | 985 } |
867 | 986 |
868 | 987 |
869 void ServerContext::ReadDicomAsJson(Json::Value& result, | 988 void ServerContext::ReadDicomAsJson(Json::Value& result, |