Mercurial > hg > orthanc-stone
comparison Framework/Volumes/ImageBuffer3D.cpp @ 684:7719eb852dd5
new class: VolumeImageGeometry
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 16 May 2019 16:47:46 +0200 |
parents | dbc1d8bfc68a |
children | 9a474e90e832 |
comparison
equal
deleted
inserted
replaced
683:dbc1d8bfc68a | 684:7719eb852dd5 |
---|---|
116 height_(height), | 116 height_(height), |
117 depth_(depth), | 117 depth_(depth), |
118 computeRange_(computeRange), | 118 computeRange_(computeRange), |
119 hasRange_(false) | 119 hasRange_(false) |
120 { | 120 { |
121 LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1); | 121 geometry_.SetSize(width, height, depth); |
122 | 122 |
123 LOG(INFO) << "Created a 3D image of size " << width << "x" << height | 123 LOG(INFO) << "Created a 3D image of size " << width << "x" << height |
124 << "x" << depth << " in " << Orthanc::EnumerationToString(format) | 124 << "x" << depth << " in " << Orthanc::EnumerationToString(format) |
125 << " (" << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB)"; | 125 << " (" << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB)"; |
126 | |
127 UpdateGeometry(); | |
128 } | 126 } |
129 | 127 |
130 | 128 |
131 void ImageBuffer3D::Clear() | 129 void ImageBuffer3D::Clear() |
132 { | 130 { |
133 memset(image_.GetBuffer(), 0, image_.GetHeight() * image_.GetPitch()); | 131 memset(image_.GetBuffer(), 0, image_.GetHeight() * image_.GetPitch()); |
134 } | 132 } |
135 | 133 |
136 | 134 |
137 void ImageBuffer3D::SetAxialGeometry(const CoordinateSystem3D& geometry) | 135 |
138 { | 136 |
139 axialGeometry_ = geometry; | 137 ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const |
140 UpdateGeometry(); | 138 { |
141 } | 139 const Vector dimensions = geometry_.GetVoxelDimensions(VolumeProjection_Axial); |
142 | 140 const CoordinateSystem3D& axial = geometry_.GetAxialGeometry(); |
143 | 141 |
144 void ImageBuffer3D::SetVoxelDimensions(double x, | 142 std::auto_ptr<ParallelSlices> result(new ParallelSlices); |
145 double y, | 143 |
146 double z) | |
147 { | |
148 if (x <= 0 || | |
149 y <= 0 || | |
150 z <= 0) | |
151 { | |
152 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
153 } | |
154 else | |
155 { | |
156 LinearAlgebra::AssignVector(voxelDimensions_, x, y, z); | |
157 UpdateGeometry(); | |
158 } | |
159 } | |
160 | |
161 | |
162 Vector ImageBuffer3D::GetVoxelDimensions(VolumeProjection projection) const | |
163 { | |
164 switch (projection) | 144 switch (projection) |
165 { | 145 { |
166 case VolumeProjection_Axial: | 146 case VolumeProjection_Axial: |
167 return voxelDimensions_; | 147 for (unsigned int z = 0; z < depth_; z++) |
168 | 148 { |
169 case VolumeProjection_Coronal: | 149 Vector origin = axial.GetOrigin(); |
170 return LinearAlgebra::CreateVector(voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); | 150 origin += static_cast<double>(z) * dimensions[2] * axial.GetNormal(); |
171 | 151 |
172 case VolumeProjection_Sagittal: | 152 result->AddSlice(origin, |
173 return LinearAlgebra::CreateVector(voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); | 153 axial.GetAxisX(), |
174 | 154 axial.GetAxisY()); |
175 default: | 155 } |
176 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 156 break; |
177 } | 157 |
178 } | 158 case VolumeProjection_Coronal: |
179 | 159 for (unsigned int y = 0; y < height_; y++) |
180 | 160 { |
181 void ImageBuffer3D::GetSliceSize(unsigned int& width, | 161 Vector origin = axial.GetOrigin(); |
182 unsigned int& height, | 162 origin += static_cast<double>(y) * dimensions[1] * axial.GetAxisY(); |
183 VolumeProjection projection) | 163 origin += static_cast<double>(depth_ - 1) * dimensions[2] * axial.GetNormal(); |
184 { | 164 |
185 switch (projection) | 165 result->AddSlice(origin, |
186 { | 166 axial.GetAxisX(), |
187 case VolumeProjection_Axial: | 167 -axial.GetNormal()); |
188 width = width_; | 168 } |
189 height = height_; | 169 break; |
190 break; | 170 |
191 | 171 case VolumeProjection_Sagittal: |
192 case VolumeProjection_Coronal: | 172 for (unsigned int x = 0; x < width_; x++) |
193 width = width_; | 173 { |
194 height = depth_; | 174 Vector origin = axial.GetOrigin(); |
195 break; | 175 origin += static_cast<double>(x) * dimensions[0] * axial.GetAxisX(); |
196 | 176 origin += static_cast<double>(depth_ - 1) * dimensions[2] * axial.GetNormal(); |
197 case VolumeProjection_Sagittal: | 177 |
198 width = height_; | 178 result->AddSlice(origin, |
199 height = depth_; | 179 axial.GetAxisY(), |
200 break; | 180 -axial.GetNormal()); |
201 | 181 } |
202 default: | 182 break; |
203 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 183 |
204 } | 184 default: |
205 } | 185 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
206 | |
207 | |
208 ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const | |
209 { | |
210 std::auto_ptr<ParallelSlices> result(new ParallelSlices); | |
211 | |
212 switch (projection) | |
213 { | |
214 case VolumeProjection_Axial: | |
215 for (unsigned int z = 0; z < depth_; z++) | |
216 { | |
217 Vector origin = axialGeometry_.GetOrigin(); | |
218 origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | |
219 | |
220 result->AddSlice(origin, | |
221 axialGeometry_.GetAxisX(), | |
222 axialGeometry_.GetAxisY()); | |
223 } | |
224 break; | |
225 | |
226 case VolumeProjection_Coronal: | |
227 for (unsigned int y = 0; y < height_; y++) | |
228 { | |
229 Vector origin = axialGeometry_.GetOrigin(); | |
230 origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY(); | |
231 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | |
232 | |
233 result->AddSlice(origin, | |
234 axialGeometry_.GetAxisX(), | |
235 -axialGeometry_.GetNormal()); | |
236 } | |
237 break; | |
238 | |
239 case VolumeProjection_Sagittal: | |
240 for (unsigned int x = 0; x < width_; x++) | |
241 { | |
242 Vector origin = axialGeometry_.GetOrigin(); | |
243 origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX(); | |
244 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | |
245 | |
246 result->AddSlice(origin, | |
247 axialGeometry_.GetAxisY(), | |
248 -axialGeometry_.GetNormal()); | |
249 } | |
250 break; | |
251 | |
252 default: | |
253 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
254 } | 186 } |
255 | 187 |
256 return result.release(); | 188 return result.release(); |
257 } | 189 } |
258 | 190 |
274 | 206 |
275 float sliceMin, sliceMax; | 207 float sliceMin, sliceMax; |
276 | 208 |
277 switch (slice.GetFormat()) | 209 switch (slice.GetFormat()) |
278 { | 210 { |
279 case Orthanc::PixelFormat_Grayscale8: | 211 case Orthanc::PixelFormat_Grayscale8: |
280 case Orthanc::PixelFormat_Grayscale16: | 212 case Orthanc::PixelFormat_Grayscale16: |
281 case Orthanc::PixelFormat_Grayscale32: | 213 case Orthanc::PixelFormat_Grayscale32: |
282 case Orthanc::PixelFormat_SignedGrayscale16: | 214 case Orthanc::PixelFormat_SignedGrayscale16: |
283 { | 215 { |
284 int64_t a, b; | 216 int64_t a, b; |
285 Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, slice); | 217 Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, slice); |
286 sliceMin = static_cast<float>(a); | 218 sliceMin = static_cast<float>(a); |
287 sliceMax = static_cast<float>(b); | 219 sliceMax = static_cast<float>(b); |
288 break; | 220 break; |
289 } | 221 } |
290 | 222 |
291 case Orthanc::PixelFormat_Float32: | 223 case Orthanc::PixelFormat_Float32: |
292 Orthanc::ImageProcessing::GetMinMaxFloatValue(sliceMin, sliceMax, slice); | 224 Orthanc::ImageProcessing::GetMinMaxFloatValue(sliceMin, sliceMax, slice); |
293 break; | 225 break; |
294 | 226 |
295 default: | 227 default: |
296 return; | 228 return; |
297 } | 229 } |
298 | 230 |
299 if (hasRange_) | 231 if (hasRange_) |
300 { | 232 { |
301 minValue_ = std::min(minValue_, sliceMin); | 233 minValue_ = std::min(minValue_, sliceMin); |
365 case VolumeProjection_Sagittal: | 297 case VolumeProjection_Sagittal: |
366 sagittal_.reset(that.ExtractSagittalSlice(slice)); | 298 sagittal_.reset(that.ExtractSagittalSlice(slice)); |
367 sagittal_->GetReadOnlyAccessor(accessor_); | 299 sagittal_->GetReadOnlyAccessor(accessor_); |
368 break; | 300 break; |
369 | 301 |
370 default: | 302 default: |
371 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 303 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
372 } | 304 } |
373 } | 305 } |
374 | 306 |
375 | 307 |
376 void ImageBuffer3D::SliceWriter::Flush() | 308 void ImageBuffer3D::SliceWriter::Flush() |
409 case VolumeProjection_Sagittal: | 341 case VolumeProjection_Sagittal: |
410 sagittal_.reset(that.ExtractSagittalSlice(slice)); | 342 sagittal_.reset(that.ExtractSagittalSlice(slice)); |
411 sagittal_->GetWriteableAccessor(accessor_); | 343 sagittal_->GetWriteableAccessor(accessor_); |
412 break; | 344 break; |
413 | 345 |
414 default: | 346 default: |
415 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 347 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
416 } | 348 } |
417 } | 349 } |
418 | 350 |
419 | 351 |
420 uint8_t ImageBuffer3D::GetVoxelGrayscale8(unsigned int x, | 352 uint8_t ImageBuffer3D::GetVoxelGrayscale8(unsigned int x, |
455 } | 387 } |
456 | 388 |
457 const void* p = image_.GetConstRow(y + height_ * (depth_ - 1 - z)); | 389 const void* p = image_.GetConstRow(y + height_ * (depth_ - 1 - z)); |
458 return reinterpret_cast<const uint16_t*>(p) [x]; | 390 return reinterpret_cast<const uint16_t*>(p) [x]; |
459 } | 391 } |
460 | |
461 | |
462 void ImageBuffer3D::UpdateGeometry() | |
463 { | |
464 Vector p = (axialGeometry_.GetOrigin() + | |
465 static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal()); | |
466 | |
467 coronalGeometry_ = CoordinateSystem3D(p, | |
468 axialGeometry_.GetAxisX(), | |
469 -axialGeometry_.GetNormal()); | |
470 | |
471 sagittalGeometry_ = CoordinateSystem3D(p, | |
472 axialGeometry_.GetAxisY(), | |
473 -axialGeometry_.GetNormal()); | |
474 | |
475 Vector origin = ( | |
476 axialGeometry_.MapSliceToWorldCoordinates(-0.5 * voxelDimensions_[0], | |
477 -0.5 * voxelDimensions_[1]) - | |
478 0.5 * voxelDimensions_[2] * axialGeometry_.GetNormal()); | |
479 | |
480 Vector scaling = ( | |
481 axialGeometry_.GetAxisX() * voxelDimensions_[0] * static_cast<double>(width_) + | |
482 axialGeometry_.GetAxisY() * voxelDimensions_[1] * static_cast<double>(height_) + | |
483 axialGeometry_.GetNormal() * voxelDimensions_[2] * static_cast<double>(depth_)); | |
484 | |
485 transform_ = LinearAlgebra::Product( | |
486 GeometryToolbox::CreateTranslationMatrix(origin[0], origin[1], origin[2]), | |
487 GeometryToolbox::CreateScalingMatrix(scaling[0], scaling[1], scaling[2])); | |
488 | |
489 LinearAlgebra::InvertMatrix(transformInverse_, transform_); | |
490 } | |
491 | |
492 | |
493 Vector ImageBuffer3D::GetCoordinates(float x, | |
494 float y, | |
495 float z) const | |
496 { | |
497 Vector p = LinearAlgebra::Product(transform_, LinearAlgebra::CreateVector(x, y, z, 1)); | |
498 | |
499 assert(LinearAlgebra::IsNear(p[3], 1)); // Affine transform, no perspective effect | |
500 | |
501 // Back to non-homogeneous coordinates | |
502 return LinearAlgebra::CreateVector(p[0], p[1], p[2]); | |
503 } | |
504 | |
505 | |
506 bool ImageBuffer3D::DetectProjection(VolumeProjection& projection, | |
507 const CoordinateSystem3D& plane) const | |
508 { | |
509 if (GeometryToolbox::IsParallel(plane.GetNormal(), | |
510 axialGeometry_.GetNormal())) | |
511 { | |
512 projection = VolumeProjection_Axial; | |
513 return true; | |
514 } | |
515 | |
516 { | |
517 if (GeometryToolbox::IsParallel(plane.GetNormal(), | |
518 coronalGeometry_.GetNormal())) | |
519 { | |
520 projection = VolumeProjection_Coronal; | |
521 return true; | |
522 } | |
523 } | |
524 | |
525 { | |
526 if (GeometryToolbox::IsParallel(plane.GetNormal(), | |
527 sagittalGeometry_.GetNormal())) | |
528 { | |
529 projection = VolumeProjection_Sagittal; | |
530 return true; | |
531 } | |
532 } | |
533 | |
534 return false; | |
535 } | |
536 } | 392 } |