Mercurial > hg > orthanc-stone
comparison Framework/Toolbox/DicomStructureSet.cpp @ 860:238693c3bc51 am-dev
merge default -> am-dev
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Mon, 24 Jun 2019 14:35:00 +0200 |
parents | 266e2b0b9abc |
children | 32eaf4929b08 |
comparison
equal
deleted
inserted
replaced
856:a6e17a5a39e7 | 860:238693c3bc51 |
---|---|
20 | 20 |
21 | 21 |
22 #include "DicomStructureSet.h" | 22 #include "DicomStructureSet.h" |
23 | 23 |
24 #include "../Toolbox/GeometryToolbox.h" | 24 #include "../Toolbox/GeometryToolbox.h" |
25 #include "../Toolbox/MessagingToolbox.h" | |
26 | 25 |
27 #include <Core/Logging.h> | 26 #include <Core/Logging.h> |
28 #include <Core/OrthancException.h> | 27 #include <Core/OrthancException.h> |
29 #include <Plugins/Samples/Common/FullOrthancDataset.h> | 28 #include <Plugins/Samples/Common/FullOrthancDataset.h> |
30 #include <Plugins/Samples/Common/DicomDatasetReader.h> | 29 #include <Plugins/Samples/Common/DicomDatasetReader.h> |
31 | 30 |
32 #include <limits> | 31 #include <limits> |
33 #include <stdio.h> | 32 #include <stdio.h> |
34 #include <boost/lexical_cast.hpp> | |
35 #include <boost/geometry.hpp> | 33 #include <boost/geometry.hpp> |
36 #include <boost/geometry/geometries/point_xy.hpp> | 34 #include <boost/geometry/geometries/point_xy.hpp> |
37 #include <boost/geometry/geometries/polygon.hpp> | 35 #include <boost/geometry/geometries/polygon.hpp> |
38 #include <boost/geometry/multi/geometries/multi_polygon.hpp> | 36 #include <boost/geometry/multi/geometries/multi_polygon.hpp> |
39 | 37 |
365 } | 363 } |
366 | 364 |
367 | 365 |
368 DicomStructureSet::DicomStructureSet(const OrthancPlugins::FullOrthancDataset& tags) | 366 DicomStructureSet::DicomStructureSet(const OrthancPlugins::FullOrthancDataset& tags) |
369 { | 367 { |
370 using namespace OrthancPlugins; | 368 OrthancPlugins::DicomDatasetReader reader(tags); |
371 | |
372 DicomDatasetReader reader(tags); | |
373 | 369 |
374 size_t count, tmp; | 370 size_t count, tmp; |
375 if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) || | 371 if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) || |
376 !tags.GetSequenceSize(tmp, DICOM_TAG_ROI_CONTOUR_SEQUENCE) || | 372 !tags.GetSequenceSize(tmp, DICOM_TAG_ROI_CONTOUR_SEQUENCE) || |
377 tmp != count || | 373 tmp != count || |
383 | 379 |
384 structures_.resize(count); | 380 structures_.resize(count); |
385 for (size_t i = 0; i < count; i++) | 381 for (size_t i = 0; i < count; i++) |
386 { | 382 { |
387 structures_[i].interpretation_ = reader.GetStringValue | 383 structures_[i].interpretation_ = reader.GetStringValue |
388 (DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i, | 384 (OrthancPlugins::DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i, |
389 DICOM_TAG_RT_ROI_INTERPRETED_TYPE), | 385 DICOM_TAG_RT_ROI_INTERPRETED_TYPE), |
390 "No interpretation"); | 386 "No interpretation"); |
391 | 387 |
392 structures_[i].name_ = reader.GetStringValue | 388 structures_[i].name_ = reader.GetStringValue |
393 (DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, | 389 (OrthancPlugins::DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, |
394 DICOM_TAG_ROI_NAME), | 390 DICOM_TAG_ROI_NAME), |
395 "No interpretation"); | 391 "No interpretation"); |
396 | 392 |
397 Vector color; | 393 Vector color; |
398 if (ParseVector(color, tags, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | 394 if (ParseVector(color, tags, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, |
399 DICOM_TAG_ROI_DISPLAY_COLOR)) && | 395 DICOM_TAG_ROI_DISPLAY_COLOR)) && |
400 color.size() == 3) | 396 color.size() == 3) |
401 { | 397 { |
402 structures_[i].red_ = ConvertColor(color[0]); | 398 structures_[i].red_ = ConvertColor(color[0]); |
403 structures_[i].green_ = ConvertColor(color[1]); | 399 structures_[i].green_ = ConvertColor(color[1]); |
404 structures_[i].blue_ = ConvertColor(color[2]); | 400 structures_[i].blue_ = ConvertColor(color[2]); |
409 structures_[i].green_ = 0; | 405 structures_[i].green_ = 0; |
410 structures_[i].blue_ = 0; | 406 structures_[i].blue_ = 0; |
411 } | 407 } |
412 | 408 |
413 size_t countSlices; | 409 size_t countSlices; |
414 if (!tags.GetSequenceSize(countSlices, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | 410 if (!tags.GetSequenceSize(countSlices, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, |
415 DICOM_TAG_CONTOUR_SEQUENCE))) | 411 DICOM_TAG_CONTOUR_SEQUENCE))) |
416 { | 412 { |
417 countSlices = 0; | 413 countSlices = 0; |
418 } | 414 } |
419 | 415 |
420 LOG(WARNING) << "New RT structure: \"" << structures_[i].name_ | 416 LOG(INFO) << "New RT structure: \"" << structures_[i].name_ |
421 << "\" with interpretation \"" << structures_[i].interpretation_ | 417 << "\" with interpretation \"" << structures_[i].interpretation_ |
422 << "\" containing " << countSlices << " slices (color: " | 418 << "\" containing " << countSlices << " slices (color: " |
423 << static_cast<int>(structures_[i].red_) << "," | 419 << static_cast<int>(structures_[i].red_) << "," |
424 << static_cast<int>(structures_[i].green_) << "," | 420 << static_cast<int>(structures_[i].green_) << "," |
425 << static_cast<int>(structures_[i].blue_) << ")"; | 421 << static_cast<int>(structures_[i].blue_) << ")"; |
426 | 422 |
423 // These temporary variables avoid allocating many vectors in the loop below | |
424 OrthancPlugins::DicomPath countPointsPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
425 DICOM_TAG_CONTOUR_SEQUENCE, 0, | |
426 DICOM_TAG_NUMBER_OF_CONTOUR_POINTS); | |
427 | |
428 OrthancPlugins::DicomPath geometricTypePath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
429 DICOM_TAG_CONTOUR_SEQUENCE, 0, | |
430 DICOM_TAG_CONTOUR_GEOMETRIC_TYPE); | |
431 | |
432 OrthancPlugins::DicomPath imageSequencePath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
433 DICOM_TAG_CONTOUR_SEQUENCE, 0, | |
434 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE); | |
435 | |
436 OrthancPlugins::DicomPath referencedInstancePath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
437 DICOM_TAG_CONTOUR_SEQUENCE, 0, | |
438 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0, | |
439 DICOM_TAG_REFERENCED_SOP_INSTANCE_UID); | |
440 | |
441 OrthancPlugins::DicomPath contourDataPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
442 DICOM_TAG_CONTOUR_SEQUENCE, 0, | |
443 DICOM_TAG_CONTOUR_DATA); | |
444 | |
427 for (size_t j = 0; j < countSlices; j++) | 445 for (size_t j = 0; j < countSlices; j++) |
428 { | 446 { |
429 unsigned int countPoints; | 447 unsigned int countPoints; |
430 | 448 |
431 if (!reader.GetUnsignedIntegerValue | 449 countPointsPath.SetPrefixIndex(1, j); |
432 (countPoints, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | 450 if (!reader.GetUnsignedIntegerValue(countPoints, countPointsPath)) |
433 DICOM_TAG_CONTOUR_SEQUENCE, j, | |
434 DICOM_TAG_NUMBER_OF_CONTOUR_POINTS))) | |
435 { | 451 { |
436 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | 452 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); |
437 } | 453 } |
438 | 454 |
439 //LOG(INFO) << "Parsing slice containing " << countPoints << " vertices"; | 455 //LOG(INFO) << "Parsing slice containing " << countPoints << " vertices"; |
440 | 456 |
441 std::string type = reader.GetMandatoryStringValue | 457 geometricTypePath.SetPrefixIndex(1, j); |
442 (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | 458 std::string type = reader.GetMandatoryStringValue(geometricTypePath); |
443 DICOM_TAG_CONTOUR_SEQUENCE, j, | |
444 DICOM_TAG_CONTOUR_GEOMETRIC_TYPE)); | |
445 if (type != "CLOSED_PLANAR") | 459 if (type != "CLOSED_PLANAR") |
446 { | 460 { |
447 LOG(WARNING) << "Ignoring contour with geometry type: " << type; | 461 LOG(WARNING) << "Ignoring contour with geometry type: " << type; |
448 continue; | 462 continue; |
449 } | 463 } |
450 | 464 |
451 size_t size; | 465 size_t size; |
452 if (!tags.GetSequenceSize(size, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | 466 |
453 DICOM_TAG_CONTOUR_SEQUENCE, j, | 467 imageSequencePath.SetPrefixIndex(1, j); |
454 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) || | 468 if (!tags.GetSequenceSize(size, imageSequencePath) || |
455 size != 1) | 469 size != 1) |
456 { | 470 { |
457 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | 471 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
458 } | 472 } |
459 | 473 |
460 std::string sopInstanceUid = reader.GetMandatoryStringValue | 474 referencedInstancePath.SetPrefixIndex(1, j); |
461 (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | 475 std::string sopInstanceUid = reader.GetMandatoryStringValue(referencedInstancePath); |
462 DICOM_TAG_CONTOUR_SEQUENCE, j, | 476 |
463 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0, | 477 contourDataPath.SetPrefixIndex(1, j); |
464 DICOM_TAG_REFERENCED_SOP_INSTANCE_UID)); | 478 std::string slicesData = reader.GetMandatoryStringValue(contourDataPath); |
465 | |
466 std::string slicesData = reader.GetMandatoryStringValue | |
467 (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
468 DICOM_TAG_CONTOUR_SEQUENCE, j, | |
469 DICOM_TAG_CONTOUR_DATA)); | |
470 | 479 |
471 Vector points; | 480 Vector points; |
472 if (!LinearAlgebra::ParseVector(points, slicesData) || | 481 if (!LinearAlgebra::ParseVector(points, slicesData) || |
473 points.size() != 3 * countPoints) | 482 points.size() != 3 * countPoints) |
474 { | 483 { |
529 { | 538 { |
530 return GetStructure(index).interpretation_; | 539 return GetStructure(index).interpretation_; |
531 } | 540 } |
532 | 541 |
533 | 542 |
543 Color DicomStructureSet::GetStructureColor(size_t index) const | |
544 { | |
545 const Structure& s = GetStructure(index); | |
546 return Color(s.red_, s.green_, s.blue_); | |
547 } | |
548 | |
549 | |
534 void DicomStructureSet::GetStructureColor(uint8_t& red, | 550 void DicomStructureSet::GetStructureColor(uint8_t& red, |
535 uint8_t& green, | 551 uint8_t& green, |
536 uint8_t& blue, | 552 uint8_t& blue, |
537 size_t index) const | 553 size_t index) const |
538 { | 554 { |
666 return referencedSlices_.begin()->second.geometry_.GetNormal(); | 682 return referencedSlices_.begin()->second.geometry_.GetNormal(); |
667 } | 683 } |
668 } | 684 } |
669 | 685 |
670 | 686 |
671 DicomStructureSet* DicomStructureSet::SynchronousLoad(OrthancPlugins::IOrthancConnection& orthanc, | |
672 const std::string& instanceId) | |
673 { | |
674 const std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3006-0050"; | |
675 OrthancPlugins::FullOrthancDataset dataset(orthanc, uri); | |
676 | |
677 std::auto_ptr<DicomStructureSet> result(new DicomStructureSet(dataset)); | |
678 | |
679 std::set<std::string> instances; | |
680 result->GetReferencedInstances(instances); | |
681 | |
682 for (std::set<std::string>::const_iterator it = instances.begin(); | |
683 it != instances.end(); ++it) | |
684 { | |
685 Json::Value lookup; | |
686 MessagingToolbox::RestApiPost(lookup, orthanc, "/tools/lookup", *it); | |
687 | |
688 if (lookup.type() != Json::arrayValue || | |
689 lookup.size() != 1 || | |
690 !lookup[0].isMember("Type") || | |
691 !lookup[0].isMember("Path") || | |
692 lookup[0]["Type"].type() != Json::stringValue || | |
693 lookup[0]["ID"].type() != Json::stringValue || | |
694 lookup[0]["Type"].asString() != "Instance") | |
695 { | |
696 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
697 } | |
698 | |
699 OrthancPlugins::FullOrthancDataset slice | |
700 (orthanc, "/instances/" + lookup[0]["ID"].asString() + "/tags"); | |
701 Orthanc::DicomMap m; | |
702 MessagingToolbox::ConvertDataset(m, slice); | |
703 result->AddReferencedSlice(m); | |
704 } | |
705 | |
706 result->CheckReferencedSlices(); | |
707 | |
708 return result.release(); | |
709 } | |
710 | |
711 | |
712 bool DicomStructureSet::ProjectStructure(std::vector< std::vector<PolygonPoint> >& polygons, | 687 bool DicomStructureSet::ProjectStructure(std::vector< std::vector<PolygonPoint> >& polygons, |
713 Structure& structure, | 688 const Structure& structure, |
714 const CoordinateSystem3D& slice) | 689 const CoordinateSystem3D& slice) const |
715 { | 690 { |
716 polygons.clear(); | 691 polygons.clear(); |
717 | 692 |
718 Vector normal = GetNormal(); | 693 Vector normal = GetNormal(); |
719 | 694 |
720 bool isOpposite; | 695 bool isOpposite; |
721 if (GeometryToolbox::IsParallelOrOpposite(isOpposite, normal, slice.GetNormal())) | 696 if (GeometryToolbox::IsParallelOrOpposite(isOpposite, normal, slice.GetNormal())) |
722 { | 697 { |
723 // This is an axial projection | 698 // This is an axial projection |
724 | 699 |
725 for (Polygons::iterator polygon = structure.polygons_.begin(); | 700 for (Polygons::const_iterator polygon = structure.polygons_.begin(); |
726 polygon != structure.polygons_.end(); ++polygon) | 701 polygon != structure.polygons_.end(); ++polygon) |
727 { | 702 { |
728 if (polygon->IsOnSlice(slice)) | 703 if (polygon->IsOnSlice(slice)) |
729 { | 704 { |
730 polygons.push_back(std::vector<PolygonPoint>()); | 705 polygons.push_back(std::vector<PolygonPoint>()); |
746 { | 721 { |
747 #if 1 | 722 #if 1 |
748 // Sagittal or coronal projection | 723 // Sagittal or coronal projection |
749 std::vector<BoostPolygon> projected; | 724 std::vector<BoostPolygon> projected; |
750 | 725 |
751 for (Polygons::iterator polygon = structure.polygons_.begin(); | 726 for (Polygons::const_iterator polygon = structure.polygons_.begin(); |
752 polygon != structure.polygons_.end(); ++polygon) | 727 polygon != structure.polygons_.end(); ++polygon) |
753 { | 728 { |
754 double x1, y1, x2, y2; | 729 double x1, y1, x2, y2; |
755 if (polygon->Project(x1, y1, x2, y2, slice)) | 730 if (polygon->Project(x1, y1, x2, y2, slice)) |
756 { | 731 { |