Mercurial > hg > orthanc-stone
comparison Framework/Volumes/ImageBuffer3D.cpp @ 318:3a4ca166fafa am-2
ImageAccessor refactoring + implemented Image Cache in SmartLoader
author | am@osimis.io |
---|---|
date | Mon, 08 Oct 2018 17:10:08 +0200 |
parents | 5412adf19980 |
children | 557c8ff1db5c |
comparison
equal
deleted
inserted
replaced
317:b66d13708f40 | 318:3a4ca166fafa |
---|---|
11 * | 11 * |
12 * This program is distributed in the hope that it will be useful, but | 12 * This program is distributed in the hope that it will be useful, but |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | 13 * WITHOUT ANY WARRANTY; without even the implied warranty of |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 * Affero General Public License for more details. | 15 * Affero General Public License for more details. |
16 * | 16 * |
17 * You should have received a copy of the GNU Affero General Public License | 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/>. | 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 **/ | 19 **/ |
20 | 20 |
21 | 21 |
27 | 27 |
28 #include <string.h> | 28 #include <string.h> |
29 | 29 |
30 namespace OrthancStone | 30 namespace OrthancStone |
31 { | 31 { |
32 Orthanc::ImageAccessor ImageBuffer3D::GetAxialSliceAccessor(unsigned int slice, | 32 void ImageBuffer3D::GetAxialSliceAccessor(Orthanc::ImageAccessor& target, |
33 bool readOnly) const | 33 unsigned int slice, |
34 bool readOnly) const | |
34 { | 35 { |
35 if (slice >= depth_) | 36 if (slice >= depth_) |
36 { | 37 { |
37 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 38 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
38 } | 39 } |
39 | 40 |
40 Orthanc::ImageAccessor accessor; | |
41 | |
42 if (readOnly) | 41 if (readOnly) |
43 { | 42 { |
44 accessor.AssignReadOnly(format_, width_, height_, image_.GetPitch(), | 43 target.AssignReadOnly(format_, width_, height_, image_.GetPitch(), |
45 image_.GetConstRow(height_ * (depth_ - 1 - slice))); | 44 image_.GetConstRow(height_ * (depth_ - 1 - slice))); |
46 } | 45 } |
47 else | 46 else |
48 { | 47 { |
49 accessor.AssignWritable(format_, width_, height_, image_.GetPitch(), | 48 target.AssignWritable(format_, width_, height_, image_.GetPitch(), |
50 image_.GetRow(height_ * (depth_ - 1 - slice))); | 49 image_.GetRow(height_ * (depth_ - 1 - slice))); |
51 } | 50 } |
52 | 51 } |
53 return accessor; | 52 |
54 } | 53 |
55 | 54 void ImageBuffer3D::GetCoronalSliceAccessor(Orthanc::ImageAccessor& target, |
56 | 55 unsigned int slice, |
57 Orthanc::ImageAccessor ImageBuffer3D::GetCoronalSliceAccessor(unsigned int slice, | 56 bool readOnly) const |
58 bool readOnly) const | |
59 { | 57 { |
60 if (slice >= height_) | 58 if (slice >= height_) |
61 { | 59 { |
62 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 60 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
63 } | 61 } |
64 | 62 |
65 Orthanc::ImageAccessor accessor; | |
66 | |
67 if (readOnly) | 63 if (readOnly) |
68 { | 64 { |
69 accessor.AssignReadOnly(format_, width_, depth_, image_.GetPitch() * height_, | 65 target.AssignReadOnly(format_, width_, depth_, image_.GetPitch() * height_, |
70 image_.GetConstRow(slice)); | 66 image_.GetConstRow(slice)); |
71 } | 67 } |
72 else | 68 else |
73 { | 69 { |
74 accessor.AssignWritable(format_, width_, depth_, image_.GetPitch() * height_, | 70 target.AssignWritable(format_, width_, depth_, image_.GetPitch() * height_, |
75 image_.GetRow(slice)); | 71 image_.GetRow(slice)); |
76 } | 72 } |
77 | |
78 return accessor; | |
79 } | 73 } |
80 | 74 |
81 | 75 |
82 Orthanc::Image* ImageBuffer3D::ExtractSagittalSlice(unsigned int slice) const | 76 Orthanc::Image* ImageBuffer3D::ExtractSagittalSlice(unsigned int slice) const |
83 { | 77 { |
95 //uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(depth_ - 1 - z)); | 89 //uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(depth_ - 1 - z)); |
96 uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(z)); | 90 uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(z)); |
97 | 91 |
98 for (unsigned int y = 0; y < height_; y++) | 92 for (unsigned int y = 0; y < height_; y++) |
99 { | 93 { |
100 const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + | 94 const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + |
101 bytesPerPixel * slice); | 95 bytesPerPixel * slice); |
102 | 96 |
103 memcpy(target, source, bytesPerPixel); | 97 memcpy(target, source, bytesPerPixel); |
104 target += bytesPerPixel; | 98 target += bytesPerPixel; |
105 } | 99 } |
161 Vector ImageBuffer3D::GetVoxelDimensions(VolumeProjection projection) const | 155 Vector ImageBuffer3D::GetVoxelDimensions(VolumeProjection projection) const |
162 { | 156 { |
163 Vector result; | 157 Vector result; |
164 switch (projection) | 158 switch (projection) |
165 { | 159 { |
166 case VolumeProjection_Axial: | 160 case VolumeProjection_Axial: |
167 result = voxelDimensions_; | 161 result = voxelDimensions_; |
168 break; | 162 break; |
169 | 163 |
170 case VolumeProjection_Coronal: | 164 case VolumeProjection_Coronal: |
171 LinearAlgebra::AssignVector(result, voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); | 165 LinearAlgebra::AssignVector(result, voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); |
172 break; | 166 break; |
173 | 167 |
174 case VolumeProjection_Sagittal: | 168 case VolumeProjection_Sagittal: |
175 LinearAlgebra::AssignVector(result, voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); | 169 LinearAlgebra::AssignVector(result, voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); |
176 break; | 170 break; |
177 | 171 |
178 default: | 172 default: |
179 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 173 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
180 } | 174 } |
181 | 175 |
182 return result; | 176 return result; |
183 } | 177 } |
184 | 178 |
187 unsigned int& height, | 181 unsigned int& height, |
188 VolumeProjection projection) | 182 VolumeProjection projection) |
189 { | 183 { |
190 switch (projection) | 184 switch (projection) |
191 { | 185 { |
192 case VolumeProjection_Axial: | 186 case VolumeProjection_Axial: |
193 width = width_; | 187 width = width_; |
194 height = height_; | 188 height = height_; |
195 break; | 189 break; |
196 | 190 |
197 case VolumeProjection_Coronal: | 191 case VolumeProjection_Coronal: |
198 width = width_; | 192 width = width_; |
199 height = depth_; | 193 height = depth_; |
200 break; | 194 break; |
201 | 195 |
202 case VolumeProjection_Sagittal: | 196 case VolumeProjection_Sagittal: |
203 width = height_; | 197 width = height_; |
204 height = depth_; | 198 height = depth_; |
205 break; | 199 break; |
206 | 200 |
207 default: | 201 default: |
208 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 202 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
209 } | 203 } |
210 } | 204 } |
211 | 205 |
212 | 206 |
213 ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const | 207 ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const |
214 { | 208 { |
215 std::auto_ptr<ParallelSlices> result(new ParallelSlices); | 209 std::auto_ptr<ParallelSlices> result(new ParallelSlices); |
216 | 210 |
217 switch (projection) | 211 switch (projection) |
218 { | 212 { |
219 case VolumeProjection_Axial: | 213 case VolumeProjection_Axial: |
220 for (unsigned int z = 0; z < depth_; z++) | 214 for (unsigned int z = 0; z < depth_; z++) |
221 { | 215 { |
222 Vector origin = axialGeometry_.GetOrigin(); | 216 Vector origin = axialGeometry_.GetOrigin(); |
223 origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | 217 origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal(); |
224 | 218 |
225 result->AddSlice(origin, | 219 result->AddSlice(origin, |
226 axialGeometry_.GetAxisX(), | 220 axialGeometry_.GetAxisX(), |
227 axialGeometry_.GetAxisY()); | 221 axialGeometry_.GetAxisY()); |
228 } | 222 } |
229 break; | 223 break; |
230 | 224 |
231 case VolumeProjection_Coronal: | 225 case VolumeProjection_Coronal: |
232 for (unsigned int y = 0; y < height_; y++) | 226 for (unsigned int y = 0; y < height_; y++) |
233 { | 227 { |
234 Vector origin = axialGeometry_.GetOrigin(); | 228 Vector origin = axialGeometry_.GetOrigin(); |
235 origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY(); | 229 origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY(); |
236 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | 230 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); |
237 | 231 |
238 result->AddSlice(origin, | 232 result->AddSlice(origin, |
239 axialGeometry_.GetAxisX(), | 233 axialGeometry_.GetAxisX(), |
240 -axialGeometry_.GetNormal()); | 234 -axialGeometry_.GetNormal()); |
241 } | 235 } |
242 break; | 236 break; |
243 | 237 |
244 case VolumeProjection_Sagittal: | 238 case VolumeProjection_Sagittal: |
245 for (unsigned int x = 0; x < width_; x++) | 239 for (unsigned int x = 0; x < width_; x++) |
246 { | 240 { |
247 Vector origin = axialGeometry_.GetOrigin(); | 241 Vector origin = axialGeometry_.GetOrigin(); |
248 origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX(); | 242 origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX(); |
249 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | 243 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); |
250 | 244 |
251 result->AddSlice(origin, | 245 result->AddSlice(origin, |
252 axialGeometry_.GetAxisY(), | 246 axialGeometry_.GetAxisY(), |
253 -axialGeometry_.GetNormal()); | 247 -axialGeometry_.GetNormal()); |
254 } | 248 } |
255 break; | 249 break; |
256 | 250 |
257 default: | 251 default: |
258 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 252 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
259 } | 253 } |
260 | 254 |
261 return result.release(); | 255 return result.release(); |
262 } | 256 } |
263 | 257 |
264 | 258 |
265 uint64_t ImageBuffer3D::GetEstimatedMemorySize() const | 259 uint64_t ImageBuffer3D::GetEstimatedMemorySize() const |
266 { | 260 { |
267 return image_.GetPitch() * image_.GetHeight() * Orthanc::GetBytesPerPixel(format_); | 261 return image_.GetPitch() * image_.GetHeight() * Orthanc::GetBytesPerPixel(format_); |
268 } | 262 } |
276 { | 270 { |
277 return; | 271 return; |
278 } | 272 } |
279 | 273 |
280 float sliceMin, sliceMax; | 274 float sliceMin, sliceMax; |
281 | 275 |
282 switch (slice.GetFormat()) | 276 switch (slice.GetFormat()) |
283 { | 277 { |
284 case Orthanc::PixelFormat_Grayscale8: | 278 case Orthanc::PixelFormat_Grayscale8: |
285 case Orthanc::PixelFormat_Grayscale16: | 279 case Orthanc::PixelFormat_Grayscale16: |
286 case Orthanc::PixelFormat_Grayscale32: | 280 case Orthanc::PixelFormat_Grayscale32: |
287 case Orthanc::PixelFormat_SignedGrayscale16: | 281 case Orthanc::PixelFormat_SignedGrayscale16: |
288 { | 282 { |
289 int64_t a, b; | 283 int64_t a, b; |
290 Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, slice); | 284 Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, slice); |
291 sliceMin = static_cast<float>(a); | 285 sliceMin = static_cast<float>(a); |
292 sliceMax = static_cast<float>(b); | 286 sliceMax = static_cast<float>(b); |
293 break; | 287 break; |
294 } | 288 } |
295 | 289 |
296 case Orthanc::PixelFormat_Float32: | 290 case Orthanc::PixelFormat_Float32: |
297 Orthanc::ImageProcessing::GetMinMaxFloatValue(sliceMin, sliceMax, slice); | 291 Orthanc::ImageProcessing::GetMinMaxFloatValue(sliceMin, sliceMax, slice); |
298 break; | 292 break; |
299 | 293 |
300 default: | 294 default: |
301 return; | 295 return; |
302 } | 296 } |
303 | 297 |
304 if (hasRange_) | 298 if (hasRange_) |
305 { | 299 { |
306 minValue_ = std::min(minValue_, sliceMin); | 300 minValue_ = std::min(minValue_, sliceMin); |
357 VolumeProjection projection, | 351 VolumeProjection projection, |
358 unsigned int slice) | 352 unsigned int slice) |
359 { | 353 { |
360 switch (projection) | 354 switch (projection) |
361 { | 355 { |
362 case VolumeProjection_Axial: | 356 case VolumeProjection_Axial: |
363 accessor_ = that.GetAxialSliceAccessor(slice, true); | 357 that.GetAxialSliceAccessor(accessor_, slice, true); |
364 break; | 358 break; |
365 | 359 |
366 case VolumeProjection_Coronal: | 360 case VolumeProjection_Coronal: |
367 accessor_ = that.GetCoronalSliceAccessor(slice, true); | 361 that.GetCoronalSliceAccessor(accessor_, slice, true); |
368 break; | 362 break; |
369 | 363 |
370 case VolumeProjection_Sagittal: | 364 case VolumeProjection_Sagittal: |
371 sagittal_.reset(that.ExtractSagittalSlice(slice)); | 365 sagittal_.reset(that.ExtractSagittalSlice(slice)); |
372 accessor_ = *sagittal_; | 366 sagittal_->GetReadOnlyAccessor(accessor_); |
373 break; | 367 break; |
374 | 368 |
375 default: | 369 default: |
376 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 370 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
377 } | 371 } |
378 } | 372 } |
379 | 373 |
380 | 374 |
381 void ImageBuffer3D::SliceWriter::Flush() | 375 void ImageBuffer3D::SliceWriter::Flush() |
383 if (modified_) | 377 if (modified_) |
384 { | 378 { |
385 if (sagittal_.get() != NULL) | 379 if (sagittal_.get() != NULL) |
386 { | 380 { |
387 // TODO | 381 // TODO |
388 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | 382 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
389 } | 383 } |
390 | 384 |
391 // Update the dynamic range of the underlying image, if | 385 // Update the dynamic range of the underlying image, if |
392 // "computeRange_" is set to true | 386 // "computeRange_" is set to true |
393 that_.ExtendImageRange(accessor_); | 387 that_.ExtendImageRange(accessor_); |
401 that_(that), | 395 that_(that), |
402 modified_(false) | 396 modified_(false) |
403 { | 397 { |
404 switch (projection) | 398 switch (projection) |
405 { | 399 { |
406 case VolumeProjection_Axial: | 400 case VolumeProjection_Axial: |
407 accessor_ = that.GetAxialSliceAccessor(slice, false); | 401 that.GetAxialSliceAccessor(accessor_, slice, false); |
408 break; | 402 break; |
409 | 403 |
410 case VolumeProjection_Coronal: | 404 case VolumeProjection_Coronal: |
411 accessor_ = that.GetCoronalSliceAccessor(slice, false); | 405 that.GetCoronalSliceAccessor(accessor_, slice, false); |
412 break; | 406 break; |
413 | 407 |
414 case VolumeProjection_Sagittal: | 408 case VolumeProjection_Sagittal: |
415 sagittal_.reset(that.ExtractSagittalSlice(slice)); | 409 sagittal_.reset(that.ExtractSagittalSlice(slice)); |
416 accessor_ = *sagittal_; | 410 sagittal_->GetWriteableAccessor(accessor_); |
417 break; | 411 break; |
418 | 412 |
419 default: | 413 default: |
420 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 414 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
421 } | 415 } |
422 } | 416 } |
423 | 417 |
424 | 418 |
425 uint8_t ImageBuffer3D::GetVoxelGrayscale8(unsigned int x, | 419 uint8_t ImageBuffer3D::GetVoxelGrayscale8(unsigned int x, |
471 Vector ps = GetVoxelDimensions(OrthancStone::VolumeProjection_Axial); | 465 Vector ps = GetVoxelDimensions(OrthancStone::VolumeProjection_Axial); |
472 | 466 |
473 const CoordinateSystem3D& axial = GetAxialGeometry(); | 467 const CoordinateSystem3D& axial = GetAxialGeometry(); |
474 | 468 |
475 Vector origin = (axial.MapSliceToWorldCoordinates(-0.5 * ps[0], -0.5 * ps[1]) - | 469 Vector origin = (axial.MapSliceToWorldCoordinates(-0.5 * ps[0], -0.5 * ps[1]) - |
476 0.5 * ps[2] * axial.GetNormal()); | 470 0.5 * ps[2] * axial.GetNormal()); |
477 | 471 |
478 return (origin + | 472 return (origin + |
479 axial.GetAxisX() * ps[0] * x * static_cast<double>(GetWidth()) + | 473 axial.GetAxisX() * ps[0] * x * static_cast<double>(GetWidth()) + |
480 axial.GetAxisY() * ps[1] * y * static_cast<double>(GetHeight()) + | 474 axial.GetAxisY() * ps[1] * y * static_cast<double>(GetHeight()) + |
481 axial.GetNormal() * ps[2] * z * static_cast<double>(GetDepth())); | 475 axial.GetNormal() * ps[2] * z * static_cast<double>(GetDepth())); |
482 } | 476 } |
483 } | 477 } |