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