Mercurial > hg > orthanc-stone
annotate OrthancStone/Sources/Toolbox/SubvoxelReader.h @ 1783:75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 14 May 2021 18:30:24 +0200 |
parents | 9ac2a65d4172 |
children | 3889ae96d2e9 |
rev | line source |
---|---|
183 | 1 /** |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
1739
9ac2a65d4172
upgrade to year 2021
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1598
diff
changeset
|
5 * Copyright (C) 2017-2021 Osimis S.A., Belgium |
183 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
1598
8563ea5d8ae4
relicensing some files, cf. osimis bm26 and chu agreement on 2020-05-20
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1596
diff
changeset
|
8 * modify it under the terms of the GNU Lesser General Public License |
183 | 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 | |
1598
8563ea5d8ae4
relicensing some files, cf. osimis bm26 and chu agreement on 2020-05-20
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1596
diff
changeset
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
8563ea5d8ae4
relicensing some files, cf. osimis bm26 and chu agreement on 2020-05-20
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1596
diff
changeset
|
15 * Lesser General Public License for more details. |
1596
4fb8fdf03314
removed annoying whitespace
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1571
diff
changeset
|
16 * |
1598
8563ea5d8ae4
relicensing some files, cf. osimis bm26 and chu agreement on 2020-05-20
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1596
diff
changeset
|
17 * You should have received a copy of the GNU Lesser General Public |
8563ea5d8ae4
relicensing some files, cf. osimis bm26 and chu agreement on 2020-05-20
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1596
diff
changeset
|
18 * License along with this program. If not, see |
8563ea5d8ae4
relicensing some files, cf. osimis bm26 and chu agreement on 2020-05-20
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1596
diff
changeset
|
19 * <http://www.gnu.org/licenses/>. |
183 | 20 **/ |
21 | |
22 | |
23 #pragma once | |
24 | |
25 #include "../Volumes/ImageBuffer3D.h" | |
26 #include "GeometryToolbox.h" | |
27 | |
1455
30deba7bc8e2
simplifying include_directories
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1270
diff
changeset
|
28 #include <Images/ImageTraits.h> |
183 | 29 |
30 #include <boost/noncopyable.hpp> | |
31 #include <cmath> | |
32 | |
33 namespace OrthancStone | |
34 { | |
35 namespace Internals | |
36 { | |
37 class SubvoxelReaderBase : public boost::noncopyable | |
38 { | |
39 private: | |
40 const ImageBuffer3D& source_; | |
41 unsigned int width_; | |
42 unsigned int height_; | |
43 unsigned int depth_; | |
44 | |
45 public: | |
1571 | 46 explicit SubvoxelReaderBase(const ImageBuffer3D& source) : |
183 | 47 source_(source), |
48 width_(source.GetWidth()), | |
49 height_(source.GetHeight()), | |
50 depth_(source.GetDepth()) | |
51 { | |
52 } | |
53 | |
54 ORTHANC_FORCE_INLINE | |
55 const Orthanc::ImageAccessor& GetSource() const | |
56 { | |
57 return source_.GetInternalImage(); | |
58 } | |
59 | |
60 ORTHANC_FORCE_INLINE | |
61 unsigned int GetWidth() const | |
62 { | |
63 return width_; | |
64 } | |
65 | |
66 ORTHANC_FORCE_INLINE | |
67 unsigned int GetHeight() const | |
68 { | |
69 return height_; | |
70 } | |
71 | |
72 ORTHANC_FORCE_INLINE | |
73 unsigned int GetDepth() const | |
74 { | |
75 return depth_; | |
76 } | |
77 | |
78 ORTHANC_FORCE_INLINE | |
79 unsigned int ComputeRow(unsigned int y, | |
80 unsigned int z) const | |
81 { | |
1783
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
82 /** |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
83 * The "(depth_ - 1 - z)" comes from the fact that |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
84 * "ImageBuffer3D" class stores its slices in DECREASING |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
85 * z-order along the normal. This computation makes the |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
86 * "SubvoxelReader" class use the same convention as |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
87 * "ImageBuffer3D::GetVoxelXXX()". |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
88 * |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
89 * WARNING: Until changeset 1782:f053c80ea411, "z" was |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
90 * directly used, causing this class to have a slice order |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
91 * that was reversed between "SubvoxelReader" and |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
92 * "ImageBuffer3D". This notably made |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
93 * "DicomVolumeImageMPRSlicer" and "DicomVolumeImageReslicer" |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
94 * inconsistent in sagittal and coronal views (the texture was |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
95 * flipped along the Y-axis in the canvas). |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
96 **/ |
75d3e2ab1fe1
BREAKING: SubvoxelReader using the same Z-axis ordering as ImageBuffer3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1739
diff
changeset
|
97 return (depth_ - 1 - z) * height_ + y; |
183 | 98 } |
99 }; | |
100 } | |
101 | |
102 | |
103 template <Orthanc::PixelFormat Format, | |
104 ImageInterpolation Interpolation> | |
105 class SubvoxelReader; | |
106 | |
107 | |
108 template <Orthanc::PixelFormat Format> | |
109 class SubvoxelReader<Format, ImageInterpolation_Nearest> : | |
110 public Internals::SubvoxelReaderBase | |
111 { | |
112 public: | |
113 typedef Orthanc::PixelTraits<Format> Traits; | |
114 typedef typename Traits::PixelType PixelType; | |
115 | |
1571 | 116 explicit SubvoxelReader(const ImageBuffer3D& source) : |
183 | 117 SubvoxelReaderBase(source) |
118 { | |
119 } | |
120 | |
121 inline bool GetValue(PixelType& target, | |
122 float x, | |
123 float y, | |
124 float z) const; | |
125 | |
126 inline bool GetFloatValue(float& target, | |
127 float x, | |
128 float y, | |
129 float z) const; | |
130 }; | |
131 | |
132 | |
133 template <Orthanc::PixelFormat Format> | |
134 class SubvoxelReader<Format, ImageInterpolation_Bilinear> : | |
135 public Internals::SubvoxelReaderBase | |
136 { | |
137 public: | |
138 typedef Orthanc::PixelTraits<Format> Traits; | |
139 typedef typename Traits::PixelType PixelType; | |
140 | |
1571 | 141 explicit SubvoxelReader(const ImageBuffer3D& source) : |
183 | 142 SubvoxelReaderBase(source) |
143 { | |
144 } | |
145 | |
146 inline bool Sample(float& f00, | |
147 float& f01, | |
148 float& f10, | |
149 float& f11, | |
150 unsigned int ux, | |
151 unsigned int uy, | |
152 unsigned int uz) const; | |
153 | |
154 inline bool GetValue(PixelType& target, | |
155 float x, | |
156 float y, | |
157 float z) const; | |
158 | |
159 inline bool GetFloatValue(float& target, | |
160 float x, | |
161 float y, | |
162 float z) const; | |
163 }; | |
164 | |
165 | |
166 template <Orthanc::PixelFormat Format> | |
167 class SubvoxelReader<Format, ImageInterpolation_Trilinear> : | |
168 public Internals::SubvoxelReaderBase | |
169 { | |
170 private: | |
171 SubvoxelReader<Format, ImageInterpolation_Bilinear> bilinear_; | |
172 | |
173 public: | |
174 typedef Orthanc::PixelTraits<Format> Traits; | |
175 typedef typename Traits::PixelType PixelType; | |
176 | |
1571 | 177 explicit SubvoxelReader(const ImageBuffer3D& source) : |
183 | 178 SubvoxelReaderBase(source), |
179 bilinear_(source) | |
180 { | |
181 } | |
182 | |
183 inline bool GetValue(PixelType& target, | |
184 float x, | |
185 float y, | |
186 float z) const; | |
187 | |
188 inline bool GetFloatValue(float& target, | |
189 float x, | |
190 float y, | |
191 float z) const; | |
192 }; | |
193 | |
194 | |
195 template <Orthanc::PixelFormat Format> | |
196 bool SubvoxelReader<Format, ImageInterpolation_Nearest>::GetValue(PixelType& target, | |
197 float x, | |
198 float y, | |
199 float z) const | |
200 { | |
201 if (x < 0 || | |
202 y < 0 || | |
203 z < 0) | |
204 { | |
205 return false; | |
206 } | |
207 else | |
208 { | |
209 unsigned int ux = static_cast<unsigned int>(std::floor(x)); | |
210 unsigned int uy = static_cast<unsigned int>(std::floor(y)); | |
211 unsigned int uz = static_cast<unsigned int>(std::floor(z)); | |
212 | |
213 if (ux < GetWidth() && | |
214 uy < GetHeight() && | |
215 uz < GetDepth()) | |
216 { | |
217 Orthanc::ImageTraits<Format>::GetPixel(target, GetSource(), ux, ComputeRow(uy, uz)); | |
218 return true; | |
219 } | |
220 else | |
221 { | |
222 return false; | |
223 } | |
224 } | |
225 } | |
226 | |
227 | |
228 template <Orthanc::PixelFormat Format> | |
229 bool SubvoxelReader<Format, ImageInterpolation_Nearest>::GetFloatValue(float& target, | |
230 float x, | |
231 float y, | |
232 float z) const | |
233 { | |
234 PixelType value; | |
235 | |
236 if (GetValue(value, x, y, z)) | |
237 { | |
238 target = Traits::PixelToFloat(value); | |
239 return true; | |
240 } | |
241 else | |
242 { | |
243 return false; | |
244 } | |
245 } | |
246 | |
247 | |
248 template <Orthanc::PixelFormat Format> | |
249 bool SubvoxelReader<Format, ImageInterpolation_Bilinear>::Sample(float& f00, | |
250 float& f01, | |
251 float& f10, | |
252 float& f11, | |
253 unsigned int ux, | |
254 unsigned int uy, | |
255 unsigned int uz) const | |
256 { | |
257 if (ux < GetWidth() && | |
258 uy < GetHeight() && | |
259 uz < GetDepth()) | |
260 { | |
261 f00 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux, ComputeRow(uy, uz)); | |
262 } | |
263 else | |
264 { | |
265 // Pixel is out of the volume | |
266 return false; | |
267 } | |
268 | |
269 if (ux + 1 < GetWidth()) | |
270 { | |
271 f01 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux + 1, ComputeRow(uy, uz)); | |
272 } | |
273 else | |
274 { | |
275 f01 = f00; | |
276 } | |
277 | |
278 if (uy + 1 < GetHeight()) | |
279 { | |
280 f10 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux, ComputeRow(uy + 1, uz)); | |
281 } | |
282 else | |
283 { | |
284 f10 = f00; | |
285 } | |
286 | |
287 if (ux + 1 < GetWidth() && | |
288 uy + 1 < GetHeight()) | |
289 { | |
290 f11 = Orthanc::ImageTraits<Format>::GetFloatPixel(GetSource(), ux + 1, ComputeRow(uy + 1, uz)); | |
291 } | |
292 else | |
293 { | |
294 f11 = f00; | |
295 } | |
296 | |
297 return true; | |
298 } | |
299 | |
300 | |
301 template <Orthanc::PixelFormat Format> | |
302 bool SubvoxelReader<Format, ImageInterpolation_Bilinear>::GetFloatValue(float& target, | |
303 float x, | |
304 float y, | |
305 float z) const | |
306 { | |
307 x -= 0.5f; | |
308 y -= 0.5f; | |
309 | |
310 if (x < 0 || | |
311 y < 0 || | |
312 z < 0) | |
313 { | |
314 return false; | |
315 } | |
316 else | |
317 { | |
318 unsigned int ux = static_cast<unsigned int>(std::floor(x)); | |
319 unsigned int uy = static_cast<unsigned int>(std::floor(y)); | |
320 unsigned int uz = static_cast<unsigned int>(std::floor(z)); | |
321 | |
322 float f00, f01, f10, f11; | |
323 if (Sample(f00, f01, f10, f11, ux, uy, uz)) | |
324 { | |
325 float ax = x - static_cast<float>(ux); | |
326 float ay = y - static_cast<float>(uy); | |
327 | |
328 target = GeometryToolbox::ComputeBilinearInterpolationUnitSquare(ax, ay, f00, f01, f10, f11); | |
329 return true; | |
330 } | |
331 else | |
332 { | |
333 return false; | |
334 } | |
335 } | |
336 } | |
337 | |
338 | |
339 template <Orthanc::PixelFormat Format> | |
340 bool SubvoxelReader<Format, ImageInterpolation_Bilinear>::GetValue(PixelType& target, | |
341 float x, | |
342 float y, | |
343 float z) const | |
344 { | |
345 float value; | |
346 | |
347 if (GetFloatValue(value, x, y, z)) | |
348 { | |
349 Traits::FloatToPixel(target, value); | |
350 return true; | |
351 } | |
352 else | |
353 { | |
354 return false; | |
355 } | |
356 } | |
357 | |
358 | |
359 template <Orthanc::PixelFormat Format> | |
360 bool SubvoxelReader<Format, ImageInterpolation_Trilinear>::GetFloatValue(float& target, | |
361 float x, | |
362 float y, | |
363 float z) const | |
364 { | |
365 x -= 0.5f; | |
366 y -= 0.5f; | |
367 z -= 0.5f; | |
368 | |
369 if (x < 0 || | |
370 y < 0 || | |
371 z < 0) | |
372 { | |
373 return false; | |
374 } | |
375 else | |
376 { | |
377 unsigned int ux = static_cast<unsigned int>(std::floor(x)); | |
378 unsigned int uy = static_cast<unsigned int>(std::floor(y)); | |
379 unsigned int uz = static_cast<unsigned int>(std::floor(z)); | |
380 | |
381 float f000, f001, f010, f011; | |
382 if (bilinear_.Sample(f000, f001, f010, f011, ux, uy, uz)) | |
383 { | |
384 const float ax = x - static_cast<float>(ux); | |
385 const float ay = y - static_cast<float>(uy); | |
386 | |
387 float f100, f101, f110, f111; | |
388 | |
389 if (bilinear_.Sample(f100, f101, f110, f111, ux, uy, uz + 1)) | |
390 { | |
391 const float az = z - static_cast<float>(uz); | |
392 target = GeometryToolbox::ComputeTrilinearInterpolationUnitSquare | |
393 (ax, ay, az, f000, f001, f010, f011, f100, f101, f110, f111); | |
394 } | |
395 else | |
396 { | |
397 target = GeometryToolbox::ComputeBilinearInterpolationUnitSquare | |
398 (ax, ay, f000, f001, f010, f011); | |
399 } | |
400 | |
401 return true; | |
402 } | |
403 else | |
404 { | |
405 return false; | |
406 } | |
407 } | |
408 } | |
409 | |
410 | |
411 template <Orthanc::PixelFormat Format> | |
412 bool SubvoxelReader<Format, ImageInterpolation_Trilinear>::GetValue(PixelType& target, | |
413 float x, | |
414 float y, | |
415 float z) const | |
416 { | |
417 float value; | |
418 | |
419 if (GetFloatValue(value, x, y, z)) | |
420 { | |
421 Traits::FloatToPixel(target, value); | |
422 return true; | |
423 } | |
424 else | |
425 { | |
426 return false; | |
427 } | |
428 } | |
429 } |