# HG changeset patch # User Sebastien Jodogne # Date 1699460611 -3600 # Node ID 4288d635d77e8a2b3387342c761a5e99f452f514 # Parent a9e23ef9ee098f88c2dd7c0dc13790ca42c9e6df first rendering of dicom-sr diff -r a9e23ef9ee09 -r 4288d635d77e Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp --- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Wed Nov 08 16:31:49 2023 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Wed Nov 08 17:23:31 2023 +0100 @@ -457,49 +457,64 @@ double pixelSpacingX, double pixelSpacingY) const ORTHANC_OVERRIDE { - size_t frameIndex; - if (!LookupFrame(frameIndex, sopInstanceUid, frameNumber)) - { - return NULL; - } - - const OrthancStone::DicomInstanceParameters& parameters = GetInstanceOfFrame(frameIndex); + std::unique_ptr layer(new OrthancStone::MacroSceneLayer); const double x = originX - pixelSpacingX / 2.0; const double y = originY - pixelSpacingY / 2.0; - const double w = parameters.GetWidth() * pixelSpacingX; - const double h = parameters.GetHeight() * pixelSpacingY; - - std::unique_ptr layer(new OrthancStone::MacroSceneLayer); - - { - std::unique_ptr polyline(new OrthancStone::PolylineSceneLayer); + + for (size_t i = 0; i < sr_->GetStructuresCount(); i++) + { + const OrthancStone::DicomStructuredReport::Structure& structure = sr_->GetStructure(i); + if (structure.GetSopInstanceUid() == sopInstanceUid && + (!structure.HasFrameNumber() || + structure.GetFrameNumber() == frameNumber)) { - OrthancStone::PolylineSceneLayer::Chain chain; - chain.push_back(OrthancStone::ScenePoint2D(x, y)); - chain.push_back(OrthancStone::ScenePoint2D(x + pixelSpacingX, y)); - chain.push_back(OrthancStone::ScenePoint2D(x + pixelSpacingX, y + pixelSpacingY)); - chain.push_back(OrthancStone::ScenePoint2D(x, y + pixelSpacingY)); - - polyline->AddChain(chain, true, 255, 0, 0); + OrthancStone::Color color(0, 0, 255); + + if (structure.HasProbabilityOfCancer()) + { + if (structure.GetProbabilityOfCancer() > 50.0f) + { + color = OrthancStone::Color(255, 0, 0); + } + else + { + color = OrthancStone::Color(0, 255, 0); + } + } + + switch (structure.GetType()) + { + case OrthancStone::DicomStructuredReport::StructureType_Point: + // TODO + break; + + case OrthancStone::DicomStructuredReport::StructureType_Polyline: + { + const OrthancStone::DicomStructuredReport::Polyline& source = dynamic_cast(structure); + + if (source.GetSize() > 1) + { + std::unique_ptr target(new OrthancStone::PolylineSceneLayer); + + OrthancStone::PolylineSceneLayer::Chain chain; + chain.resize(source.GetSize()); + for (size_t i = 0; i < source.GetSize(); i++) + { + chain[i] = OrthancStone::ScenePoint2D(x + source.GetPoint(i).GetX() * pixelSpacingX, + y + source.GetPoint(i).GetY() * pixelSpacingY); + } + + target->AddChain(chain, false, color.GetRed(), color.GetGreen(), color.GetBlue()); + layer->AddLayer(target.release()); + } + break; + } + + default: + break; + } } - - layer->AddLayer(polyline.release()); - } - - { - std::unique_ptr polyline(new OrthancStone::PolylineSceneLayer); - { - OrthancStone::PolylineSceneLayer::Chain chain; - chain.push_back(OrthancStone::ScenePoint2D(x, y)); - chain.push_back(OrthancStone::ScenePoint2D(x + w, y)); - chain.push_back(OrthancStone::ScenePoint2D(x + w, y + h)); - chain.push_back(OrthancStone::ScenePoint2D(x, y + h)); - - polyline->AddChain(chain, true, 255, 0, 0); - } - - layer->AddLayer(polyline.release()); } return layer.release(); diff -r a9e23ef9ee09 -r 4288d635d77e OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp --- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp Wed Nov 08 16:31:49 2023 +0100 +++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp Wed Nov 08 17:23:31 2023 +0100 @@ -118,193 +118,138 @@ namespace OrthancStone { - void DicomStructuredReport::ReferencedInstance::AddFrame(unsigned int frame) + void DicomStructuredReport::Structure::Copy(const Structure& other) + { + if (other.HasFrameNumber()) + { + SetFrameNumber(other.GetFrameNumber()); + } + + if (other.HasProbabilityOfCancer()) + { + SetProbabilityOfCancer(other.GetProbabilityOfCancer()); + } + } + + + DicomStructuredReport::Structure::Structure(const std::string& sopInstanceUid) : + sopInstanceUid_(sopInstanceUid), + hasFrameNumber_(false), + hasProbabilityOfCancer_(false) { - frames_.insert(frame); + } + + + void DicomStructuredReport::Structure::SetFrameNumber(unsigned int frame) + { + hasFrameNumber_ = true; + frameNumber_ = frame; + } + + + void DicomStructuredReport::Structure::SetProbabilityOfCancer(float probability) + { + if (probability < 0 || + probability > 100) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + hasProbabilityOfCancer_ = true; + probabilityOfCancer_ = probability; + } } - class DicomStructuredReport::Structure : public boost::noncopyable + unsigned int DicomStructuredReport::Structure::GetFrameNumber() const { - private: - std::string sopInstanceUid_; - bool hasFrameNumber_; - unsigned int frameNumber_; - bool hasProbabilityOfCancer_; - float probabilityOfCancer_; - - protected: - void Copy(const Structure& other) + if (hasFrameNumber_) { - if (other.HasFrameNumber()) - { - SetFrameNumber(other.GetFrameNumber()); - } + return frameNumber_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } - if (other.HasProbabilityOfCancer()) - { - SetProbabilityOfCancer(other.GetProbabilityOfCancer()); - } - } - - public: - Structure(const std::string& sopInstanceUid) : - sopInstanceUid_(sopInstanceUid), - hasFrameNumber_(false), - hasProbabilityOfCancer_(false) + float DicomStructuredReport::Structure::GetProbabilityOfCancer() const + { + if (hasProbabilityOfCancer_) { - } - - virtual ~Structure() - { + return probabilityOfCancer_; } - - virtual Structure* Clone() const = 0; - - const std::string& GetSopInstanceUid() const + else { - return sopInstanceUid_; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } + } - void SetFrameNumber(unsigned int frame) - { - hasFrameNumber_ = true; - frameNumber_ = frame; - } - void SetProbabilityOfCancer(float probability) - { - if (probability < 0 || - probability > 100) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - hasProbabilityOfCancer_ = true; - probabilityOfCancer_ = probability; - } - } + DicomStructuredReport::Point::Point(const std::string& sopInstanceUid, + double x, + double y) : + Structure(sopInstanceUid), + point_(x, y) + { + } + - bool HasFrameNumber() const - { - return hasFrameNumber_; - } + DicomStructuredReport::Structure* DicomStructuredReport::Point::Clone() const + { + std::unique_ptr cloned(new Point(GetSopInstanceUid(), point_.GetX(), point_.GetY())); + cloned->Copy(*this); + return cloned.release(); + } - bool HasProbabilityOfCancer() const - { - return hasProbabilityOfCancer_; - } - unsigned int GetFrameNumber() const + DicomStructuredReport::Polyline::Polyline(const std::string& sopInstanceUid, + const float* points, + unsigned long pointsCount) : + Structure(sopInstanceUid) + { + if (pointsCount % 2 != 0) { - if (hasFrameNumber_) - { - return frameNumber_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } - float GetProbabilityOfCancer() const + points_.reserve(pointsCount / 2); + + for (unsigned long i = 0; i < pointsCount; i += 2) { - if (hasProbabilityOfCancer_) - { - return probabilityOfCancer_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } + points_.push_back(ScenePoint2D(points[i], points[i + 1])); } - }; + } - class DicomStructuredReport::Point : public Structure + DicomStructuredReport::Polyline::Polyline(const std::string& sopInstanceUid, + const std::vector& points) : + Structure(sopInstanceUid), + points_(points) { - private: - ScenePoint2D point_; - - public: - Point(const std::string& sopInstanceUid, - double x, - double y) : - Structure(sopInstanceUid), - point_(x, y) - { - } - - virtual Structure* Clone() const - { - std::unique_ptr cloned(new Point(GetSopInstanceUid(), point_.GetX(), point_.GetY())); - cloned->Copy(*this); - return cloned.release(); - } - - const ScenePoint2D& GetPoint() const - { - return point_; - } - }; + } - class DicomStructuredReport::Polyline : public Structure + DicomStructuredReport::Structure* DicomStructuredReport::Polyline::Clone() const { - private: - std::vector points_; + std::unique_ptr cloned(new Polyline(GetSopInstanceUid(), points_)); + cloned->Copy(*this); + return cloned.release(); + } - public: - Polyline(const std::string& sopInstanceUid, - const float* points, - unsigned long pointsCount) : - Structure(sopInstanceUid) - { - if (pointsCount % 2 != 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - points_.reserve(pointsCount / 2); - - for (unsigned long i = 0; i < pointsCount; i += 2) - { - points_.push_back(ScenePoint2D(points[i], points[i + 1])); - } - } - Polyline(const std::string& sopInstanceUid, - const std::vector& points) : - Structure(sopInstanceUid), - points_(points) - { - } - - virtual Structure* Clone() const - { - std::unique_ptr cloned(new Polyline(GetSopInstanceUid(), points_)); - cloned->Copy(*this); - return cloned.release(); - } - - size_t GetSize() const + const ScenePoint2D& DicomStructuredReport::Polyline::GetPoint(size_t i) const + { + if (i >= points_.size()) { - return points_.size(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - - const ScenePoint2D& GetPoint(size_t i) const + else { - if (i >= points_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - return points_[i]; - } + return points_[i]; } - }; + } void DicomStructuredReport::AddStructure(const std::string& sopInstanceUid, @@ -573,7 +518,7 @@ instancesInformation_[it->first] = new ReferencedInstance(*it->second); } - for (std::list::const_iterator it = other.structures_.begin(); it != other.structures_.end(); ++it) + for (std::deque::const_iterator it = other.structures_.begin(); it != other.structures_.end(); ++it) { assert(*it != NULL); structures_.push_back((*it)->Clone()); @@ -583,7 +528,7 @@ DicomStructuredReport::~DicomStructuredReport() { - for (std::list::iterator it = structures_.begin(); it != structures_.end(); ++it) + for (std::deque::iterator it = structures_.begin(); it != structures_.end(); ++it) { assert(*it != NULL); delete *it; @@ -648,4 +593,18 @@ } } } + + + const DicomStructuredReport::Structure& DicomStructuredReport::GetStructure(size_t index) const + { + if (index >= structures_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(structures_[index] != NULL); + return *structures_[index]; + } + } } diff -r a9e23ef9ee09 -r 4288d635d77e OrthancStone/Sources/Toolbox/DicomStructuredReport.h --- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.h Wed Nov 08 16:31:49 2023 +0100 +++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.h Wed Nov 08 17:23:31 2023 +0100 @@ -31,9 +31,12 @@ # error Support for DCMTK must be enabled #endif +#include "../Scene2D/ScenePoint2D.h" + #include #include +#include #include #include @@ -41,11 +44,115 @@ { class DicomStructuredReport : public boost::noncopyable { + public: + enum StructureType + { + StructureType_Point, + StructureType_Polyline + }; + + class Structure : public boost::noncopyable + { + private: + std::string sopInstanceUid_; + bool hasFrameNumber_; + unsigned int frameNumber_; + bool hasProbabilityOfCancer_; + float probabilityOfCancer_; + + protected: + void Copy(const Structure& other); + + public: + Structure(const std::string& sopInstanceUid); + + virtual ~Structure() + { + } + + virtual Structure* Clone() const = 0; + + virtual StructureType GetType() const = 0; + + const std::string& GetSopInstanceUid() const + { + return sopInstanceUid_; + } + + void SetFrameNumber(unsigned int frame); + + void SetProbabilityOfCancer(float probability); + + bool HasFrameNumber() const + { + return hasFrameNumber_; + } + + bool HasProbabilityOfCancer() const + { + return hasProbabilityOfCancer_; + } + + unsigned int GetFrameNumber() const; + + float GetProbabilityOfCancer() const; + }; + + + class Point : public Structure + { + private: + ScenePoint2D point_; + + public: + Point(const std::string& sopInstanceUid, + double x, + double y); + + virtual Structure* Clone() const ORTHANC_OVERRIDE; + + virtual StructureType GetType() const ORTHANC_OVERRIDE + { + return StructureType_Point; + } + + const ScenePoint2D& GetPoint() const + { + return point_; + } + }; + + + class Polyline : public Structure + { + private: + std::vector points_; + + public: + Polyline(const std::string& sopInstanceUid, + const float* points, + unsigned long pointsCount); + + Polyline(const std::string& sopInstanceUid, + const std::vector& points); + + virtual Structure* Clone() const ORTHANC_OVERRIDE; + + virtual StructureType GetType() const ORTHANC_OVERRIDE + { + return StructureType_Polyline; + } + + size_t GetSize() const + { + return points_.size(); + } + + const ScenePoint2D& GetPoint(size_t i) const; + }; + + private: - class Structure; - class Point; - class Polyline; - class ReferencedInstance { private: @@ -79,7 +186,10 @@ return sopClassUid_; } - void AddFrame(unsigned int frame); + void AddFrame(unsigned int frame) + { + frames_.insert(frame); + } const std::set& GetFrames() const { @@ -100,7 +210,7 @@ std::string sopInstanceUid_; std::map instancesInformation_; std::vector orderedInstances_; - std::list structures_; + std::deque structures_; public: class ReferencedFrame @@ -185,5 +295,12 @@ size_t i) const; void ExportReferencedFrames(std::list& frames) const; + + size_t GetStructuresCount() const + { + return structures_.size(); + } + + const Structure& GetStructure(size_t index) const; }; }