# HG changeset patch # User Sebastien Jodogne # Date 1535541868 -7200 # Node ID d88970f1ffbfa9cf4baf98ef181d973811a152d0 # Parent 579acc5e5412cb5de740eea796276188e512c595 fix ordering of non-parallel slices + /tools/reconstruct diff -r 579acc5e5412 -r d88970f1ffbf Core/DicomFormat/DicomMap.cpp --- a/Core/DicomFormat/DicomMap.cpp Tue Aug 28 15:14:33 2018 +0200 +++ b/Core/DicomFormat/DicomMap.cpp Wed Aug 29 13:24:28 2018 +0200 @@ -109,7 +109,16 @@ DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER, DICOM_TAG_SOP_INSTANCE_UID, DICOM_TAG_IMAGE_POSITION_PATIENT, // New in db v6 - DICOM_TAG_IMAGE_COMMENTS // New in db v6 + DICOM_TAG_IMAGE_COMMENTS, // New in db v6 + + /** + * Main DICOM tags that are not part of any release of the + * database schema yet, and that will be part of future db v7. In + * the meantime, the user must call "/tools/reconstruct" once to + * access these tags if the corresponding DICOM files where + * indexed in the database by an older version of Orthanc. + **/ + DICOM_TAG_IMAGE_ORIENTATION_PATIENT // New in Orthanc 1.4.2 }; diff -r 579acc5e5412 -r d88970f1ffbf NEWS --- a/NEWS Tue Aug 28 15:14:33 2018 +0200 +++ b/NEWS Wed Aug 29 13:24:28 2018 +0200 @@ -5,6 +5,13 @@ ------- * "OrthancPeers" configuration option now allows to specify HTTP headers +* Fix "/series/.../ordered-slices" in the presence of non-parallel slices + +REST API +-------- + +* "/tools/reconstruct" to reconstruct the main DICOM tags, the JSON summary and + the metadata of all the instances stored in Orthanc. This is a slow operation! Plugins ------- @@ -15,6 +22,7 @@ Maintenance ----------- +* New main DICOM tag: "ImageOrientationPatient" at the instance level * New configuration option: "HttpVerbose" to debug outgoing HTTP connections * Fix incoming DICOM C-Store filtering for JPEG-LS transfer syntaxes * Fix OrthancPluginHttpClient() to return the HTTP status on errors diff -r 579acc5e5412 -r d88970f1ffbf OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Tue Aug 28 15:14:33 2018 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Aug 29 13:24:28 2018 +0200 @@ -1549,6 +1549,23 @@ } + static void ReconstructAllResources(RestApiPostCall& call) + { + ServerContext& context = OrthancRestApi::GetContext(call); + + std::list studies; + context.GetIndex().GetAllUuids(studies, ResourceType_Study); + + for (std::list::const_iterator + study = studies.begin(); study != studies.end(); ++study) + { + ServerToolbox::ReconstructResource(context, *study); + } + + call.GetOutput().AnswerBuffer("", "text/plain"); + } + + void OrthancRestApi::RegisterResources() { Register("/instances", ListResources); @@ -1654,5 +1671,6 @@ Register("/studies/{id}/reconstruct", ReconstructResource); Register("/series/{id}/reconstruct", ReconstructResource); Register("/instances/{id}/reconstruct", ReconstructResource); + Register("/tools/reconstruct", ReconstructAllResources); } } diff -r 579acc5e5412 -r d88970f1ffbf OrthancServer/SliceOrdering.cpp --- a/OrthancServer/SliceOrdering.cpp Tue Aug 28 15:14:33 2018 +0200 +++ b/OrthancServer/SliceOrdering.cpp Wed Aug 29 13:24:28 2018 +0200 @@ -95,12 +95,69 @@ } + static bool IsCloseToZero(double x) + { + return fabs(x) < 10.0 * std::numeric_limits::epsilon(); + } + + + bool SliceOrdering::ComputeNormal(Vector& normal, + const DicomMap& dicom) + { + std::vector cosines; + + if (TokenizeVector(cosines, dicom, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, 6)) + { + assert(cosines.size() == 6); + normal[0] = cosines[1] * cosines[5] - cosines[2] * cosines[4]; + normal[1] = cosines[2] * cosines[3] - cosines[0] * cosines[5]; + normal[2] = cosines[0] * cosines[4] - cosines[1] * cosines[3]; + return true; + } + else + { + return false; + } + } + + + bool SliceOrdering::IsParallelOrOpposite(const Vector& u, + const Vector& v) + { + // Check out "GeometryToolbox::IsParallelOrOpposite()" in Stone of + // Orthanc for explanations + const double u1 = u[0]; + const double u2 = u[1]; + const double u3 = u[2]; + const double normU = sqrt(u1 * u1 + u2 * u2 + u3 * u3); + + const double v1 = v[0]; + const double v2 = v[1]; + const double v3 = v[2]; + const double normV = sqrt(v1 * v1 + v2 * v2 + v3 * v3); + + if (IsCloseToZero(normU * normV)) + { + return false; + } + else + { + const double cosAngle = (u1 * v1 + u2 * v2 + u3 * v3) / (normU * normV); + + return (IsCloseToZero(cosAngle - 1.0) || // Close to +1: Parallel, non-opposite + IsCloseToZero(fabs(cosAngle) - 1.0)); // Close to -1: Parallel, opposite + } + } + + struct SliceOrdering::Instance : public boost::noncopyable { private: std::string instanceId_; bool hasPosition_; Vector position_; + bool hasNormal_; + Vector normal_; bool hasIndexInSeries_; size_t indexInSeries_; unsigned int framesCount_; @@ -141,6 +198,8 @@ position_[2] = tmp[2]; } + hasNormal_ = ComputeNormal(normal_, instance); + std::string s; hasIndexInSeries_ = false; @@ -190,6 +249,17 @@ { return framesCount_; } + + bool HasNormal() const + { + return hasNormal_; + } + + const Vector& GetNormal() const + { + assert(hasNormal_); + return normal_; + } }; @@ -226,15 +296,7 @@ throw OrthancException(ErrorCode_UnknownResource); } - std::vector cosines; - hasNormal_ = TokenizeVector(cosines, series, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, 6); - - if (hasNormal_) - { - normal_[0] = cosines[1] * cosines[5] - cosines[2] * cosines[4]; - normal_[1] = cosines[2] * cosines[3] - cosines[0] * cosines[5]; - normal_[2] = cosines[0] * cosines[4] - cosines[1] * cosines[3]; - } + hasNormal_ = ComputeNormal(normal_, series); } @@ -268,7 +330,10 @@ for (size_t i = 0; i < instances_.size(); i++) { assert(instances_[i] != NULL); - if (!instances_[i]->HasPosition()) + + if (!instances_[i]->HasPosition() || + (instances_[i]->HasNormal() && + !IsParallelOrOpposite(instances_[i]->GetNormal(), normal_))) { return false; } diff -r 579acc5e5412 -r d88970f1ffbf OrthancServer/SliceOrdering.h --- a/OrthancServer/SliceOrdering.h Tue Aug 28 15:14:33 2018 +0200 +++ b/OrthancServer/SliceOrdering.h Wed Aug 29 13:24:28 2018 +0200 @@ -52,6 +52,12 @@ std::vector instances_; bool isVolume_; + static bool ComputeNormal(Vector& normal, + const DicomMap& dicom); + + static bool IsParallelOrOpposite(const Vector& a, + const Vector& b); + static bool IndexInSeriesComparator(const SliceOrdering::Instance* a, const SliceOrdering::Instance* b); diff -r 579acc5e5412 -r d88970f1ffbf UnitTestsSources/DicomMapTests.cpp --- a/UnitTestsSources/DicomMapTests.cpp Tue Aug 28 15:14:33 2018 +0200 +++ b/UnitTestsSources/DicomMapTests.cpp Wed Aug 29 13:24:28 2018 +0200 @@ -197,6 +197,7 @@ *it == DicomTag(0x0020, 0x0100) || /* TemporalPositionIdentifier, from MR Image module */ *it == DicomTag(0x0028, 0x0008) || /* NumberOfFrames, from Multi-frame module attributes, related to Image */ *it == DicomTag(0x0020, 0x0032) || /* ImagePositionPatient, from Image Plan module, related to Image */ + *it == DicomTag(0x0020, 0x0037) || /* ImageOrientationPatient, from Image Plane Module (Orthanc 1.4.2) */ *it == DicomTag(0x0020, 0x4000))) /* ImageComments, from General Image module */ { ok = true;