Mercurial > hg > orthanc
comparison OrthancFramework/UnitTestsSources/DicomMapTests.cpp @ 4220:92a21efa5c96
reorganization of DicomStreamReader
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 30 Sep 2020 15:33:47 +0200 |
parents | b8ed2852a35d |
children | 3d6f14a05db1 |
comparison
equal
deleted
inserted
replaced
4219:b8ed2852a35d | 4220:92a21efa5c96 |
---|---|
32 #include <gtest/gtest.h> | 32 #include <gtest/gtest.h> |
33 | 33 |
34 #include "../Sources/Compatibility.h" | 34 #include "../Sources/Compatibility.h" |
35 #include "../Sources/OrthancException.h" | 35 #include "../Sources/OrthancException.h" |
36 #include "../Sources/DicomFormat/DicomMap.h" | 36 #include "../Sources/DicomFormat/DicomMap.h" |
37 #include "../Sources/DicomFormat/DicomStreamReader.h" | |
37 #include "../Sources/DicomParsing/FromDcmtkBridge.h" | 38 #include "../Sources/DicomParsing/FromDcmtkBridge.h" |
38 #include "../Sources/DicomParsing/ToDcmtkBridge.h" | 39 #include "../Sources/DicomParsing/ToDcmtkBridge.h" |
39 #include "../Sources/DicomParsing/ParsedDicomFile.h" | 40 #include "../Sources/DicomParsing/ParsedDicomFile.h" |
40 #include "../Sources/DicomParsing/DicomWebJsonVisitor.h" | 41 #include "../Sources/DicomParsing/DicomWebJsonVisitor.h" |
41 | 42 |
796 } | 797 } |
797 | 798 |
798 | 799 |
799 namespace | 800 namespace |
800 { | 801 { |
801 class StreamBlockReader : public boost::noncopyable | |
802 { | |
803 private: | |
804 std::istream& stream_; | |
805 std::string block_; | |
806 size_t blockPos_; | |
807 uint64_t processedBytes_; | |
808 | |
809 public: | |
810 StreamBlockReader(std::istream& stream) : | |
811 stream_(stream), | |
812 blockPos_(0), | |
813 processedBytes_(0) | |
814 { | |
815 } | |
816 | |
817 void Schedule(size_t blockSize) | |
818 { | |
819 if (!block_.empty()) | |
820 { | |
821 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
822 } | |
823 else | |
824 { | |
825 block_.resize(blockSize); | |
826 blockPos_ = 0; | |
827 } | |
828 } | |
829 | |
830 bool Read(std::string& block) | |
831 { | |
832 if (block_.empty()) | |
833 { | |
834 if (blockPos_ != 0) | |
835 { | |
836 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
837 } | |
838 | |
839 block.clear(); | |
840 return true; | |
841 } | |
842 else | |
843 { | |
844 while (blockPos_ < block_.size()) | |
845 { | |
846 #if 0 | |
847 char c; | |
848 stream_.get(c); | |
849 | |
850 if (stream_.good()) | |
851 { | |
852 block_[blockPos_] = c; | |
853 blockPos_++; | |
854 } | |
855 else | |
856 { | |
857 return false; | |
858 } | |
859 #else | |
860 size_t n = block_.size() - blockPos_; | |
861 std::streamsize r = stream_.readsome(&block_[blockPos_], n); | |
862 if (r == 0) | |
863 { | |
864 return false; | |
865 } | |
866 else | |
867 { | |
868 blockPos_ += r; | |
869 } | |
870 #endif | |
871 } | |
872 | |
873 processedBytes_ += block_.size(); | |
874 | |
875 block.swap(block_); | |
876 block_.clear(); | |
877 return true; | |
878 } | |
879 } | |
880 | |
881 uint64_t GetProcessedBytes() const | |
882 { | |
883 return processedBytes_; | |
884 } | |
885 }; | |
886 | |
887 | |
888 | |
889 | |
890 /** | |
891 * This class parses a stream containing a DICOM instance. It does | |
892 * *not* support the visit of sequences (it only works at the first | |
893 * level of the hierarchy), and it stops the processing once pixel | |
894 * data is reached in compressed transfer syntaxes. | |
895 **/ | |
896 class DicomStreamReader : public boost::noncopyable | |
897 { | |
898 public: | |
899 class IVisitor : public boost::noncopyable | |
900 { | |
901 public: | |
902 virtual ~IVisitor() | |
903 { | |
904 } | |
905 | |
906 // The data from this function will always be Little Endian (as | |
907 // specified by the DICOM standard) | |
908 virtual void VisitMetaHeaderTag(const DicomTag& tag, | |
909 const ValueRepresentation& vr, | |
910 const std::string& value) = 0; | |
911 | |
912 // Return "false" to stop processing | |
913 virtual bool VisitDatasetTag(const DicomTag& tag, | |
914 const ValueRepresentation& vr, | |
915 DicomTransferSyntax transferSyntax, | |
916 const std::string& value, | |
917 bool isLittleEndian) = 0; | |
918 }; | |
919 | |
920 private: | |
921 enum State | |
922 { | |
923 State_Preamble, | |
924 State_MetaHeader, | |
925 State_DatasetTag, | |
926 State_SequenceExplicitLength, | |
927 State_SequenceExplicitValue, | |
928 State_DatasetExplicitLength, | |
929 State_DatasetValue, | |
930 State_Done | |
931 }; | |
932 | |
933 StreamBlockReader reader_; | |
934 State state_; | |
935 DicomTransferSyntax transferSyntax_; | |
936 DicomTag previousTag_; | |
937 DicomTag danglingTag_; // Root-level tag | |
938 ValueRepresentation danglingVR_; | |
939 unsigned int sequenceDepth_; | |
940 | |
941 static uint16_t ReadUnsignedInteger16(const char* dicom, | |
942 bool littleEndian) | |
943 { | |
944 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom); | |
945 | |
946 if (littleEndian) | |
947 { | |
948 return (static_cast<uint16_t>(p[0]) | | |
949 (static_cast<uint16_t>(p[1]) << 8)); | |
950 } | |
951 else | |
952 { | |
953 return (static_cast<uint16_t>(p[1]) | | |
954 (static_cast<uint16_t>(p[0]) << 8)); | |
955 } | |
956 } | |
957 | |
958 | |
959 static uint32_t ReadUnsignedInteger32(const char* dicom, | |
960 bool littleEndian) | |
961 { | |
962 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom); | |
963 | |
964 if (littleEndian) | |
965 { | |
966 return (static_cast<uint32_t>(p[0]) | | |
967 (static_cast<uint32_t>(p[1]) << 8) | | |
968 (static_cast<uint32_t>(p[2]) << 16) | | |
969 (static_cast<uint32_t>(p[3]) << 24)); | |
970 } | |
971 else | |
972 { | |
973 return (static_cast<uint32_t>(p[3]) | | |
974 (static_cast<uint32_t>(p[2]) << 8) | | |
975 (static_cast<uint32_t>(p[1]) << 16) | | |
976 (static_cast<uint32_t>(p[0]) << 24)); | |
977 } | |
978 } | |
979 | |
980 | |
981 static DicomTag ReadTag(const char* dicom, | |
982 bool littleEndian) | |
983 { | |
984 return DicomTag(ReadUnsignedInteger16(dicom, littleEndian), | |
985 ReadUnsignedInteger16(dicom + 2, littleEndian)); | |
986 } | |
987 | |
988 | |
989 static bool IsShortExplicitTag(ValueRepresentation vr) | |
990 { | |
991 /** | |
992 * Are we in the case of Table 7.1-2? "Data Element with | |
993 * Explicit VR of AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO, | |
994 * LT, PN, SH, SL, SS, ST, TM, UI, UL and US" | |
995 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2 | |
996 **/ | |
997 return (vr == ValueRepresentation_ApplicationEntity /* AE */ || | |
998 vr == ValueRepresentation_AgeString /* AS */ || | |
999 vr == ValueRepresentation_AttributeTag /* AT */ || | |
1000 vr == ValueRepresentation_CodeString /* CS */ || | |
1001 vr == ValueRepresentation_Date /* DA */ || | |
1002 vr == ValueRepresentation_DecimalString /* DS */ || | |
1003 vr == ValueRepresentation_DateTime /* DT */ || | |
1004 vr == ValueRepresentation_FloatingPointSingle /* FL */ || | |
1005 vr == ValueRepresentation_FloatingPointDouble /* FD */ || | |
1006 vr == ValueRepresentation_IntegerString /* IS */ || | |
1007 vr == ValueRepresentation_LongString /* LO */ || | |
1008 vr == ValueRepresentation_LongText /* LT */ || | |
1009 vr == ValueRepresentation_PersonName /* PN */ || | |
1010 vr == ValueRepresentation_ShortString /* SH */ || | |
1011 vr == ValueRepresentation_SignedLong /* SL */ || | |
1012 vr == ValueRepresentation_SignedShort /* SS */ || | |
1013 vr == ValueRepresentation_ShortText /* ST */ || | |
1014 vr == ValueRepresentation_Time /* TM */ || | |
1015 vr == ValueRepresentation_UniqueIdentifier /* UI */ || | |
1016 vr == ValueRepresentation_UnsignedLong /* UL */ || | |
1017 vr == ValueRepresentation_UnsignedShort /* US */); | |
1018 } | |
1019 | |
1020 | |
1021 bool IsLittleEndian() const | |
1022 { | |
1023 return (transferSyntax_ != DicomTransferSyntax_BigEndianExplicit); | |
1024 } | |
1025 | |
1026 | |
1027 void PrintBlock(const std::string& block) | |
1028 { | |
1029 for (size_t i = 0; i < block.size(); i++) | |
1030 { | |
1031 printf("%02x ", static_cast<uint8_t>(block[i])); | |
1032 if (i % 16 == 15) | |
1033 printf("\n"); | |
1034 } | |
1035 printf("\n"); | |
1036 } | |
1037 | |
1038 void HandlePreamble(IVisitor& visitor, | |
1039 const std::string& block) | |
1040 { | |
1041 //printf("PREAMBLE:\n"); | |
1042 //PrintBlock(block); | |
1043 | |
1044 assert(block.size() == 144u); | |
1045 assert(reader_.GetProcessedBytes() == 144u); | |
1046 | |
1047 /** | |
1048 * The "DICOM file meta information" is always encoded using | |
1049 * "Explicit VR Little Endian Transfer Syntax" | |
1050 * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html | |
1051 **/ | |
1052 if (block[128] != 'D' || | |
1053 block[129] != 'I' || | |
1054 block[130] != 'C' || | |
1055 block[131] != 'M' || | |
1056 ReadTag(block.c_str() + 132, true) != DicomTag(0x0002, 0x0000) || | |
1057 block[136] != 'U' || | |
1058 block[137] != 'L' || | |
1059 ReadUnsignedInteger16(block.c_str() + 138, true) != 4) | |
1060 { | |
1061 throw OrthancException(ErrorCode_BadFileFormat); | |
1062 } | |
1063 | |
1064 uint32_t length = ReadUnsignedInteger32(block.c_str() + 140, true); | |
1065 | |
1066 reader_.Schedule(length); | |
1067 state_ = State_MetaHeader; | |
1068 } | |
1069 | |
1070 | |
1071 void HandleMetaHeader(IVisitor& visitor, | |
1072 const std::string& block) | |
1073 { | |
1074 //printf("META-HEADER:\n"); | |
1075 //PrintBlock(block); | |
1076 | |
1077 size_t pos = 0; | |
1078 const char* p = block.c_str(); | |
1079 | |
1080 bool hasTransferSyntax = false; | |
1081 | |
1082 while (pos + 8 <= block.size()) | |
1083 { | |
1084 DicomTag tag = ReadTag(p + pos, true); | |
1085 | |
1086 ValueRepresentation vr = StringToValueRepresentation(std::string(p + pos + 4, 2), true); | |
1087 | |
1088 if (IsShortExplicitTag(vr)) | |
1089 { | |
1090 uint16_t length = ReadUnsignedInteger16(p + pos + 6, true); | |
1091 | |
1092 std::string value; | |
1093 value.assign(p + pos + 8, length); | |
1094 | |
1095 if (tag.GetGroup() == 0x0002) | |
1096 { | |
1097 visitor.VisitMetaHeaderTag(tag, vr, value); | |
1098 } | |
1099 | |
1100 if (tag == DICOM_TAG_TRANSFER_SYNTAX_UID) | |
1101 { | |
1102 // Remove possible padding byte | |
1103 if (!value.empty() && | |
1104 value[value.size() - 1] == '\0') | |
1105 { | |
1106 value.resize(value.size() - 1); | |
1107 } | |
1108 | |
1109 if (LookupTransferSyntax(transferSyntax_, value)) | |
1110 { | |
1111 hasTransferSyntax = true; | |
1112 } | |
1113 else | |
1114 { | |
1115 throw OrthancException(ErrorCode_NotImplemented, "Unsupported transfer syntax: " + value); | |
1116 } | |
1117 } | |
1118 | |
1119 pos += length + 8; | |
1120 } | |
1121 else if (pos + 12 <= block.size()) | |
1122 { | |
1123 uint16_t reserved = ReadUnsignedInteger16(p + pos + 6, true); | |
1124 if (reserved != 0) | |
1125 { | |
1126 break; | |
1127 } | |
1128 | |
1129 uint32_t length = ReadUnsignedInteger32(p + pos + 8, true); | |
1130 | |
1131 std::string value; | |
1132 value.assign(p + pos + 12, length); | |
1133 | |
1134 if (tag.GetGroup() == 0x0002) | |
1135 { | |
1136 visitor.VisitMetaHeaderTag(tag, vr, value); | |
1137 } | |
1138 | |
1139 pos += length + 12; | |
1140 } | |
1141 } | |
1142 | |
1143 if (pos != block.size()) | |
1144 { | |
1145 throw OrthancException(ErrorCode_BadFileFormat); | |
1146 } | |
1147 | |
1148 if (!hasTransferSyntax) | |
1149 { | |
1150 throw OrthancException(ErrorCode_BadFileFormat, "DICOM file meta-header without transfer syntax UID"); | |
1151 } | |
1152 | |
1153 reader_.Schedule(8); | |
1154 state_ = State_DatasetTag; | |
1155 } | |
1156 | |
1157 | |
1158 void HandleDatasetTag(const std::string& block, | |
1159 const DicomTag& untilTag) | |
1160 { | |
1161 static const DicomTag DICOM_TAG_SEQUENCE_ITEM(0xfffe, 0xe000); | |
1162 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_ITEM(0xfffe, 0xe00d); | |
1163 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE(0xfffe, 0xe0dd); | |
1164 | |
1165 assert(block.size() == 8u); | |
1166 | |
1167 const bool littleEndian = IsLittleEndian(); | |
1168 DicomTag tag = ReadTag(block.c_str(), littleEndian); | |
1169 | |
1170 if (sequenceDepth_ == 0 && | |
1171 tag >= untilTag) | |
1172 { | |
1173 state_ = State_Done; | |
1174 return; | |
1175 } | |
1176 | |
1177 if (tag == DICOM_TAG_SEQUENCE_ITEM || | |
1178 tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM || | |
1179 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE) | |
1180 { | |
1181 //printf("SEQUENCE TAG:\n"); | |
1182 //PrintBlock(block); | |
1183 | |
1184 // The special sequence items are encoded like "Implicit VR" | |
1185 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, littleEndian); | |
1186 | |
1187 if (tag == DICOM_TAG_SEQUENCE_ITEM) | |
1188 { | |
1189 for (unsigned int i = 0; i <= sequenceDepth_; i++) | |
1190 printf(" "); | |
1191 if (length == 0xffffffffu) | |
1192 { | |
1193 // Undefined length: Need to loop over the tags of the nested dataset | |
1194 printf("...next dataset in sequence...\n"); | |
1195 reader_.Schedule(8); | |
1196 state_ = State_DatasetTag; | |
1197 } | |
1198 else | |
1199 { | |
1200 // Explicit length: Can skip the full sequence at once | |
1201 printf("...next dataset in sequence... %u bytes\n", length); | |
1202 reader_.Schedule(length); | |
1203 state_ = State_DatasetValue; | |
1204 } | |
1205 } | |
1206 else if (tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM || | |
1207 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE) | |
1208 { | |
1209 if (length != 0 || | |
1210 sequenceDepth_ == 0) | |
1211 { | |
1212 throw OrthancException(ErrorCode_BadFileFormat); | |
1213 } | |
1214 | |
1215 if (tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE) | |
1216 { | |
1217 for (unsigned int i = 0; i < sequenceDepth_; i++) | |
1218 printf(" "); | |
1219 printf("...leaving sequence...\n"); | |
1220 | |
1221 sequenceDepth_ --; | |
1222 } | |
1223 else | |
1224 { | |
1225 if (sequenceDepth_ == 0) | |
1226 { | |
1227 throw OrthancException(ErrorCode_BadFileFormat); | |
1228 } | |
1229 } | |
1230 | |
1231 reader_.Schedule(8); | |
1232 state_ = State_DatasetTag; | |
1233 } | |
1234 else | |
1235 { | |
1236 throw OrthancException(ErrorCode_InternalError); | |
1237 } | |
1238 } | |
1239 else | |
1240 { | |
1241 //printf("DATASET TAG:\n"); | |
1242 //PrintBlock(block); | |
1243 | |
1244 previousTag_ = tag; | |
1245 | |
1246 ValueRepresentation vr = ValueRepresentation_Unknown; | |
1247 | |
1248 if (transferSyntax_ == DicomTransferSyntax_LittleEndianImplicit) | |
1249 { | |
1250 if (sequenceDepth_ == 0) | |
1251 { | |
1252 danglingTag_ = tag; | |
1253 danglingVR_ = vr; | |
1254 } | |
1255 | |
1256 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, true /* little endian */); | |
1257 HandleDatasetExplicitLength(length); | |
1258 } | |
1259 else | |
1260 { | |
1261 // This in an explicit transfer syntax | |
1262 | |
1263 vr = StringToValueRepresentation( | |
1264 std::string(block.c_str() + 4, 2), false /* ignore unknown VR */); | |
1265 | |
1266 if (vr != ValueRepresentation_Sequence && | |
1267 sequenceDepth_ > 0) | |
1268 { | |
1269 for (unsigned int i = 0; i <= sequenceDepth_; i++) | |
1270 printf(" "); | |
1271 printf("%s\n", tag.Format().c_str()); | |
1272 } | |
1273 | |
1274 if (vr == ValueRepresentation_Sequence) | |
1275 { | |
1276 for (unsigned int i = 0; i <= sequenceDepth_; i++) | |
1277 printf(" "); | |
1278 printf("...entering sequence... %s\n", tag.Format().c_str()); | |
1279 sequenceDepth_ ++; | |
1280 reader_.Schedule(4); | |
1281 state_ = State_SequenceExplicitLength; | |
1282 } | |
1283 else if (IsShortExplicitTag(vr)) | |
1284 { | |
1285 uint16_t length = ReadUnsignedInteger16(block.c_str() + 6, littleEndian); | |
1286 | |
1287 reader_.Schedule(length); | |
1288 state_ = State_DatasetValue; | |
1289 } | |
1290 else | |
1291 { | |
1292 uint16_t reserved = ReadUnsignedInteger16(block.c_str() + 6, littleEndian); | |
1293 if (reserved != 0) | |
1294 { | |
1295 throw OrthancException(ErrorCode_BadFileFormat); | |
1296 } | |
1297 | |
1298 reader_.Schedule(4); | |
1299 state_ = State_DatasetExplicitLength; | |
1300 } | |
1301 | |
1302 if (sequenceDepth_ == 0) | |
1303 { | |
1304 danglingTag_ = tag; | |
1305 danglingVR_ = vr; | |
1306 } | |
1307 } | |
1308 } | |
1309 } | |
1310 | |
1311 | |
1312 void HandleDatasetExplicitLength(uint32_t length) | |
1313 { | |
1314 if (length == 0xffffffffu) | |
1315 { | |
1316 /** | |
1317 * This is the case of pixel data with compressed transfer | |
1318 * syntaxes. Schedule the reading of the first tag of the | |
1319 * nested dataset. | |
1320 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.html | |
1321 **/ | |
1322 | |
1323 for (unsigned int i = 0; i <= sequenceDepth_; i++) | |
1324 printf(" "); | |
1325 printf("...entering sequence... %s\n", previousTag_.Format().c_str()); | |
1326 | |
1327 state_ = State_DatasetTag; | |
1328 reader_.Schedule(8); | |
1329 sequenceDepth_ ++; | |
1330 } | |
1331 else | |
1332 { | |
1333 reader_.Schedule(length); | |
1334 state_ = State_DatasetValue; | |
1335 } | |
1336 } | |
1337 | |
1338 void HandleDatasetExplicitLength(const std::string& block) | |
1339 { | |
1340 //printf("DATASET TAG LENGTH:\n"); | |
1341 //PrintBlock(block); | |
1342 | |
1343 assert(block.size() == 4); | |
1344 | |
1345 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian()); | |
1346 HandleDatasetExplicitLength(length); | |
1347 } | |
1348 | |
1349 void HandleSequenceExplicitLength(const std::string& block) | |
1350 { | |
1351 //printf("DATASET TAG LENGTH:\n"); | |
1352 //PrintBlock(block); | |
1353 | |
1354 assert(block.size() == 4); | |
1355 | |
1356 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian()); | |
1357 if (length == 0xffffffffu) | |
1358 { | |
1359 state_ = State_DatasetTag; | |
1360 reader_.Schedule(8); | |
1361 } | |
1362 else | |
1363 { | |
1364 for (unsigned int i = 0; i <= sequenceDepth_; i++) | |
1365 printf(" "); | |
1366 printf("...skipping sequence thanks to explicit length... %d\n", length); | |
1367 | |
1368 reader_.Schedule(length); | |
1369 state_ = State_SequenceExplicitValue; | |
1370 } | |
1371 } | |
1372 | |
1373 void HandleSequenceExplicitValue() | |
1374 { | |
1375 if (sequenceDepth_ == 0) | |
1376 { | |
1377 throw OrthancException(ErrorCode_InternalError); | |
1378 } | |
1379 | |
1380 sequenceDepth_ --; | |
1381 | |
1382 state_ = State_DatasetTag; | |
1383 reader_.Schedule(8); | |
1384 } | |
1385 | |
1386 | |
1387 void HandleDatasetValue(IVisitor& visitor, | |
1388 const std::string& block) | |
1389 { | |
1390 if (sequenceDepth_ == 0) | |
1391 { | |
1392 bool c; | |
1393 | |
1394 if (!block.empty() && | |
1395 (block[block.size() - 1] == ' ' || | |
1396 block[block.size() - 1] == '\0') && | |
1397 (danglingVR_ == ValueRepresentation_ApplicationEntity || | |
1398 danglingVR_ == ValueRepresentation_AgeString || | |
1399 danglingVR_ == ValueRepresentation_CodeString || | |
1400 danglingVR_ == ValueRepresentation_DecimalString || | |
1401 danglingVR_ == ValueRepresentation_IntegerString || | |
1402 danglingVR_ == ValueRepresentation_LongString || | |
1403 danglingVR_ == ValueRepresentation_LongText || | |
1404 danglingVR_ == ValueRepresentation_PersonName || | |
1405 danglingVR_ == ValueRepresentation_ShortString || | |
1406 danglingVR_ == ValueRepresentation_ShortText || | |
1407 danglingVR_ == ValueRepresentation_UniqueIdentifier || | |
1408 danglingVR_ == ValueRepresentation_UnlimitedText)) | |
1409 { | |
1410 std::string s(block.begin(), block.end() - 1); | |
1411 c = visitor.VisitDatasetTag(danglingTag_, danglingVR_, transferSyntax_, s, IsLittleEndian()); | |
1412 } | |
1413 else | |
1414 { | |
1415 c = visitor.VisitDatasetTag(danglingTag_, danglingVR_, transferSyntax_, block, IsLittleEndian()); | |
1416 } | |
1417 | |
1418 if (!c) | |
1419 { | |
1420 state_ = State_Done; | |
1421 return; | |
1422 } | |
1423 } | |
1424 | |
1425 reader_.Schedule(8); | |
1426 state_ = State_DatasetTag; | |
1427 } | |
1428 | |
1429 | |
1430 public: | |
1431 DicomStreamReader(std::istream& stream) : | |
1432 reader_(stream), | |
1433 state_(State_Preamble), | |
1434 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit), // Dummy | |
1435 previousTag_(0x0000, 0x0000), // Dummy | |
1436 danglingTag_(0x0000, 0x0000), // Dummy | |
1437 danglingVR_(ValueRepresentation_Unknown), // Dummy | |
1438 sequenceDepth_(0) | |
1439 { | |
1440 reader_.Schedule(128 /* empty header */ + | |
1441 4 /* "DICM" magic value */ + | |
1442 4 /* (0x0002, 0x0000) tag */ + | |
1443 2 /* value representation of (0x0002, 0x0000) == "UL" */ + | |
1444 2 /* length of "UL" value == 4 */ + | |
1445 4 /* actual length of the meta-header */); | |
1446 } | |
1447 | |
1448 void Consume(IVisitor& visitor, | |
1449 const DicomTag& untilTag) | |
1450 { | |
1451 while (state_ != State_Done) | |
1452 { | |
1453 std::string block; | |
1454 if (reader_.Read(block)) | |
1455 { | |
1456 switch (state_) | |
1457 { | |
1458 case State_Preamble: | |
1459 HandlePreamble(visitor, block); | |
1460 break; | |
1461 | |
1462 case State_MetaHeader: | |
1463 HandleMetaHeader(visitor, block); | |
1464 break; | |
1465 | |
1466 case State_DatasetTag: | |
1467 HandleDatasetTag(block, untilTag); | |
1468 break; | |
1469 | |
1470 case State_DatasetExplicitLength: | |
1471 HandleDatasetExplicitLength(block); | |
1472 break; | |
1473 | |
1474 case State_SequenceExplicitLength: | |
1475 HandleSequenceExplicitLength(block); | |
1476 break; | |
1477 | |
1478 case State_SequenceExplicitValue: | |
1479 HandleSequenceExplicitValue(); | |
1480 break; | |
1481 | |
1482 case State_DatasetValue: | |
1483 HandleDatasetValue(visitor, block); | |
1484 break; | |
1485 | |
1486 default: | |
1487 throw OrthancException(ErrorCode_InternalError); | |
1488 } | |
1489 } | |
1490 else | |
1491 { | |
1492 return; // No more data in the stream | |
1493 } | |
1494 } | |
1495 } | |
1496 | |
1497 void Consume(IVisitor& visitor) | |
1498 { | |
1499 DicomTag untilTag(0xffff, 0xffff); | |
1500 Consume(visitor, untilTag); | |
1501 } | |
1502 | |
1503 bool IsDone() const | |
1504 { | |
1505 return (state_ == State_Done); | |
1506 } | |
1507 | |
1508 uint64_t GetProcessedBytes() const | |
1509 { | |
1510 return reader_.GetProcessedBytes(); | |
1511 } | |
1512 }; | |
1513 | |
1514 | |
1515 | |
1516 class V : public DicomStreamReader::IVisitor | 802 class V : public DicomStreamReader::IVisitor |
1517 { | 803 { |
1518 private: | 804 private: |
1519 DicomMap map_; | 805 DicomMap map_; |
1520 | 806 |
1529 const std::string& value) ORTHANC_OVERRIDE | 815 const std::string& value) ORTHANC_OVERRIDE |
1530 { | 816 { |
1531 std::cout << "Header: " << tag.Format() << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl; | 817 std::cout << "Header: " << tag.Format() << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl; |
1532 } | 818 } |
1533 | 819 |
820 virtual void VisitTransferSyntax(DicomTransferSyntax transferSyntax) ORTHANC_OVERRIDE | |
821 { | |
822 printf("TRANSFER SYNTAX: %s\n", GetTransferSyntaxUid(transferSyntax)); | |
823 } | |
824 | |
1534 virtual bool VisitDatasetTag(const DicomTag& tag, | 825 virtual bool VisitDatasetTag(const DicomTag& tag, |
1535 const ValueRepresentation& vr, | 826 const ValueRepresentation& vr, |
1536 DicomTransferSyntax transferSyntax, | |
1537 const std::string& value, | 827 const std::string& value, |
1538 bool isLittleEndian) ORTHANC_OVERRIDE | 828 bool isLittleEndian) ORTHANC_OVERRIDE |
1539 { | 829 { |
1540 if (!isLittleEndian) | 830 if (!isLittleEndian) |
1541 printf("** "); | 831 printf("** "); |
1646 DicomStreamReader r(stream); | 936 DicomStreamReader r(stream); |
1647 V visitor; | 937 V visitor; |
1648 | 938 |
1649 try | 939 try |
1650 { | 940 { |
1651 //r.Consume(visitor, DICOM_TAG_PIXEL_DATA); | 941 r.Consume(visitor, DICOM_TAG_PIXEL_DATA); |
1652 r.Consume(visitor); | 942 //r.Consume(visitor); |
1653 success++; | 943 success++; |
1654 } | 944 } |
1655 catch (OrthancException& e) | 945 catch (OrthancException& e) |
1656 { | 946 { |
1657 errors.insert(path); | 947 errors.insert(path); |