Mercurial > hg > orthanc-stone
comparison Framework/Toolbox/Slice.cpp @ 117:42c05a3baee3 wasm
loading multi-frame instances as 3D volumes
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 28 Sep 2017 16:55:51 +0200 |
parents | 2eca030792aa |
children | a4d0b6c82b29 |
comparison
equal
deleted
inserted
replaced
116:4c5f7cda8624 | 117:42c05a3baee3 |
---|---|
19 **/ | 19 **/ |
20 | 20 |
21 | 21 |
22 #include "Slice.h" | 22 #include "Slice.h" |
23 | 23 |
24 #include "../Enumerations.h" | |
25 | |
26 #include <Core/Logging.h> | |
24 #include <Core/OrthancException.h> | 27 #include <Core/OrthancException.h> |
28 #include <Core/Toolbox.h> | |
29 | |
30 #include <boost/lexical_cast.hpp> | |
25 | 31 |
26 namespace OrthancStone | 32 namespace OrthancStone |
27 { | 33 { |
34 static bool ParseDouble(double& target, | |
35 const std::string& source) | |
36 { | |
37 try | |
38 { | |
39 target = boost::lexical_cast<double>(source); | |
40 return true; | |
41 } | |
42 catch (boost::bad_lexical_cast&) | |
43 { | |
44 return false; | |
45 } | |
46 } | |
47 | |
48 bool Slice::ComputeRTDoseGeometry(const OrthancPlugins::DicomDatasetReader& reader, | |
49 unsigned int frame) | |
50 { | |
51 // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html | |
52 static const OrthancPlugins::DicomTag DICOM_TAG_GRID_FRAME_OFFSET_VECTOR(0x3004, 0x000c); | |
53 static const OrthancPlugins::DicomTag DICOM_TAG_FRAME_INCREMENT_POINTER(0x0028, 0x0009); | |
54 | |
55 std::string increment = reader.GetStringValue(DICOM_TAG_FRAME_INCREMENT_POINTER, ""); | |
56 std::string offsetTag; | |
57 | |
58 bool ok = reader.GetDataset().GetStringValue(offsetTag, DICOM_TAG_GRID_FRAME_OFFSET_VECTOR); | |
59 if (!ok) | |
60 { | |
61 LOG(ERROR) << "Cannot read the \"GridFrameOffsetVector\" tag, check you are using Orthanc >= 1.3.1"; | |
62 return false; | |
63 } | |
64 | |
65 Orthanc::Toolbox::ToUpperCase(increment); | |
66 if (increment != "3004,000C" || | |
67 offsetTag.empty()) | |
68 { | |
69 return false; | |
70 } | |
71 | |
72 std::vector<std::string> offsets; | |
73 Orthanc::Toolbox::TokenizeString(offsets, offsetTag, '\\'); | |
74 | |
75 if (frameCount_ == 0 || | |
76 offsets.size() != frameCount_ || | |
77 frame >= frameCount_) | |
78 { | |
79 LOG(ERROR) << "No information about the 3D location of some slice(s) in a RT DOSE"; | |
80 return false; | |
81 } | |
82 | |
83 double offset0, z; | |
84 | |
85 if (!ParseDouble(offset0, offsets[0]) || | |
86 !ParseDouble(z, offsets[frame])) | |
87 { | |
88 LOG(ERROR) << "Invalid syntax"; | |
89 return false; | |
90 } | |
91 | |
92 if (!GeometryToolbox::IsCloseToZero(offset0)) | |
93 { | |
94 LOG(ERROR) << "Invalid syntax"; | |
95 return false; | |
96 } | |
97 | |
98 geometry_ = CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(), | |
99 geometry_.GetAxisX(), | |
100 geometry_.GetAxisY()); | |
101 | |
102 return true; | |
103 } | |
104 | |
105 | |
28 bool Slice::ParseOrthancFrame(const OrthancPlugins::IDicomDataset& dataset, | 106 bool Slice::ParseOrthancFrame(const OrthancPlugins::IDicomDataset& dataset, |
29 const std::string& instanceId, | 107 const std::string& instanceId, |
30 unsigned int frame) | 108 unsigned int frame) |
31 { | 109 { |
110 orthancInstanceId_ = instanceId; | |
111 frame_ = frame; | |
112 type_ = Type_OrthancDecodableFrame; | |
113 | |
32 OrthancPlugins::DicomDatasetReader reader(dataset); | 114 OrthancPlugins::DicomDatasetReader reader(dataset); |
33 | 115 |
34 unsigned int frameCount; | 116 sopClassUid_ = reader.GetStringValue(OrthancPlugins::DICOM_TAG_SOP_CLASS_UID, ""); |
35 if (!reader.GetUnsignedIntegerValue(frameCount, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES)) | 117 if (sopClassUid_.empty()) |
36 { | 118 { |
37 frameCount = 1; // Assume instance with one frame | 119 LOG(ERROR) << "Instance without a SOP class UID"; |
38 } | 120 return false; |
39 | 121 } |
40 if (frame >= frameCount) | 122 |
41 { | 123 if (!reader.GetUnsignedIntegerValue(frameCount_, OrthancPlugins::DICOM_TAG_NUMBER_OF_FRAMES)) |
42 return false; | 124 { |
43 } | 125 frameCount_ = 1; // Assume instance with one frame |
44 | 126 } |
45 if (!reader.GetDoubleValue(thickness_, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS)) | 127 |
46 { | 128 if (frame >= frameCount_) |
47 thickness_ = 100.0 * std::numeric_limits<double>::epsilon(); | 129 { |
48 } | 130 return false; |
131 } | |
132 | |
133 if (!reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) || | |
134 !reader.GetUnsignedIntegerValue(height_, OrthancPlugins::DICOM_TAG_ROWS)) | |
135 { | |
136 return false; | |
137 } | |
138 | |
139 thickness_ = 100.0 * std::numeric_limits<double>::epsilon(); | |
140 | |
141 std::string tmp; | |
142 if (dataset.GetStringValue(tmp, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS)) | |
143 { | |
144 if (!tmp.empty() && | |
145 !ParseDouble(thickness_, tmp)) | |
146 { | |
147 return false; // Syntax error | |
148 } | |
149 } | |
150 | |
151 converter_.ReadParameters(dataset); | |
49 | 152 |
50 GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); | 153 GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); |
51 | 154 |
52 std::string position, orientation; | 155 std::string position, orientation; |
53 if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && | 156 if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && |
54 dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) | 157 dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) |
55 { | 158 { |
56 geometry_ = CoordinateSystem3D(position, orientation); | 159 geometry_ = CoordinateSystem3D(position, orientation); |
57 } | 160 |
58 | 161 bool ok = true; |
59 if (reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) && | 162 SopClassUid tmp; |
60 reader.GetUnsignedIntegerValue(height_, OrthancPlugins::DICOM_TAG_ROWS)) | 163 |
61 { | 164 if (StringToSopClassUid(tmp, sopClassUid_)) |
62 orthancInstanceId_ = instanceId; | 165 { |
63 frame_ = frame; | 166 switch (tmp) |
64 converter_.ReadParameters(dataset); | 167 { |
65 | 168 case SopClassUid_RTDose: |
66 type_ = Type_OrthancInstance; | 169 type_ = Type_OrthancRawFrame; |
67 return true; | 170 ok = ComputeRTDoseGeometry(reader, frame); |
171 break; | |
172 | |
173 default: | |
174 break; | |
175 } | |
176 } | |
177 | |
178 if (!ok) | |
179 { | |
180 LOG(ERROR) << "Cannot deduce the 3D location of frame " << frame | |
181 << " in instance " << instanceId << ", whose SOP class UID is: " << sopClassUid_; | |
182 return false; | |
183 } | |
184 } | |
185 | |
186 return true; | |
187 } | |
188 | |
189 | |
190 const std::string Slice::GetOrthancInstanceId() const | |
191 { | |
192 if (type_ == Type_OrthancDecodableFrame || | |
193 type_ == Type_OrthancRawFrame) | |
194 { | |
195 return orthancInstanceId_; | |
68 } | 196 } |
69 else | 197 else |
70 { | 198 { |
71 return false; | 199 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
72 } | 200 } |
73 } | |
74 | |
75 | |
76 const std::string Slice::GetOrthancInstanceId() const | |
77 { | |
78 if (type_ != Type_OrthancInstance) | |
79 { | |
80 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
81 } | |
82 | |
83 return orthancInstanceId_; | |
84 } | 201 } |
85 | 202 |
86 | 203 |
87 unsigned int Slice::GetFrame() const | 204 unsigned int Slice::GetFrame() const |
88 { | 205 { |