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 {