Mercurial > hg > orthanc-stone
comparison OrthancStone/Sources/Volumes/VolumeImageGeometry.cpp @ 1512:244ad1e4e76a
reorganization of folders
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 07 Jul 2020 16:21:02 +0200 |
parents | Framework/Volumes/VolumeImageGeometry.cpp@30deba7bc8e2 |
children | 8563ea5d8ae4 |
comparison
equal
deleted
inserted
replaced
1511:9dfeee74c1e6 | 1512:244ad1e4e76a |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "VolumeImageGeometry.h" | |
23 | |
24 #include "../Toolbox/GeometryToolbox.h" | |
25 | |
26 #include <OrthancException.h> | |
27 | |
28 | |
29 namespace OrthancStone | |
30 { | |
31 void VolumeImageGeometry::Invalidate() | |
32 { | |
33 Vector p = (axialGeometry_.GetOrigin() + | |
34 static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal()); | |
35 | |
36 coronalGeometry_ = CoordinateSystem3D(p, | |
37 axialGeometry_.GetAxisX(), | |
38 -axialGeometry_.GetNormal()); | |
39 | |
40 sagittalGeometry_ = CoordinateSystem3D(p, | |
41 axialGeometry_.GetAxisY(), | |
42 -axialGeometry_.GetNormal()); | |
43 | |
44 Vector origin = ( | |
45 axialGeometry_.MapSliceToWorldCoordinates(-0.5 * voxelDimensions_[0], | |
46 -0.5 * voxelDimensions_[1]) - | |
47 0.5 * voxelDimensions_[2] * axialGeometry_.GetNormal()); | |
48 | |
49 LOG(TRACE) << "VolumeImageGeometry::Invalidate() origin = " << origin(0) << "," << origin(1) << "," << origin(2) << " | width_ = " << width_ << " | height_ = " << height_ << " | depth_ = " << depth_; | |
50 | |
51 Vector scaling; | |
52 | |
53 if (width_ == 0 || | |
54 height_ == 0 || | |
55 depth_ == 0) | |
56 { | |
57 LinearAlgebra::AssignVector(scaling, 1, 1, 1); | |
58 } | |
59 else | |
60 { | |
61 scaling = ( | |
62 axialGeometry_.GetAxisX() * voxelDimensions_[0] * static_cast<double>(width_) + | |
63 axialGeometry_.GetAxisY() * voxelDimensions_[1] * static_cast<double>(height_) + | |
64 axialGeometry_.GetNormal() * voxelDimensions_[2] * static_cast<double>(depth_)); | |
65 } | |
66 | |
67 transform_ = LinearAlgebra::Product( | |
68 GeometryToolbox::CreateTranslationMatrix(origin[0], origin[1], origin[2]), | |
69 GeometryToolbox::CreateScalingMatrix(scaling[0], scaling[1], scaling[2])); | |
70 | |
71 LinearAlgebra::InvertMatrix(transformInverse_, transform_); | |
72 } | |
73 | |
74 | |
75 VolumeImageGeometry::VolumeImageGeometry() : | |
76 width_(0), | |
77 height_(0), | |
78 depth_(0) | |
79 { | |
80 LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1); | |
81 Invalidate(); | |
82 } | |
83 | |
84 | |
85 void VolumeImageGeometry::SetSizeInVoxels(unsigned int width, | |
86 unsigned int height, | |
87 unsigned int depth) | |
88 { | |
89 width_ = width; | |
90 height_ = height; | |
91 depth_ = depth; | |
92 Invalidate(); | |
93 } | |
94 | |
95 | |
96 void VolumeImageGeometry::SetAxialGeometry(const CoordinateSystem3D& geometry) | |
97 { | |
98 axialGeometry_ = geometry; | |
99 Invalidate(); | |
100 } | |
101 | |
102 | |
103 void VolumeImageGeometry::SetVoxelDimensions(double x, | |
104 double y, | |
105 double z) | |
106 { | |
107 if (x <= 0 || | |
108 y <= 0 || | |
109 z <= 0) | |
110 { | |
111 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
112 } | |
113 else | |
114 { | |
115 LinearAlgebra::AssignVector(voxelDimensions_, x, y, z); | |
116 Invalidate(); | |
117 } | |
118 } | |
119 | |
120 | |
121 const CoordinateSystem3D& VolumeImageGeometry::GetProjectionGeometry(VolumeProjection projection) const | |
122 { | |
123 switch (projection) | |
124 { | |
125 case VolumeProjection_Axial: | |
126 return axialGeometry_; | |
127 | |
128 case VolumeProjection_Coronal: | |
129 return coronalGeometry_; | |
130 | |
131 case VolumeProjection_Sagittal: | |
132 return sagittalGeometry_; | |
133 | |
134 default: | |
135 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
136 } | |
137 } | |
138 | |
139 | |
140 Vector VolumeImageGeometry::GetVoxelDimensions(VolumeProjection projection) const | |
141 { | |
142 switch (projection) | |
143 { | |
144 case VolumeProjection_Axial: | |
145 return voxelDimensions_; | |
146 | |
147 case VolumeProjection_Coronal: | |
148 return LinearAlgebra::CreateVector(voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); | |
149 | |
150 case VolumeProjection_Sagittal: | |
151 return LinearAlgebra::CreateVector(voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); | |
152 | |
153 default: | |
154 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
155 } | |
156 } | |
157 | |
158 | |
159 unsigned int VolumeImageGeometry::GetProjectionWidth(VolumeProjection projection) const | |
160 { | |
161 switch (projection) | |
162 { | |
163 case VolumeProjection_Axial: | |
164 return width_; | |
165 | |
166 case VolumeProjection_Coronal: | |
167 return width_; | |
168 | |
169 case VolumeProjection_Sagittal: | |
170 return height_; | |
171 | |
172 default: | |
173 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
174 } | |
175 } | |
176 | |
177 | |
178 unsigned int VolumeImageGeometry::GetProjectionHeight(VolumeProjection projection) const | |
179 { | |
180 switch (projection) | |
181 { | |
182 case VolumeProjection_Axial: | |
183 return height_; | |
184 | |
185 case VolumeProjection_Coronal: | |
186 return depth_; | |
187 | |
188 case VolumeProjection_Sagittal: | |
189 return depth_; | |
190 | |
191 default: | |
192 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
193 } | |
194 } | |
195 | |
196 | |
197 unsigned int VolumeImageGeometry::GetProjectionDepth(VolumeProjection projection) const | |
198 { | |
199 switch (projection) | |
200 { | |
201 case VolumeProjection_Axial: | |
202 return depth_; | |
203 | |
204 case VolumeProjection_Coronal: | |
205 return height_; | |
206 | |
207 case VolumeProjection_Sagittal: | |
208 return width_; | |
209 | |
210 default: | |
211 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
212 } | |
213 } | |
214 | |
215 | |
216 Vector VolumeImageGeometry::GetCoordinates(float x, | |
217 float y, | |
218 float z) const | |
219 { | |
220 Vector p = LinearAlgebra::Product(transform_, LinearAlgebra::CreateVector(x, y, z, 1)); | |
221 | |
222 assert(LinearAlgebra::IsNear(p[3], 1)); // Affine transform, no perspective effect | |
223 | |
224 // Back to non-homogeneous coordinates | |
225 return LinearAlgebra::CreateVector(p[0], p[1], p[2]); | |
226 } | |
227 | |
228 | |
229 bool VolumeImageGeometry::DetectProjection(VolumeProjection& projection, | |
230 const Vector& planeNormal) const | |
231 { | |
232 if (GeometryToolbox::IsParallel(planeNormal, axialGeometry_.GetNormal())) | |
233 { | |
234 projection = VolumeProjection_Axial; | |
235 return true; | |
236 } | |
237 else if (GeometryToolbox::IsParallel(planeNormal, coronalGeometry_.GetNormal())) | |
238 { | |
239 projection = VolumeProjection_Coronal; | |
240 return true; | |
241 } | |
242 else if (GeometryToolbox::IsParallel(planeNormal, sagittalGeometry_.GetNormal())) | |
243 { | |
244 projection = VolumeProjection_Sagittal; | |
245 return true; | |
246 } | |
247 else | |
248 { | |
249 return false; | |
250 } | |
251 } | |
252 | |
253 | |
254 bool VolumeImageGeometry::DetectSlice(VolumeProjection& projection, | |
255 unsigned int& slice, | |
256 const CoordinateSystem3D& plane) const | |
257 { | |
258 if (!DetectProjection(projection, plane.GetNormal())) | |
259 { | |
260 return false; | |
261 } | |
262 | |
263 // Transforms the coordinates of the origin of the plane, into the | |
264 // coordinates of the axial geometry | |
265 const Vector& origin = plane.GetOrigin(); | |
266 Vector p = LinearAlgebra::Product( | |
267 transformInverse_, | |
268 LinearAlgebra::CreateVector(origin[0], origin[1], origin[2], 1)); | |
269 | |
270 assert(LinearAlgebra::IsNear(p[3], 1)); | |
271 | |
272 double z; | |
273 | |
274 switch (projection) | |
275 { | |
276 case VolumeProjection_Axial: | |
277 z = p[2]; | |
278 break; | |
279 | |
280 case VolumeProjection_Coronal: | |
281 z = p[1]; | |
282 break; | |
283 | |
284 case VolumeProjection_Sagittal: | |
285 z = p[0]; | |
286 break; | |
287 | |
288 default: | |
289 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
290 } | |
291 | |
292 const unsigned int projectionDepth = GetProjectionDepth(projection); | |
293 | |
294 z *= static_cast<double>(projectionDepth); | |
295 if (z < 0) | |
296 { | |
297 return false; | |
298 } | |
299 else | |
300 { | |
301 unsigned int d = static_cast<unsigned int>(std::floor(z)); | |
302 if (d >= projectionDepth) | |
303 { | |
304 return false; | |
305 } | |
306 else | |
307 { | |
308 slice = d; | |
309 return true; | |
310 } | |
311 } | |
312 } | |
313 | |
314 | |
315 CoordinateSystem3D VolumeImageGeometry::GetProjectionSlice(VolumeProjection projection, | |
316 unsigned int z) const | |
317 { | |
318 if (z >= GetProjectionDepth(projection)) | |
319 { | |
320 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
321 } | |
322 | |
323 Vector dim = GetVoxelDimensions(projection); | |
324 CoordinateSystem3D plane = GetProjectionGeometry(projection); | |
325 | |
326 Vector normal = plane.GetNormal(); | |
327 if (projection == VolumeProjection_Sagittal) | |
328 { | |
329 /** | |
330 * WARNING: In sagittal geometry, the normal points to REDUCING | |
331 * X-axis in the 3D world. This is necessary to keep the | |
332 * right-hand coordinate system. Hence the negation. | |
333 **/ | |
334 normal = -normal; | |
335 } | |
336 | |
337 plane.SetOrigin(plane.GetOrigin() + static_cast<double>(z) * dim[2] * normal); | |
338 | |
339 return plane; | |
340 } | |
341 | |
342 std::ostream& operator<<(std::ostream& s, const VolumeImageGeometry& v) | |
343 { | |
344 s << "width: " << v.width_ << " height: " << v.height_ | |
345 << " depth: " << v.depth_ | |
346 << " axialGeometry: " << v.axialGeometry_ | |
347 << " coronalGeometry: " << v.coronalGeometry_ | |
348 << " sagittalGeometry: " << v.sagittalGeometry_ | |
349 << " voxelDimensions_: " << v.voxelDimensions_ | |
350 << " height: " << v.height_ | |
351 << " transform: " << v.transform_ | |
352 << " transformInverse: " << v.transformInverse_; | |
353 return s; | |
354 } | |
355 | |
356 } |