comparison OrthancFramework/UnitTestsSources/DicomMapTests.cpp @ 4215:28e9457dc7ab

working on sequences
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Sep 2020 16:30:24 +0200
parents 7b011cfda135
children 61585d8a9faf
comparison
equal deleted inserted replaced
4214:7b011cfda135 4215:28e9457dc7ab
787 public: 787 public:
788 virtual ~IVisitor() 788 virtual ~IVisitor()
789 { 789 {
790 } 790 }
791 791
792 // The data from this function will always be Little Endian (as
793 // specified by the DICOM standard)
792 virtual void VisitMetaHeaderTag(const DicomTag& tag, 794 virtual void VisitMetaHeaderTag(const DicomTag& tag,
793 const ValueRepresentation& vr, 795 const ValueRepresentation& vr,
794 const std::string& value) = 0; 796 const std::string& value) = 0;
795 797
796 // Return "false" to stop processing 798 // Return "false" to stop processing
797 virtual bool VisitDatasetTag(const DicomTag& tag, 799 virtual bool VisitDatasetTag(const DicomTag& tag,
798 const ValueRepresentation& vr, 800 const ValueRepresentation& vr,
799 DicomTransferSyntax transferSyntax, 801 DicomTransferSyntax transferSyntax,
800 const std::string& value) = 0; 802 const std::string& value,
803 bool isLittleEndian) = 0;
801 }; 804 };
802 805
803 private: 806 private:
804 enum State 807 enum State
805 { 808 {
812 }; 815 };
813 816
814 StreamBlockReader reader_; 817 StreamBlockReader reader_;
815 State state_; 818 State state_;
816 DicomTransferSyntax transferSyntax_; 819 DicomTransferSyntax transferSyntax_;
820 DicomTag previousTag_;
817 DicomTag danglingTag_; 821 DicomTag danglingTag_;
818 ValueRepresentation danglingVR_; 822 ValueRepresentation danglingVR_;
823 unsigned int sequenceDepth_;
819 824
820 static uint16_t ReadUnsignedInteger16(const char* dicom, 825 static uint16_t ReadUnsignedInteger16(const char* dicom,
821 bool littleEndian) 826 bool littleEndian)
822 { 827 {
823 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom); 828 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom);
892 vr == ValueRepresentation_ShortText /* ST */ || 897 vr == ValueRepresentation_ShortText /* ST */ ||
893 vr == ValueRepresentation_Time /* TM */ || 898 vr == ValueRepresentation_Time /* TM */ ||
894 vr == ValueRepresentation_UniqueIdentifier /* UI */ || 899 vr == ValueRepresentation_UniqueIdentifier /* UI */ ||
895 vr == ValueRepresentation_UnsignedLong /* UL */ || 900 vr == ValueRepresentation_UnsignedLong /* UL */ ||
896 vr == ValueRepresentation_UnsignedShort /* US */); 901 vr == ValueRepresentation_UnsignedShort /* US */);
902 }
903
904
905 bool IsLittleEndian() const
906 {
907 return (transferSyntax_ != DicomTransferSyntax_BigEndianExplicit);
897 } 908 }
898 909
899 910
900 void PrintBlock(const std::string& block) 911 void PrintBlock(const std::string& block)
901 { 912 {
1029 1040
1030 1041
1031 void HandleDatasetTag(IVisitor& visitor, 1042 void HandleDatasetTag(IVisitor& visitor,
1032 const std::string& block) 1043 const std::string& block)
1033 { 1044 {
1034 printf("DATASET TAG:\n"); 1045 static const DicomTag DICOM_TAG_SEQUENCE_ITEM(0xfffe, 0xe000);
1035 PrintBlock(block); 1046 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_ITEM(0xfffe, 0xe00d);
1047 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE(0xfffe, 0xe0dd);
1048
1049 //printf("DATASET TAG:\n");
1050 //PrintBlock(block);
1036 1051
1037 assert(block.size() == 8u); 1052 assert(block.size() == 8u);
1038 1053
1039 const bool littleEndian = (transferSyntax_ != DicomTransferSyntax_BigEndianExplicit); 1054 const bool littleEndian = IsLittleEndian();
1055 DicomTag tag = ReadTag(block.c_str(), littleEndian);
1056
1057 if (tag == DICOM_TAG_SEQUENCE_ITEM ||
1058 tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM ||
1059 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
1060 {
1061 printf("SEQUENCE TAG:\n");
1062 PrintBlock(block);
1063
1064 // The special sequence items are encoded like "Implicit VR"
1065 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, littleEndian);
1066
1067 if (tag == DICOM_TAG_SEQUENCE_ITEM)
1068 {
1069 for (unsigned int i = 0; i <= sequenceDepth_; i++)
1070 printf(" ");
1071 if (length == 0xffffffffu)
1072 {
1073 // Undefined length: Need to loop over the tags of the nested dataset
1074 printf("...next dataset in sequence...\n");
1075 reader_.Schedule(8);
1076 state_ = State_DatasetTag;
1077 }
1078 else
1079 {
1080 // Explicit length: Can skip the full sequence at once
1081 printf("...next dataset in sequence... %u bytes\n", length);
1082 reader_.Schedule(length);
1083 state_ = State_DatasetValue;
1084 }
1085 }
1086 else if (tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM ||
1087 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
1088 {
1089 if (length != 0 ||
1090 sequenceDepth_ == 0)
1091 {
1092 throw OrthancException(ErrorCode_BadFileFormat);
1093 }
1094
1095 if (tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
1096 {
1097 sequenceDepth_ --;
1098
1099 for (unsigned int i = 0; i <= sequenceDepth_; i++)
1100 printf(" ");
1101 printf("...leaving sequence...\n");
1102 }
1103
1104 reader_.Schedule(8);
1105 state_ = State_DatasetTag;
1106 }
1107 else
1108 {
1109 throw OrthancException(ErrorCode_InternalError);
1110 }
1111 }
1112 else
1113 {
1114 ValueRepresentation vr = ValueRepresentation_Unknown;
1040 1115
1041 danglingTag_ = ReadTag(block.c_str(), littleEndian); 1116 if (transferSyntax_ == DicomTransferSyntax_LittleEndianImplicit)
1042 danglingVR_ = ValueRepresentation_Unknown; 1117 {
1043 1118 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, true /* little endian */);
1044 /*if (danglingTag_ == DICOM_TAG_PIXEL_DATA)
1045 {
1046 state_ = State_Done;
1047 return;
1048 }*/
1049
1050 if (transferSyntax_ == DicomTransferSyntax_LittleEndianImplicit)
1051 {
1052 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, true /* little endian */);
1053 1119
1054 reader_.Schedule(length);
1055 state_ = State_DatasetValue;
1056 }
1057 else
1058 {
1059 // This in an explicit transfer syntax
1060
1061 danglingVR_ = StringToValueRepresentation(
1062 std::string(block.c_str() + 4, 2), false /* ignore unknown VR */);
1063
1064 if (IsShortExplicitTag(danglingVR_))
1065 {
1066 uint16_t length = ReadUnsignedInteger16(block.c_str() + 6, littleEndian);
1067
1068 reader_.Schedule(length); 1120 reader_.Schedule(length);
1069 state_ = State_DatasetValue; 1121 state_ = State_DatasetValue;
1070 } 1122 }
1071 else 1123 else
1072 { 1124 {
1073 uint16_t reserved = ReadUnsignedInteger16(block.c_str() + 6, littleEndian); 1125 // This in an explicit transfer syntax
1074 if (reserved != 0) 1126
1127 vr = StringToValueRepresentation(
1128 std::string(block.c_str() + 4, 2), false /* ignore unknown VR */);
1129
1130 if (vr == ValueRepresentation_Sequence)
1075 { 1131 {
1076 throw OrthancException(ErrorCode_BadFileFormat); 1132 for (unsigned int i = 0; i <= sequenceDepth_; i++)
1133 printf(" ");
1134 printf("...entering sequence... %s\n", previousTag_.Format().c_str());
1135 sequenceDepth_ ++;
1136 reader_.Schedule(4);
1137 state_ = State_DatasetExplicitLength;
1077 } 1138 }
1078 1139 else if (IsShortExplicitTag(vr))
1079 reader_.Schedule(4); 1140 {
1080 state_ = State_DatasetExplicitLength; 1141 uint16_t length = ReadUnsignedInteger16(block.c_str() + 6, littleEndian);
1142
1143 reader_.Schedule(length);
1144 state_ = State_DatasetValue;
1145 }
1146 else
1147 {
1148 uint16_t reserved = ReadUnsignedInteger16(block.c_str() + 6, littleEndian);
1149 if (reserved != 0)
1150 {
1151 throw OrthancException(ErrorCode_BadFileFormat);
1152 }
1153
1154 reader_.Schedule(4);
1155 state_ = State_DatasetExplicitLength;
1156 }
1081 } 1157 }
1082 }; 1158
1159 previousTag_ = tag;
1160
1161 if (sequenceDepth_ == 0)
1162 {
1163 danglingTag_ = tag;
1164 danglingVR_ = vr;
1165 }
1166 else
1167 {
1168 for (unsigned int i = 0; i <= sequenceDepth_; i++)
1169 printf(" ");
1170 printf(" %s\n", tag.Format().c_str());
1171 }
1172 }
1083 } 1173 }
1084 1174
1085 1175
1086 void HandleDatasetExplicitLength(IVisitor& visitor, 1176 void HandleDatasetExplicitLength(IVisitor& visitor,
1087 const std::string& block) 1177 const std::string& block)
1089 //printf("DATASET TAG LENGTH:\n"); 1179 //printf("DATASET TAG LENGTH:\n");
1090 //PrintBlock(block); 1180 //PrintBlock(block);
1091 1181
1092 assert(block.size() == 4); 1182 assert(block.size() == 4);
1093 1183
1094 const bool littleEndian = (transferSyntax_ != DicomTransferSyntax_BigEndianExplicit); 1184 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian());
1095
1096 uint32_t length = ReadUnsignedInteger32(block.c_str(), littleEndian);
1097 if (length == 0xffffffffu) 1185 if (length == 0xffffffffu)
1098 { 1186 {
1099 // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.html
1100 printf("AIE\n");
1101
1102 /** 1187 /**
1103 * This is the case for compressed transfer syntaxes. We stop 1188 * This is the case of sequences, or pixel data with
1104 * the processing here, as this would cause StreamBlockReader 1189 * compressed transfer syntaxes. Schedule the reading of the
1105 * to allocate a huge memory buffer of 2GB. 1190 * first tag of the nested dataset.
1191 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.html
1106 **/ 1192 **/
1107 state_ = State_Done; 1193
1194 state_ = State_DatasetTag;
1195 reader_.Schedule(8);
1108 } 1196 }
1109 else 1197 else
1110 { 1198 {
1111 reader_.Schedule(length); 1199 reader_.Schedule(length);
1112 state_ = State_DatasetValue; 1200 state_ = State_DatasetValue;
1117 public: 1205 public:
1118 DicomStreamReader(std::istream& stream) : 1206 DicomStreamReader(std::istream& stream) :
1119 reader_(stream), 1207 reader_(stream),
1120 state_(State_Preamble), 1208 state_(State_Preamble),
1121 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit), // Dummy 1209 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit), // Dummy
1210 previousTag_(0x0000, 0x0000), // Dummy
1122 danglingTag_(0x0000, 0x0000), // Dummy 1211 danglingTag_(0x0000, 0x0000), // Dummy
1123 danglingVR_(ValueRepresentation_Unknown) // Dummy 1212 danglingVR_(ValueRepresentation_Unknown), // Dummy
1213 sequenceDepth_(0)
1124 { 1214 {
1125 reader_.Schedule(128 /* empty header */ + 1215 reader_.Schedule(128 /* empty header */ +
1126 4 /* "DICM" magic value */ + 1216 4 /* "DICM" magic value */ +
1127 4 /* (0x0002, 0x0000) tag */ + 1217 4 /* (0x0002, 0x0000) tag */ +
1128 2 /* value representation of (0x0002, 0x0000) == "UL" */ + 1218 2 /* value representation of (0x0002, 0x0000) == "UL" */ +
1154 case State_DatasetExplicitLength: 1244 case State_DatasetExplicitLength:
1155 HandleDatasetExplicitLength(visitor, block); 1245 HandleDatasetExplicitLength(visitor, block);
1156 break; 1246 break;
1157 1247
1158 case State_DatasetValue: 1248 case State_DatasetValue:
1159 if (visitor.VisitDatasetTag(danglingTag_, danglingVR_, transferSyntax_, block)) 1249 if (sequenceDepth_ == 0 &&
1250 !visitor.VisitDatasetTag(danglingTag_, danglingVR_, transferSyntax_, block, IsLittleEndian()))
1251 {
1252 state_ = State_Done;
1253 }
1254 else
1160 { 1255 {
1161 reader_.Schedule(8); 1256 reader_.Schedule(8);
1162 state_ = State_DatasetTag; 1257 state_ = State_DatasetTag;
1163 } 1258 }
1164 else 1259
1165 {
1166 state_ = State_Done;
1167 }
1168 break; 1260 break;
1169 1261
1170 default: 1262 default:
1171 throw OrthancException(ErrorCode_InternalError); 1263 throw OrthancException(ErrorCode_InternalError);
1172 } 1264 }
1194 class V : public DicomStreamReader::IVisitor 1286 class V : public DicomStreamReader::IVisitor
1195 { 1287 {
1196 public: 1288 public:
1197 virtual void VisitMetaHeaderTag(const DicomTag& tag, 1289 virtual void VisitMetaHeaderTag(const DicomTag& tag,
1198 const ValueRepresentation& vr, 1290 const ValueRepresentation& vr,
1199 const std::string& value) 1291 const std::string& value) ORTHANC_OVERRIDE
1200 { 1292 {
1201 std::cout << "Header: " << tag.Format() << " [" << value.c_str() << "] (" << value.size() << ")" << std::endl; 1293 std::cout << "Header: " << tag.Format() << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl;
1202 } 1294 }
1203 1295
1204 virtual bool VisitDatasetTag(const DicomTag& tag, 1296 virtual bool VisitDatasetTag(const DicomTag& tag,
1205 const ValueRepresentation& vr, 1297 const ValueRepresentation& vr,
1206 DicomTransferSyntax transferSyntax, 1298 DicomTransferSyntax transferSyntax,
1207 const std::string& value) 1299 const std::string& value,
1208 { 1300 bool isLittleEndian) ORTHANC_OVERRIDE
1301 {
1302 if (!isLittleEndian)
1303 printf("** ");
1209 if (tag.GetGroup() < 0x7f00) 1304 if (tag.GetGroup() < 0x7f00)
1210 std::cout << "Dataset: " << tag.Format() << " [" << value.c_str() << "] (" << value.size() << ")" << std::endl; 1305 std::cout << "Dataset: " << tag.Format() << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl;
1211 else 1306 else
1212 std::cout << "Dataset: " << tag.Format() << " [PIXEL] (" << value.size() << ")" << std::endl; 1307 std::cout << "Dataset: " << tag.Format() << " [PIXEL] (" << value.size() << ")" << std::endl;
1213 1308
1214 return true; 1309 return true;
1215 } 1310 }
1222 { 1317 {
1223 static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/"; 1318 static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/";
1224 1319
1225 std::string dicom; 1320 std::string dicom;
1226 //SystemToolbox::ReadFile(dicom, PATH + "../ColorTestMalaterre.dcm", false); 1321 //SystemToolbox::ReadFile(dicom, PATH + "../ColorTestMalaterre.dcm", false);
1322 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.1.dcm", false);
1227 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.2.dcm", false); // Big Endian 1323 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.2.dcm", false); // Big Endian
1228 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.1.dcm", false);
1229 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.50.dcm", false); 1324 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.50.dcm", false);
1230 SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.51.dcm", false); 1325 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.51.dcm", false);
1326 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.57.dcm", false);
1327 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.70.dcm", false);
1328 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.80.dcm", false);
1329 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.81.dcm", false);
1330 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.90.dcm", false);
1331 //SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.4.91.dcm", false);
1332 SystemToolbox::ReadFile(dicom, PATH + "1.2.840.10008.1.2.5.dcm", false);
1231 1333
1232 std::stringstream stream; 1334 std::stringstream stream;
1233 size_t pos = 0; 1335 size_t pos = 0;
1234 1336
1235 DicomStreamReader r(stream); 1337 DicomStreamReader r(stream);