Mercurial > hg > orthanc-stone
comparison Framework/Radiography/RadiographyScene.cpp @ 430:b85f635f1eb5 am-vsol-upgrade
added serialization for RadiographyScene
author | am@osimis.io |
---|---|
date | Thu, 29 Nov 2018 15:11:19 +0100 |
parents | 751fb354149e 3f9017db1738 |
children | 26b90b110719 |
comparison
equal
deleted
inserted
replaced
429:c7fb700a7d12 | 430:b85f635f1eb5 |
---|---|
19 **/ | 19 **/ |
20 | 20 |
21 | 21 |
22 #include "RadiographyScene.h" | 22 #include "RadiographyScene.h" |
23 | 23 |
24 #include "RadiographyAlphaLayer.h" | |
25 #include "RadiographyDicomLayer.h" | |
26 #include "RadiographyTextLayer.h" | |
24 #include "../Toolbox/DicomFrameConverter.h" | 27 #include "../Toolbox/DicomFrameConverter.h" |
25 | 28 |
26 #include <Core/Images/Image.h> | 29 #include <Core/Images/Image.h> |
27 #include <Core/Images/ImageProcessing.h> | 30 #include <Core/Images/ImageProcessing.h> |
28 #include <Core/Images/PamReader.h> | 31 #include <Core/Images/PamReader.h> |
119 { | 122 { |
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 123 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
121 } | 124 } |
122 } | 125 } |
123 | 126 |
124 | |
125 | |
126 class RadiographyScene::AlphaLayer : public RadiographyLayer | |
127 { | |
128 private: | |
129 const RadiographyScene& scene_; | |
130 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 | |
131 bool useWindowing_; | |
132 float foreground_; | |
133 | |
134 public: | |
135 AlphaLayer(const RadiographyScene& scene) : | |
136 scene_(scene), | |
137 useWindowing_(true), | |
138 foreground_(0) | |
139 { | |
140 } | |
141 | |
142 | |
143 void SetForegroundValue(float foreground) | |
144 { | |
145 useWindowing_ = false; | |
146 foreground_ = foreground; | |
147 } | |
148 | |
149 | |
150 void SetAlpha(Orthanc::ImageAccessor* image) | |
151 { | |
152 std::auto_ptr<Orthanc::ImageAccessor> raii(image); | |
153 | |
154 if (image == NULL) | |
155 { | |
156 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
157 } | |
158 | |
159 if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8) | |
160 { | |
161 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
162 } | |
163 | |
164 SetSize(image->GetWidth(), image->GetHeight()); | |
165 alpha_ = raii; | |
166 } | |
167 | |
168 | |
169 void LoadText(const Orthanc::Font& font, | |
170 const std::string& utf8) | |
171 { | |
172 SetAlpha(font.RenderAlpha(utf8)); | |
173 } | |
174 | |
175 | |
176 virtual bool GetDefaultWindowing(float& center, | |
177 float& width) const | |
178 { | |
179 return false; | |
180 } | |
181 | |
182 | |
183 virtual void Render(Orthanc::ImageAccessor& buffer, | |
184 const AffineTransform2D& viewTransform, | |
185 ImageInterpolation interpolation) const | |
186 { | |
187 if (alpha_.get() == NULL) | |
188 { | |
189 return; | |
190 } | |
191 | |
192 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) | |
193 { | |
194 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
195 } | |
196 | |
197 unsigned int cropX, cropY, cropWidth, cropHeight; | |
198 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
199 | |
200 const AffineTransform2D t = AffineTransform2D::Combine( | |
201 viewTransform, GetTransform(), | |
202 AffineTransform2D::CreateOffset(cropX, cropY)); | |
203 | |
204 Orthanc::ImageAccessor cropped; | |
205 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); | |
206 | |
207 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); | |
208 | |
209 t.Apply(tmp, cropped, interpolation, true /* clear */); | |
210 | |
211 // Blit | |
212 const unsigned int width = buffer.GetWidth(); | |
213 const unsigned int height = buffer.GetHeight(); | |
214 | |
215 float value = foreground_; | |
216 | |
217 if (useWindowing_) | |
218 { | |
219 float center, width; | |
220 if (scene_.GetWindowing(center, width)) | |
221 { | |
222 value = center + width / 2.0f; | |
223 } | |
224 } | |
225 | |
226 for (unsigned int y = 0; y < height; y++) | |
227 { | |
228 float *q = reinterpret_cast<float*>(buffer.GetRow(y)); | |
229 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)); | |
230 | |
231 for (unsigned int x = 0; x < width; x++, p++, q++) | |
232 { | |
233 float a = static_cast<float>(*p) / 255.0f; | |
234 | |
235 *q = (a * value + (1.0f - a) * (*q)); | |
236 } | |
237 } | |
238 } | |
239 | |
240 | |
241 virtual bool GetRange(float& minValue, | |
242 float& maxValue) const | |
243 { | |
244 if (useWindowing_) | |
245 { | |
246 return false; | |
247 } | |
248 else | |
249 { | |
250 minValue = 0; | |
251 maxValue = 0; | |
252 | |
253 if (foreground_ < 0) | |
254 { | |
255 minValue = foreground_; | |
256 } | |
257 | |
258 if (foreground_ > 0) | |
259 { | |
260 maxValue = foreground_; | |
261 } | |
262 | |
263 return true; | |
264 } | |
265 } | |
266 }; | |
267 | |
268 | |
269 | |
270 class RadiographyScene::DicomLayer : public RadiographyLayer | |
271 { | |
272 private: | |
273 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | |
274 std::auto_ptr<DicomFrameConverter> converter_; | |
275 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 | |
276 | |
277 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) | |
278 { | |
279 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); | |
280 } | |
281 | |
282 | |
283 void ApplyConverter() | |
284 { | |
285 if (source_.get() != NULL && | |
286 converter_.get() != NULL) | |
287 { | |
288 converted_.reset(converter_->ConvertFrame(*source_)); | |
289 } | |
290 } | |
291 | |
292 public: | |
293 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) | |
294 { | |
295 converter_.reset(new DicomFrameConverter); | |
296 converter_->ReadParameters(dataset); | |
297 ApplyConverter(); | |
298 | |
299 std::string tmp; | |
300 Vector pixelSpacing; | |
301 | |
302 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && | |
303 LinearAlgebra::ParseVector(pixelSpacing, tmp) && | |
304 pixelSpacing.size() == 2) | |
305 { | |
306 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); | |
307 } | |
308 | |
309 //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY()); | |
310 | |
311 OrthancPlugins::DicomDatasetReader reader(dataset); | |
312 | |
313 unsigned int width, height; | |
314 if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || | |
315 !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) | |
316 { | |
317 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
318 } | |
319 else | |
320 { | |
321 SetSize(width, height); | |
322 } | |
323 } | |
324 | |
325 | |
326 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership | |
327 { | |
328 std::auto_ptr<Orthanc::ImageAccessor> raii(image); | |
329 | |
330 if (image == NULL) | |
331 { | |
332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
333 } | |
334 | |
335 SetSize(image->GetWidth(), image->GetHeight()); | |
336 | |
337 source_ = raii; | |
338 ApplyConverter(); | |
339 } | |
340 | |
341 | |
342 virtual void Render(Orthanc::ImageAccessor& buffer, | |
343 const AffineTransform2D& viewTransform, | |
344 ImageInterpolation interpolation) const | |
345 { | |
346 if (converted_.get() != NULL) | |
347 { | |
348 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) | |
349 { | |
350 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
351 } | |
352 | |
353 unsigned int cropX, cropY, cropWidth, cropHeight; | |
354 GetCrop(cropX, cropY, cropWidth, cropHeight); | |
355 | |
356 AffineTransform2D t = AffineTransform2D::Combine( | |
357 viewTransform, GetTransform(), | |
358 AffineTransform2D::CreateOffset(cropX, cropY)); | |
359 | |
360 Orthanc::ImageAccessor cropped; | |
361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); | |
362 | |
363 t.Apply(buffer, cropped, interpolation, false); | |
364 } | |
365 } | |
366 | |
367 | |
368 virtual bool GetDefaultWindowing(float& center, | |
369 float& width) const | |
370 { | |
371 if (converter_.get() != NULL && | |
372 converter_->HasDefaultWindow()) | |
373 { | |
374 center = static_cast<float>(converter_->GetDefaultWindowCenter()); | |
375 width = static_cast<float>(converter_->GetDefaultWindowWidth()); | |
376 return true; | |
377 } | |
378 else | |
379 { | |
380 return false; | |
381 } | |
382 } | |
383 | |
384 | |
385 virtual bool GetRange(float& minValue, | |
386 float& maxValue) const | |
387 { | |
388 if (converted_.get() != NULL) | |
389 { | |
390 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) | |
391 { | |
392 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
393 } | |
394 | |
395 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_); | |
396 return true; | |
397 } | |
398 else | |
399 { | |
400 return false; | |
401 } | |
402 } | |
403 }; | |
404 | |
405 | |
406 RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) | 127 RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) |
407 { | 128 { |
408 if (layer == NULL) | 129 if (layer == NULL) |
409 { | 130 { |
410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 131 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
416 | 137 |
417 size_t index = countLayers_++; | 138 size_t index = countLayers_++; |
418 raii->SetIndex(index); | 139 raii->SetIndex(index); |
419 layers_[index] = raii.release(); | 140 layers_[index] = raii.release(); |
420 | 141 |
421 EmitMessage(GeometryChangedMessage(*this)); | 142 EmitMessage(GeometryChangedMessage(*this, *layer)); |
422 EmitMessage(ContentChangedMessage(*this)); | 143 EmitMessage(ContentChangedMessage(*this, *layer)); |
423 | 144 |
424 return *layer; | 145 return *layer; |
425 } | 146 } |
426 | 147 |
427 | 148 |
443 assert(it->second != NULL); | 164 assert(it->second != NULL); |
444 delete it->second; | 165 delete it->second; |
445 } | 166 } |
446 } | 167 } |
447 | 168 |
169 void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const | |
170 { | |
171 for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) | |
172 { | |
173 output.push_back(it->first); | |
174 } | |
175 } | |
176 | |
448 void RadiographyScene::RemoveLayer(size_t layerIndex) | 177 void RadiographyScene::RemoveLayer(size_t layerIndex) |
449 { | 178 { |
450 LOG(INFO) << "Removing layer: " << layerIndex; | 179 LOG(INFO) << "Removing layer: " << layerIndex; |
451 | 180 |
452 if (layerIndex > countLayers_) | 181 if (layerIndex > countLayers_) |
457 layers_.erase(layerIndex); | 186 layers_.erase(layerIndex); |
458 countLayers_--; | 187 countLayers_--; |
459 LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; | 188 LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; |
460 } | 189 } |
461 | 190 |
462 RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) | 191 const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const |
463 { | 192 { |
464 if (layerIndex > countLayers_) | 193 if (layerIndex > countLayers_) |
465 { | 194 { |
466 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 195 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
467 } | 196 } |
468 return *(layers_[layerIndex]); | 197 |
198 return *(layers_.at(layerIndex)); | |
469 } | 199 } |
470 | 200 |
471 bool RadiographyScene::GetWindowing(float& center, | 201 bool RadiographyScene::GetWindowing(float& center, |
472 float& width) const | 202 float& width) const |
473 { | 203 { |
503 windowingWidth_ = width; | 233 windowingWidth_ = width; |
504 } | 234 } |
505 | 235 |
506 | 236 |
507 RadiographyLayer& RadiographyScene::LoadText(const Orthanc::Font& font, | 237 RadiographyLayer& RadiographyScene::LoadText(const Orthanc::Font& font, |
508 const std::string& utf8) | 238 const std::string& utf8, |
509 { | 239 RadiographyLayer::Geometry* geometry) |
510 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this)); | 240 { |
241 std::auto_ptr<RadiographyTextLayer> alpha(new RadiographyTextLayer(*this)); | |
511 alpha->LoadText(font, utf8); | 242 alpha->LoadText(font, utf8); |
243 if (geometry != NULL) | |
244 { | |
245 alpha->SetGeometry(*geometry); | |
246 } | |
512 | 247 |
513 return RegisterLayer(alpha.release()); | 248 return RegisterLayer(alpha.release()); |
514 } | 249 } |
515 | 250 |
516 | 251 |
517 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, | 252 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, |
518 unsigned int height) | 253 unsigned int height, |
254 RadiographyLayer::Geometry* geometry) | |
519 { | 255 { |
520 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); | 256 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); |
521 | 257 |
522 for (unsigned int padding = 0; | 258 for (unsigned int padding = 0; |
523 (width > 2 * padding) && (height > 2 * padding); | 259 (width > 2 * padding) && (height > 2 * padding); |
536 Orthanc::ImageAccessor region; | 272 Orthanc::ImageAccessor region; |
537 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); | 273 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); |
538 Orthanc::ImageProcessing::Set(region, color); | 274 Orthanc::ImageProcessing::Set(region, color); |
539 } | 275 } |
540 | 276 |
541 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this)); | 277 return LoadAlphaBitmap(block.release(), geometry); |
542 alpha->SetAlpha(block.release()); | 278 } |
279 | |
280 RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry) | |
281 { | |
282 std::auto_ptr<RadiographyAlphaLayer> alpha(new RadiographyAlphaLayer(*this)); | |
283 alpha->SetAlpha(bitmap); | |
284 if (geometry != NULL) | |
285 { | |
286 alpha->SetGeometry(*geometry); | |
287 } | |
543 | 288 |
544 return RegisterLayer(alpha.release()); | 289 return RegisterLayer(alpha.release()); |
545 } | 290 } |
546 | |
547 | 291 |
548 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, | 292 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, |
549 const std::string& instance, | 293 const std::string& instance, |
550 unsigned int frame, | 294 unsigned int frame, |
551 bool httpCompression) | 295 bool httpCompression, |
552 { | 296 RadiographyLayer::Geometry* geometry) |
553 RadiographyLayer& layer = RegisterLayer(new DicomLayer); | 297 { |
298 RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer)); | |
299 layer.SetInstance(instance, frame); | |
300 | |
301 if (geometry != NULL) | |
302 { | |
303 layer.SetGeometry(*geometry); | |
304 } | |
554 | 305 |
555 { | 306 { |
556 IWebService::HttpHeaders headers; | 307 IWebService::HttpHeaders headers; |
557 std::string uri = "/instances/" + instance + "/tags"; | 308 std::string uri = "/instances/" + instance + "/tags"; |
558 | 309 |
586 } | 337 } |
587 | 338 |
588 | 339 |
589 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) | 340 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) |
590 { | 341 { |
591 RadiographyLayer& layer = RegisterLayer(new DicomLayer); | 342 RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer); |
592 | 343 |
593 | 344 |
594 return layer; | 345 return layer; |
595 } | 346 } |
596 | 347 |
608 if (layer != layers_.end()) | 359 if (layer != layers_.end()) |
609 { | 360 { |
610 assert(layer->second != NULL); | 361 assert(layer->second != NULL); |
611 | 362 |
612 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); | 363 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); |
613 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom); | 364 dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetDicomTags(dicom); |
614 | 365 |
615 float c, w; | 366 float c, w; |
616 if (!hasWindowing_ && | 367 if (!hasWindowing_ && |
617 layer->second->GetDefaultWindowing(c, w)) | 368 layer->second->GetDefaultWindowing(c, w)) |
618 { | 369 { |
619 hasWindowing_ = true; | 370 hasWindowing_ = true; |
620 windowingCenter_ = c; | 371 windowingCenter_ = c; |
621 windowingWidth_ = w; | 372 windowingWidth_ = w; |
622 } | 373 } |
623 | 374 |
624 EmitMessage(GeometryChangedMessage(*this)); | 375 EmitMessage(GeometryChangedMessage(*this, *(layer->second))); |
625 } | 376 } |
626 } | 377 } |
627 | 378 |
628 | 379 |
629 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) | 380 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) |
644 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); | 395 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); |
645 } | 396 } |
646 | 397 |
647 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); | 398 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); |
648 reader->ReadFromMemory(content); | 399 reader->ReadFromMemory(content); |
649 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release()); | 400 dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetSourceImage(reader.release()); |
650 | 401 |
651 EmitMessage(ContentChangedMessage(*this)); | 402 EmitMessage(ContentChangedMessage(*this, *(layer->second))); |
652 } | 403 } |
653 } | 404 } |
654 | 405 |
655 | 406 |
656 Extent2D RadiographyScene::GetSceneExtent() const | 407 Extent2D RadiographyScene::GetSceneExtent() const |
670 | 421 |
671 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, | 422 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, |
672 const AffineTransform2D& viewTransform, | 423 const AffineTransform2D& viewTransform, |
673 ImageInterpolation interpolation) const | 424 ImageInterpolation interpolation) const |
674 { | 425 { |
675 Orthanc::ImageProcessing::Set(buffer, 0); | 426 Orthanc::ImageProcessing::Set(buffer, 0); // TODO: get background color (depending on inverted state) |
676 | 427 |
677 // Render layers in the background-to-foreground order | 428 // Render layers in the background-to-foreground order |
678 for (size_t index = 0; index < countLayers_; index++) | 429 for (size_t index = 0; index < countLayers_; index++) |
679 { | 430 { |
680 Layers::const_iterator it = layers_.find(index); | 431 Layers::const_iterator it = layers_.find(index); |
768 ImageInterpolation interpolation, | 519 ImageInterpolation interpolation, |
769 bool usePam) | 520 bool usePam) |
770 { | 521 { |
771 Json::Value createDicomRequestContent; | 522 Json::Value createDicomRequestContent; |
772 | 523 |
773 Export(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); | 524 ExportToCreateDicomRequest(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); |
774 | 525 |
775 if (!parentOrthancId.empty()) | 526 if (!parentOrthancId.empty()) |
776 { | 527 { |
777 createDicomRequestContent["Parent"] = parentOrthancId; | 528 createDicomRequestContent["Parent"] = parentOrthancId; |
778 } | 529 } |
784 NULL, NULL); | 535 NULL, NULL); |
785 } | 536 } |
786 | 537 |
787 // Export using PAM is faster than using PNG, but requires Orthanc | 538 // Export using PAM is faster than using PNG, but requires Orthanc |
788 // core >= 1.4.3 | 539 // core >= 1.4.3 |
789 void RadiographyScene::Export(Json::Value& createDicomRequestContent, | 540 void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, |
790 const Orthanc::DicomMap& dicom, | 541 const Orthanc::DicomMap& dicom, |
791 double pixelSpacingX, | 542 double pixelSpacingX, |
792 double pixelSpacingY, | 543 double pixelSpacingY, |
793 bool invert, | 544 bool invert, |
794 ImageInterpolation interpolation, | 545 ImageInterpolation interpolation, |