comparison OrthancFramework/UnitTestsSources/DicomMapTests.cpp @ 4218:8e069a7e1c11

validation against orthanc-tests
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 30 Sep 2020 11:49:38 +0200
parents 78c8b4a18619
children b8ed2852a35d
comparison
equal deleted inserted replaced
4217:78c8b4a18619 4218:8e069a7e1c11
651 } 651 }
652 } 652 }
653 653
654 654
655 655
656 TEST(DicomTag, Comparisons)
657 {
658 DicomTag a(0x0000, 0x0000);
659 DicomTag b(0x0010, 0x0010);
660 DicomTag c(0x0010, 0x0020);
661 DicomTag d(0x0020, 0x0000);
662
663 // operator==()
664 ASSERT_TRUE(a == a);
665 ASSERT_FALSE(a == b);
666
667 // operator!=()
668 ASSERT_FALSE(a != a);
669 ASSERT_TRUE(a != b);
670
671 // operator<=()
672 ASSERT_TRUE(a <= a);
673 ASSERT_TRUE(a <= b);
674 ASSERT_TRUE(a <= c);
675 ASSERT_TRUE(a <= d);
676
677 ASSERT_FALSE(b <= a);
678 ASSERT_TRUE(b <= b);
679 ASSERT_TRUE(b <= c);
680 ASSERT_TRUE(b <= d);
681
682 ASSERT_FALSE(c <= a);
683 ASSERT_FALSE(c <= b);
684 ASSERT_TRUE(c <= c);
685 ASSERT_TRUE(c <= d);
686
687 ASSERT_FALSE(d <= a);
688 ASSERT_FALSE(d <= b);
689 ASSERT_FALSE(d <= c);
690 ASSERT_TRUE(d <= d);
691
692 // operator<()
693 ASSERT_FALSE(a < a);
694 ASSERT_TRUE(a < b);
695 ASSERT_TRUE(a < c);
696 ASSERT_TRUE(a < d);
697
698 ASSERT_FALSE(b < a);
699 ASSERT_FALSE(b < b);
700 ASSERT_TRUE(b < c);
701 ASSERT_TRUE(b < d);
702
703 ASSERT_FALSE(c < a);
704 ASSERT_FALSE(c < b);
705 ASSERT_FALSE(c < c);
706 ASSERT_TRUE(c < d);
707
708 ASSERT_FALSE(d < a);
709 ASSERT_FALSE(d < b);
710 ASSERT_FALSE(d < c);
711 ASSERT_FALSE(d < d);
712
713 // operator>=()
714 ASSERT_TRUE(a >= a);
715 ASSERT_FALSE(a >= b);
716 ASSERT_FALSE(a >= c);
717 ASSERT_FALSE(a >= d);
718
719 ASSERT_TRUE(b >= a);
720 ASSERT_TRUE(b >= b);
721 ASSERT_FALSE(b >= c);
722 ASSERT_FALSE(b >= d);
723
724 ASSERT_TRUE(c >= a);
725 ASSERT_TRUE(c >= b);
726 ASSERT_TRUE(c >= c);
727 ASSERT_FALSE(c >= d);
728
729 ASSERT_TRUE(d >= a);
730 ASSERT_TRUE(d >= b);
731 ASSERT_TRUE(d >= c);
732 ASSERT_TRUE(d >= d);
733
734 // operator>()
735 ASSERT_FALSE(a > a);
736 ASSERT_FALSE(a > b);
737 ASSERT_FALSE(a > c);
738 ASSERT_FALSE(a > d);
739
740 ASSERT_TRUE(b > a);
741 ASSERT_FALSE(b > b);
742 ASSERT_FALSE(b > c);
743 ASSERT_FALSE(b > d);
744
745 ASSERT_TRUE(c > a);
746 ASSERT_TRUE(c > b);
747 ASSERT_FALSE(c > c);
748 ASSERT_FALSE(c > d);
749
750 ASSERT_TRUE(d > a);
751 ASSERT_TRUE(d > b);
752 ASSERT_TRUE(d > c);
753 ASSERT_FALSE(d > d);
754 }
755
756
656 757
657 #include "../Sources/SystemToolbox.h" 758 #include "../Sources/SystemToolbox.h"
658 759
659 TEST(DicomMap, DISABLED_ParseDicomMetaInformation) 760 TEST(DicomMap, DISABLED_ParseDicomMetaInformation)
660 { 761 {
740 } 841 }
741 else 842 else
742 { 843 {
743 while (blockPos_ < block_.size()) 844 while (blockPos_ < block_.size())
744 { 845 {
846 #if 0
745 char c; 847 char c;
746 stream_.get(c); 848 stream_.get(c);
747 849
748 if (stream_.good()) 850 if (stream_.good())
749 { 851 {
752 } 854 }
753 else 855 else
754 { 856 {
755 return false; 857 return false;
756 } 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
757 } 871 }
758 872
759 processedBytes_ += block_.size(); 873 processedBytes_ += block_.size();
760 874
761 block.swap(block_); 875 block.swap(block_);
817 }; 931 };
818 932
819 StreamBlockReader reader_; 933 StreamBlockReader reader_;
820 State state_; 934 State state_;
821 DicomTransferSyntax transferSyntax_; 935 DicomTransferSyntax transferSyntax_;
822 DicomTag danglingTag_; 936 DicomTag previousTag_;
937 DicomTag danglingTag_; // Root-level tag
823 ValueRepresentation danglingVR_; 938 ValueRepresentation danglingVR_;
824 unsigned int sequenceDepth_; 939 unsigned int sequenceDepth_;
825 940
826 static uint16_t ReadUnsignedInteger16(const char* dicom, 941 static uint16_t ReadUnsignedInteger16(const char* dicom,
827 bool littleEndian) 942 bool littleEndian)
1038 reader_.Schedule(8); 1153 reader_.Schedule(8);
1039 state_ = State_DatasetTag; 1154 state_ = State_DatasetTag;
1040 } 1155 }
1041 1156
1042 1157
1043 void HandleDatasetTag(const std::string& block) 1158 void HandleDatasetTag(const std::string& block,
1159 const DicomTag& untilTag)
1044 { 1160 {
1045 static const DicomTag DICOM_TAG_SEQUENCE_ITEM(0xfffe, 0xe000); 1161 static const DicomTag DICOM_TAG_SEQUENCE_ITEM(0xfffe, 0xe000);
1046 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_ITEM(0xfffe, 0xe00d); 1162 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_ITEM(0xfffe, 0xe00d);
1047 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE(0xfffe, 0xe0dd); 1163 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE(0xfffe, 0xe0dd);
1048 1164
1049 assert(block.size() == 8u); 1165 assert(block.size() == 8u);
1050 1166
1051 const bool littleEndian = IsLittleEndian(); 1167 const bool littleEndian = IsLittleEndian();
1052 DicomTag tag = ReadTag(block.c_str(), littleEndian); 1168 DicomTag tag = ReadTag(block.c_str(), littleEndian);
1053 1169
1170 if (tag >= untilTag)
1171 {
1172 state_ = State_Done;
1173 return;
1174 }
1175
1054 if (tag == DICOM_TAG_SEQUENCE_ITEM || 1176 if (tag == DICOM_TAG_SEQUENCE_ITEM ||
1055 tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM || 1177 tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM ||
1056 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE) 1178 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
1057 { 1179 {
1058 //printf("SEQUENCE TAG:\n"); 1180 //printf("SEQUENCE TAG:\n");
1116 else 1238 else
1117 { 1239 {
1118 //printf("DATASET TAG:\n"); 1240 //printf("DATASET TAG:\n");
1119 //PrintBlock(block); 1241 //PrintBlock(block);
1120 1242
1243 previousTag_ = tag;
1244
1121 ValueRepresentation vr = ValueRepresentation_Unknown; 1245 ValueRepresentation vr = ValueRepresentation_Unknown;
1122 1246
1123 if (transferSyntax_ == DicomTransferSyntax_LittleEndianImplicit) 1247 if (transferSyntax_ == DicomTransferSyntax_LittleEndianImplicit)
1124 { 1248 {
1249 if (sequenceDepth_ == 0)
1250 {
1251 danglingTag_ = tag;
1252 danglingVR_ = vr;
1253 }
1254
1125 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, true /* little endian */); 1255 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, true /* little endian */);
1126 1256 HandleDatasetExplicitLength(length);
1127 reader_.Schedule(length);
1128 state_ = State_DatasetValue;
1129 } 1257 }
1130 else 1258 else
1131 { 1259 {
1132 // This in an explicit transfer syntax 1260 // This in an explicit transfer syntax
1133 1261
1167 } 1295 }
1168 1296
1169 reader_.Schedule(4); 1297 reader_.Schedule(4);
1170 state_ = State_DatasetExplicitLength; 1298 state_ = State_DatasetExplicitLength;
1171 } 1299 }
1300
1301 if (sequenceDepth_ == 0)
1302 {
1303 danglingTag_ = tag;
1304 danglingVR_ = vr;
1305 }
1172 } 1306 }
1173 1307 }
1174 if (sequenceDepth_ == 0) 1308 }
1175 { 1309
1176 danglingTag_ = tag; 1310
1177 danglingVR_ = vr; 1311 void HandleDatasetExplicitLength(uint32_t length)
1178 } 1312 {
1179 }
1180 }
1181
1182
1183 void HandleDatasetExplicitLength(const std::string& block)
1184 {
1185 //printf("DATASET TAG LENGTH:\n");
1186 //PrintBlock(block);
1187
1188 assert(block.size() == 4);
1189
1190 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian());
1191
1192 if (length == 0xffffffffu) 1313 if (length == 0xffffffffu)
1193 { 1314 {
1194 /** 1315 /**
1195 * This is the case of pixel data with compressed transfer 1316 * This is the case of pixel data with compressed transfer
1196 * syntaxes. Schedule the reading of the first tag of the 1317 * syntaxes. Schedule the reading of the first tag of the
1197 * nested dataset. 1318 * nested dataset.
1198 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.html 1319 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.html
1199 **/ 1320 **/
1200 1321
1201 if (sequenceDepth_ != 0) 1322 for (unsigned int i = 0; i <= sequenceDepth_; i++)
1202 { 1323 printf(" ");
1203 throw OrthancException(ErrorCode_BadFileFormat); 1324 printf("...entering sequence... %s\n", previousTag_.Format().c_str());
1204 }
1205 1325
1206 printf(" ...entering sequence... %s\n", danglingTag_.Format().c_str());
1207 state_ = State_DatasetTag; 1326 state_ = State_DatasetTag;
1208 reader_.Schedule(8); 1327 reader_.Schedule(8);
1209 sequenceDepth_ ++; 1328 sequenceDepth_ ++;
1210 } 1329 }
1211 else 1330 else
1212 { 1331 {
1213 reader_.Schedule(length); 1332 reader_.Schedule(length);
1214 state_ = State_DatasetValue; 1333 state_ = State_DatasetValue;
1215 } 1334 }
1216 } 1335 }
1217 1336
1218 1337 void HandleDatasetExplicitLength(const std::string& block)
1338 {
1339 //printf("DATASET TAG LENGTH:\n");
1340 //PrintBlock(block);
1341
1342 assert(block.size() == 4);
1343
1344 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian());
1345 HandleDatasetExplicitLength(length);
1346 }
1347
1219 void HandleSequenceExplicitLength(const std::string& block) 1348 void HandleSequenceExplicitLength(const std::string& block)
1220 { 1349 {
1221 //printf("DATASET TAG LENGTH:\n"); 1350 //printf("DATASET TAG LENGTH:\n");
1222 //PrintBlock(block); 1351 //PrintBlock(block);
1223 1352
1300 public: 1429 public:
1301 DicomStreamReader(std::istream& stream) : 1430 DicomStreamReader(std::istream& stream) :
1302 reader_(stream), 1431 reader_(stream),
1303 state_(State_Preamble), 1432 state_(State_Preamble),
1304 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit), // Dummy 1433 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit), // Dummy
1434 previousTag_(0x0000, 0x0000), // Dummy
1305 danglingTag_(0x0000, 0x0000), // Dummy 1435 danglingTag_(0x0000, 0x0000), // Dummy
1306 danglingVR_(ValueRepresentation_Unknown), // Dummy 1436 danglingVR_(ValueRepresentation_Unknown), // Dummy
1307 sequenceDepth_(0) 1437 sequenceDepth_(0)
1308 { 1438 {
1309 reader_.Schedule(128 /* empty header */ + 1439 reader_.Schedule(128 /* empty header */ +
1312 2 /* value representation of (0x0002, 0x0000) == "UL" */ + 1442 2 /* value representation of (0x0002, 0x0000) == "UL" */ +
1313 2 /* length of "UL" value == 4 */ + 1443 2 /* length of "UL" value == 4 */ +
1314 4 /* actual length of the meta-header */); 1444 4 /* actual length of the meta-header */);
1315 } 1445 }
1316 1446
1317 void Consume(IVisitor& visitor) 1447 void Consume(IVisitor& visitor,
1448 const DicomTag& untilTag)
1318 { 1449 {
1319 while (state_ != State_Done) 1450 while (state_ != State_Done)
1320 { 1451 {
1321 std::string block; 1452 std::string block;
1322 if (reader_.Read(block)) 1453 if (reader_.Read(block))
1330 case State_MetaHeader: 1461 case State_MetaHeader:
1331 HandleMetaHeader(visitor, block); 1462 HandleMetaHeader(visitor, block);
1332 break; 1463 break;
1333 1464
1334 case State_DatasetTag: 1465 case State_DatasetTag:
1335 HandleDatasetTag(block); 1466 HandleDatasetTag(block, untilTag);
1336 break; 1467 break;
1337 1468
1338 case State_DatasetExplicitLength: 1469 case State_DatasetExplicitLength:
1339 HandleDatasetExplicitLength(block); 1470 HandleDatasetExplicitLength(block);
1340 break; 1471 break;
1360 return; // No more data in the stream 1491 return; // No more data in the stream
1361 } 1492 }
1362 } 1493 }
1363 } 1494 }
1364 1495
1496 void Consume(IVisitor& visitor)
1497 {
1498 DicomTag untilTag(0xffff, 0xffff);
1499 Consume(visitor, untilTag);
1500 }
1501
1365 bool IsDone() const 1502 bool IsDone() const
1366 { 1503 {
1367 return (state_ == State_Done); 1504 return (state_ == State_Done);
1368 } 1505 }
1369 1506
1375 1512
1376 1513
1377 1514
1378 class V : public DicomStreamReader::IVisitor 1515 class V : public DicomStreamReader::IVisitor
1379 { 1516 {
1517 private:
1518 DicomMap map_;
1519
1380 public: 1520 public:
1521 const DicomMap& GetDicomMap() const
1522 {
1523 return map_;
1524 }
1525
1381 virtual void VisitMetaHeaderTag(const DicomTag& tag, 1526 virtual void VisitMetaHeaderTag(const DicomTag& tag,
1382 const ValueRepresentation& vr, 1527 const ValueRepresentation& vr,
1383 const std::string& value) ORTHANC_OVERRIDE 1528 const std::string& value) ORTHANC_OVERRIDE
1384 { 1529 {
1385 std::cout << "Header: " << tag.Format() << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl; 1530 std::cout << "Header: " << tag.Format() << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl;
1392 bool isLittleEndian) ORTHANC_OVERRIDE 1537 bool isLittleEndian) ORTHANC_OVERRIDE
1393 { 1538 {
1394 if (!isLittleEndian) 1539 if (!isLittleEndian)
1395 printf("** "); 1540 printf("** ");
1396 if (tag.GetGroup() < 0x7f00) 1541 if (tag.GetGroup() < 0x7f00)
1542 {
1397 std::cout << "Dataset: " << tag.Format() << " " << EnumerationToString(vr) 1543 std::cout << "Dataset: " << tag.Format() << " " << EnumerationToString(vr)
1398 << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl; 1544 << " [" << Toolbox::ConvertToAscii(value).c_str() << "] (" << value.size() << ")" << std::endl;
1545 }
1399 else 1546 else
1547 {
1400 std::cout << "Dataset: " << tag.Format() << " " << EnumerationToString(vr) 1548 std::cout << "Dataset: " << tag.Format() << " " << EnumerationToString(vr)
1401 << " [PIXEL] (" << value.size() << ")" << std::endl; 1549 << " [PIXEL] (" << value.size() << ")" << std::endl;
1402 1550 }
1551
1552 map_.SetValue(tag, value, Toolbox::IsAsciiString(value));
1553
1403 return true; 1554 return true;
1404 } 1555 }
1405 }; 1556 };
1406 } 1557 }
1407 1558
1448 1599
1449 TEST(DicomStreamReader, DISABLED_Tutu2) 1600 TEST(DicomStreamReader, DISABLED_Tutu2)
1450 { 1601 {
1451 static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/"; 1602 static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/";
1452 1603
1453 const std::string path = PATH + "1.2.840.10008.1.2.4.50.dcm"; 1604 //const std::string path = PATH + "1.2.840.10008.1.2.4.50.dcm";
1454 //const std::string path = PATH + "1.2.840.10008.1.2.2.dcm"; 1605 //const std::string path = PATH + "1.2.840.10008.1.2.2.dcm";
1455 1606 const std::string path = "/home/jodogne/Subversion/orthanc-tests/Database/HierarchicalAnonymization/RTH/RT.dcm";
1456 1607
1457 std::ifstream stream(path.c_str()); 1608 std::ifstream stream(path.c_str());
1458 1609
1459 DicomStreamReader r(stream); 1610 DicomStreamReader r(stream);
1460 V visitor; 1611 V visitor;
1461 1612
1462 r.Consume(visitor); 1613 r.Consume(visitor);
1463 1614
1464 printf(">> %d\n", r.GetProcessedBytes()); 1615 printf(">> %d\n", r.GetProcessedBytes());
1465 } 1616 }
1617
1618
1619 #include <boost/filesystem.hpp>
1620
1621 TEST(DicomStreamReader, DISABLED_Tutu3)
1622 {
1623 static const std::string PATH = "/home/jodogne/Subversion/orthanc-tests/Database/";
1624
1625 std::set<std::string> errors;
1626 unsigned int success = 0;
1627
1628 for (boost::filesystem::recursive_directory_iterator current(PATH), end;
1629 current != end ; ++current)
1630 {
1631 if (SystemToolbox::IsRegularFile(current->path().string()))
1632 {
1633 try
1634 {
1635 if (current->path().extension() == ".dcm")
1636 {
1637 const std::string path = current->path().string();
1638 printf("[%s]\n", path.c_str());
1639
1640 DicomMap m1;
1641
1642 {
1643 std::ifstream stream(path.c_str());
1644
1645 DicomStreamReader r(stream);
1646 V visitor;
1647
1648 try
1649 {
1650 //r.Consume(visitor, DICOM_TAG_PIXEL_DATA);
1651 r.Consume(visitor);
1652 success++;
1653 }
1654 catch (OrthancException& e)
1655 {
1656 errors.insert(path);
1657 continue;
1658 }
1659
1660 m1.Assign(visitor.GetDicomMap());
1661 }
1662
1663 m1.SetValue(DICOM_TAG_PIXEL_DATA, "", true);
1664
1665
1666 DicomMap m2;
1667
1668 {
1669 std::string dicom;
1670 SystemToolbox::ReadFile(dicom, path);
1671
1672 ParsedDicomFile f(dicom);
1673 f.ExtractDicomSummary(m2, 256);
1674 }
1675
1676 std::set<DicomTag> tags;
1677 m2.GetTags(tags);
1678
1679 bool first = true;
1680 for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
1681 {
1682 if (!m1.HasTag(*it))
1683 {
1684 if (first)
1685 {
1686 fprintf(stderr, "[%s]\n", path.c_str());
1687 first = false;
1688 }
1689
1690 std::cerr << "ERROR: " << it->Format() << std::endl;
1691 }
1692 else if (!m2.GetValue(*it).IsNull() &&
1693 !m2.GetValue(*it).IsBinary() &&
1694 Toolbox::IsAsciiString(m1.GetValue(*it).GetContent()))
1695 {
1696 const std::string& v1 = m1.GetValue(*it).GetContent();
1697 const std::string& v2 = m2.GetValue(*it).GetContent();
1698
1699 if (v1 != v2 &&
1700 (v1.size() != v2.size() + 1 ||
1701 v1.substr(0, v2.size()) != v2))
1702 {
1703 std::cerr << "ERROR: [" << v1 << "] [" << v2 << "]" << std::endl;
1704 }
1705 }
1706 }
1707 }
1708 }
1709 catch (boost::filesystem::filesystem_error&)
1710 {
1711 }
1712 }
1713 }
1714
1715
1716 printf("\n== ERRORS ==\n");
1717 for (std::set<std::string>::const_iterator
1718 it = errors.begin(); it != errors.end(); ++it)
1719 {
1720 printf("[%s]\n", it->c_str());
1721 }
1722
1723 printf("\n== SUCCESSES: %d ==\n\n", success);
1724 }