Mercurial > hg > orthanc-stone
changeset 2220:532764b7b57f deep-learning
integration mainline->deep-learning
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 22 Apr 2025 18:20:01 +0200 (3 months ago) |
parents | a8066ce6bacc (current diff) a10b7a6ec869 (diff) |
children | |
files | Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp |
diffstat | 6 files changed, 153 insertions(+), 105 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/IStoneWebViewerContext.h Tue Apr 22 14:55:22 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/IStoneWebViewerContext.h Tue Apr 22 18:20:01 2025 +0200 @@ -28,6 +28,15 @@ #include <Images/ImageAccessor.h> +static const int LAYER_TEXTURE = 0; +static const int LAYER_OVERLAY = 1; +static const int LAYER_ORIENTATION_MARKERS = 2; +static const int LAYER_REFERENCE_LINES = 3; +static const int LAYER_ANNOTATIONS_STONE = 5; +static const int LAYER_ANNOTATIONS_OSIRIX = 4; +static const int LAYER_STRUCTURED_REPORT = 6; + + #define DISPATCH_JAVASCRIPT_EVENT(name) \ EM_ASM( \ const customEvent = document.createEvent("CustomEvent"); \
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Apr 22 14:55:22 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Apr 22 18:20:01 2025 +0200 @@ -73,14 +73,6 @@ static const double PI = boost::math::constants::pi<double>(); -static const int LAYER_TEXTURE = 0; -static const int LAYER_OVERLAY = 1; -static const int LAYER_ORIENTATION_MARKERS = 2; -static const int LAYER_REFERENCE_LINES = 3; -static const int LAYER_ANNOTATIONS_STONE = 5; -static const int LAYER_ANNOTATIONS_OSIRIX = 4; -static const int LAYER_STRUCTURED_REPORT = 6; - #if !defined(STONE_WEB_VIEWER_EXPORT) // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm
--- a/OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp Tue Apr 22 14:55:22 2025 +0200 +++ b/OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp Tue Apr 22 18:20:01 2025 +0200 @@ -486,4 +486,24 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } } + + + bool CoordinateSystem3D::Equals(const CoordinateSystem3D& other) const + { + if (!IsValid() && !other.IsValid()) + { + return true; + } + else if (IsValid() && other.IsValid()) + { + // The normal is automatically computed by "CheckAndComputeNormal()" that is called in all the constructors + return (LinearAlgebra::IsCloseToZero(boost::numeric::ublas::norm_2(GetOrigin() - other.GetOrigin())) && + LinearAlgebra::IsCloseToZero(boost::numeric::ublas::norm_2(GetAxisX() - other.GetAxisX())) && + LinearAlgebra::IsCloseToZero(boost::numeric::ublas::norm_2(GetAxisY() - other.GetAxisY()))); + } + else + { + return false; + } + } }
--- a/OrthancStone/Sources/Toolbox/CoordinateSystem3D.h Tue Apr 22 14:55:22 2025 +0200 +++ b/OrthancStone/Sources/Toolbox/CoordinateSystem3D.h Tue Apr 22 18:20:01 2025 +0200 @@ -165,5 +165,7 @@ std::string& bottom /* out */, std::string& left /* out */, std::string& right /* out */) const; + + bool Equals(const CoordinateSystem3D& other) const; }; }
--- a/OrthancStone/Sources/Toolbox/DicomStructureSet.cpp Tue Apr 22 14:55:22 2025 +0200 +++ b/OrthancStone/Sources/Toolbox/DicomStructureSet.cpp Tue Apr 22 18:20:01 2025 +0200 @@ -226,18 +226,6 @@ geometry_ = geometry; projectionAlongNormal_ = GeometryToolbox::ProjectAlongNormal(geometry.GetOrigin(), geometry.GetNormal()); sliceThickness_ = it->second.thickness_; - - extent_.Clear(); - - for (Points::const_iterator it2 = points_.begin(); it2 != points_.end(); ++it2) - { - if (IsPointOnSliceIfAny(*it2)) - { - double x, y; - geometry.ProjectPoint2(x, y, *it2); - extent_.AddPoint(x, y); - } - } return true; } } @@ -286,7 +274,7 @@ void DicomStructureSet::Polygon::Project(std::list<Extent2D>& target, const CoordinateSystem3D& cuttingPlane, const Vector& estimatedNormal, - double estimatedSliceThickness) const + double estimatedSliceThickness) { CoordinateSystem3D geometry; double thickness = estimatedSliceThickness; @@ -314,8 +302,11 @@ bool found = false; for (size_t i = 1; i < points_.size(); i++) { - axisX = points_[1] - origin; - if (boost::numeric::ublas::norm_2(axisX) > 10.0 * std::numeric_limits<double>::epsilon()) + axisX = points_[i] - origin; + + bool isOpposite; // Ignored + if (boost::numeric::ublas::norm_2(axisX) > 10.0 * std::numeric_limits<double>::epsilon() && + !GeometryToolbox::IsParallelOrOpposite(isOpposite, axisX, estimatedNormal)) { found = true; break; @@ -371,14 +362,33 @@ return; // Should never happen } + if (cachedProjectedSegments_.get() == NULL || + !cachedGeometry_.Equals(geometry)) + { + cachedGeometry_ = geometry; + + cachedProjectedSegments_.reset(new std::vector<float>()); + cachedProjectedSegments_->resize(2 * points_.size()); + + for (size_t i = 0; i < points_.size(); i++) + { + double x, y; + geometry.ProjectPoint(x, y, points_[i]); + (*cachedProjectedSegments_) [2 * i] = x; + (*cachedProjectedSegments_) [2 * i + 1] = y; + } + } + std::vector<double> intersections; intersections.reserve(points_.size()); for (size_t i = 0; i < points_.size(); i++) { - double segmentX1, segmentY1, segmentX2, segmentY2; - geometry.ProjectPoint(segmentX1, segmentY1, points_[i]); - geometry.ProjectPoint(segmentX2, segmentY2, points_[(i + 1) % points_.size()]); + const size_t next = (i + 1) % points_.size(); + const double segmentX1 = (*cachedProjectedSegments_) [2 * i]; + const double segmentY1 = (*cachedProjectedSegments_) [2 * i + 1]; + const double segmentX2 = (*cachedProjectedSegments_) [2 * next]; + const double segmentY2 = (*cachedProjectedSegments_) [2 * next + 1]; double x, y; if (GeometryToolbox::IntersectLineAndSegment(x, y, cuttingX1, cuttingY1, cuttingX2, cuttingY2, @@ -432,14 +442,27 @@ } + DicomStructureSet::Structure::~Structure() + { + for (Polygons::iterator it = polygons_.begin(); it != polygons_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + } + + const DicomStructureSet::Structure& DicomStructureSet::GetStructure(size_t index) const { if (index >= structures_.size()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - - return structures_[index]; + else + { + assert(structures_[index] != NULL); + return *structures_[index]; + } } @@ -449,8 +472,11 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - - return structures_[index]; + else + { + assert(structures_[index] != NULL); + return *structures_[index]; + } } void DicomStructureSet::Setup(const IDicomDataset& tags) @@ -495,18 +521,19 @@ roiNumbersIndex[roiNumber] = i; - structures_[i].name_ = reader.GetStringValue + structures_[i] = new Structure(); + structures_[i]->name_ = reader.GetStringValue (Orthanc::DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, DICOM_TAG_ROI_NAME), "No name"); - structures_[i].interpretation_ = "No interpretation"; + structures_[i]->interpretation_ = "No interpretation"; - if (structureNamesIndex_.find(structures_[i].name_) == structureNamesIndex_.end()) + if (structureNamesIndex_.find(structures_[i]->name_) == structureNamesIndex_.end()) { - structureNamesIndex_[structures_[i].name_] = i; + structureNamesIndex_[structures_[i]->name_] = i; } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, - "RT-STRUCT with twice the same name for a structure: " + structures_[i].name_); + "RT-STRUCT with twice the same name for a structure: " + structures_[i]->name_); } } } @@ -544,7 +571,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } - structures_[found->second].interpretation_ = interpretation; + structures_[found->second]->interpretation_ = interpretation; } } } @@ -577,7 +604,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } - Structure& target = structures_[found->second]; + Structure& target = *structures_[found->second]; Vector color; if (FastParseVector(color, tags, Orthanc::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, @@ -685,8 +712,8 @@ LOG(ERROR) << "WARNING. The following Dicom tag (Referenced SOP Instance UID) contains an empty value : // (3006,0039)[" << i << "] / (0x3006, 0x0040)[0] / (0x3006, 0x0016)[0] / (0x0008, 0x1155)"; } - Polygon polygon(sopInstanceUid); - polygon.Reserve(countPoints); + std::unique_ptr<Polygon> polygon(new Polygon(sopInstanceUid)); + polygon->Reserve(countPoints); for (size_t k = 0; k < countPoints; k++) { @@ -694,10 +721,10 @@ v[0] = points[3 * k]; v[1] = points[3 * k + 1]; v[2] = points[3 * k + 2]; - polygon.AddPoint(v); + polygon->AddPoint(v); } - target.polygons_.push_back(polygon); + target.polygons_.push_back(polygon.release()); } } } @@ -722,29 +749,13 @@ #endif - Vector DicomStructureSet::GetStructureCenter(size_t index) const + DicomStructureSet::~DicomStructureSet() { - const Structure& structure = GetStructure(index); - - Vector center; - LinearAlgebra::AssignVector(center, 0, 0, 0); - if (structure.polygons_.empty()) + for (size_t i = 0; i < structures_.size(); i++) { - return center; + assert(structures_[i] != NULL); + delete structures_[i]; } - - double n = static_cast<double>(structure.polygons_.size()); - - for (Polygons::const_iterator polygon = structure.polygons_.begin(); - polygon != structure.polygons_.end(); ++polygon) - { - if (!polygon->GetPoints().empty()) - { - center += polygon->GetPoints().front() / n; - } - } - - return center; } @@ -769,13 +780,14 @@ void DicomStructureSet::GetReferencedInstances(std::set<std::string>& instances) const { - for (Structures::const_iterator structure = structures_.begin(); - structure != structures_.end(); ++structure) + for (size_t i = 0; i < structures_.size(); i++) { - for (Polygons::const_iterator polygon = structure->polygons_.begin(); - polygon != structure->polygons_.end(); ++polygon) + assert(structures_[i] != NULL); + for (Polygons::const_iterator polygon = structures_[i]->polygons_.begin(); + polygon != structures_[i]->polygons_.end(); ++polygon) { - instances.insert(polygon->GetSopInstanceUid()); + assert(*polygon != NULL); + instances.insert((*polygon)->GetSopInstanceUid()); } } } @@ -819,13 +831,15 @@ referencedSlices_[sopInstanceUid] = ReferencedSlice(seriesInstanceUid, geometry, thickness); - for (Structures::iterator structure = structures_.begin(); - structure != structures_.end(); ++structure) + for (size_t i = 0; i < structures_.size(); i++) { - for (Polygons::iterator polygon = structure->polygons_.begin(); - polygon != structure->polygons_.end(); ++polygon) + assert(structures_[i] != NULL); + + for (Polygons::iterator polygon = structures_[i]->polygons_.begin(); + polygon != structures_[i]->polygons_.end(); ++polygon) { - polygon->UpdateReferencedSlice(referencedSlices_); + assert(*polygon != NULL); + (*polygon)->UpdateReferencedSlice(referencedSlices_); } } } @@ -862,15 +876,16 @@ void DicomStructureSet::CheckReferencedSlices() { - for (Structures::iterator structure = structures_.begin(); - structure != structures_.end(); ++structure) + for (size_t i = 0; i < structures_.size(); i++) { - for (Polygons::iterator polygon = structure->polygons_.begin(); - polygon != structure->polygons_.end(); ++polygon) + assert(structures_[i] != NULL); + for (Polygons::iterator polygon = structures_[i]->polygons_.begin(); + polygon != structures_[i]->polygons_.end(); ++polygon) { - if (!polygon->UpdateReferencedSlice(referencedSlices_)) + assert(*polygon != NULL); + if (!(*polygon)->UpdateReferencedSlice(referencedSlices_)) { - std::string sopInstanceUid = polygon->GetSopInstanceUid(); + std::string sopInstanceUid = (*polygon)->GetSopInstanceUid(); if (Orthanc::Toolbox::StripSpaces(sopInstanceUid) == "") { LOG(ERROR) << "DicomStructureSet::CheckReferencedSlices(): " @@ -924,9 +939,10 @@ for (Polygons::const_iterator polygon = structure.polygons_.begin(); polygon != structure.polygons_.end(); ++polygon) { - const Points& points = polygon->GetPoints(); + assert(*polygon != NULL); + const Points& points = (*polygon)->GetPoints(); - if (polygon->IsOnSlice(cutting, GetEstimatedNormal(), GetEstimatedSliceThickness()) && + if ((*polygon)->IsOnSlice(cutting, GetEstimatedNormal(), GetEstimatedSliceThickness()) && !points.empty()) { chains.push_back(std::vector<ScenePoint2D>()); @@ -990,7 +1006,8 @@ for (Polygons::const_iterator polygon = structure.polygons_.begin(); polygon != structure.polygons_.end(); ++polygon) { - polygon->Project(rectangles, cutting, GetEstimatedNormal(), GetEstimatedSliceThickness()); + assert(*polygon != NULL); + (*polygon)->Project(rectangles, cutting, GetEstimatedNormal(), GetEstimatedSliceThickness()); } typedef std::list< std::vector<ScenePoint2D> > Contours; @@ -1044,12 +1061,13 @@ // TODO - Could be optimized by adding a multimap on "Structure", mapping // from SOP Instance UID to polygons - for (Polygons::const_iterator it = structure.polygons_.begin(); - it != structure.polygons_.end(); ++it) + for (Polygons::const_iterator polygon = structure.polygons_.begin(); + polygon != structure.polygons_.end(); ++polygon) { - if (it->GetSopInstanceUid() == sopInstanceUid) + assert(*polygon != NULL); + if ((*polygon)->GetSopInstanceUid() == sopInstanceUid) { - target.push_back(it->GetPoints()); + target.push_back((*polygon)->GetPoints()); } } } @@ -1066,13 +1084,16 @@ unsigned int countPolygons = 0; for (size_t i = 0; i < structures_.size(); i++) { - const Polygons& polygons = structures_[i].polygons_; + assert(structures_[i] != NULL); + const Polygons& polygons = structures_[i]->polygons_; - for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) + for (Polygons::const_iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon) { + assert(*polygon != NULL); + countPolygons++; - - const Points& points = it->GetPoints(); + + const Points& points = (*polygon)->GetPoints(); if (points.size() >= 3) { @@ -1149,11 +1170,13 @@ for (size_t i = 0; i < structures_.size(); i++) { - const Polygons& polygons = structures_[i].polygons_; + assert(structures_[i] != NULL); + const Polygons& polygons = structures_[i]->polygons_; - for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) + for (Polygons::const_iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon) { - const Points& points = it->GetPoints(); + assert(*polygon != NULL); + const Points& points = (*polygon)->GetPoints(); polygonsProjection.push_back(GeometryToolbox::ProjectAlongNormal(points[0], estimatedNormal_)); } }
--- a/OrthancStone/Sources/Toolbox/DicomStructureSet.h Tue Apr 22 14:55:22 2025 +0200 +++ b/OrthancStone/Sources/Toolbox/DicomStructureSet.h Tue Apr 22 18:20:01 2025 +0200 @@ -72,7 +72,7 @@ typedef std::vector<Vector> Points; - class Polygon + class Polygon : public boost::noncopyable { private: std::string sopInstanceUid_; @@ -81,7 +81,9 @@ double projectionAlongNormal_; double sliceThickness_; // In millimeters Points points_; - Extent2D extent_; + + CoordinateSystem3D cachedGeometry_; + std::unique_ptr< std::vector<float> > cachedProjectedSegments_; bool IsPointOnSliceIfAny(const Vector& v) const; @@ -130,12 +132,12 @@ void Project(std::list<Extent2D>& target, const CoordinateSystem3D& cuttingPlane, const Vector& estimatedNormal, - double estimatedSliceThickness) const; + double estimatedSliceThickness); }; - typedef std::list<Polygon> Polygons; + typedef std::list<Polygon*> Polygons; - struct Structure + struct Structure : public boost::noncopyable { std::string name_; std::string interpretation_; @@ -143,17 +145,17 @@ uint8_t red_; uint8_t green_; uint8_t blue_; + + ~Structure(); }; - typedef std::vector<Structure> Structures; typedef std::map<std::string, size_t> StructureNamesIndex; - Structures structures_; - ReferencedSlices referencedSlices_; - Vector estimatedNormal_; - double estimatedSliceThickness_; - StructureNamesIndex structureNamesIndex_; - + std::vector<Structure*> structures_; + ReferencedSlices referencedSlices_; + Vector estimatedNormal_; + double estimatedSliceThickness_; + StructureNamesIndex structureNamesIndex_; void Setup(const IDicomDataset& dataset); @@ -177,13 +179,13 @@ explicit DicomStructureSet(Orthanc::ParsedDicomFile& instance); #endif + ~DicomStructureSet(); + size_t GetStructuresCount() const { return structures_.size(); } - Vector GetStructureCenter(size_t index) const; - const std::string& GetStructureName(size_t index) const; const std::string& GetStructureInterpretation(size_t index) const;