comparison OrthancCppClient/Series.cpp @ 986:de18e90d5507

fix GetVoxelSizeZ
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 01 Jul 2014 16:51:18 +0200
parents a811bdf8b8eb
children 6e7e5ed91c2d
comparison
equal deleted inserted replaced
981:ef02bd1c2f0e 986:de18e90d5507
51 SliceLocator(Instance& someSlice) 51 SliceLocator(Instance& someSlice)
52 { 52 {
53 /** 53 /**
54 * Compute the slice normal from Image Orientation Patient. 54 * Compute the slice normal from Image Orientation Patient.
55 * http://nipy.sourceforge.net/nibabel/dicom/dicom_orientation.html#dicom-z-from-slice 55 * http://nipy.sourceforge.net/nibabel/dicom/dicom_orientation.html#dicom-z-from-slice
56 * http://dicomiseasy.blogspot.be/2013/06/getting-oriented-using-image-plane.html
56 * http://www.itk.org/pipermail/insight-users/2003-September/004762.html 57 * http://www.itk.org/pipermail/insight-users/2003-September/004762.html
57 **/ 58 **/
58 59
59 std::vector<float> cosines; 60 std::vector<float> cosines;
60 someSlice.SplitVectorOfFloats(cosines, "ImageOrientationPatient"); 61 someSlice.SplitVectorOfFloats(cosines, "ImageOrientationPatient"); // 0020-0037
61 62
62 if (cosines.size() != 6) 63 if (cosines.size() != 6)
63 { 64 {
64 throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); 65 throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
65 } 66 }
74 * Compute the distance of some slice along the slice normal. 75 * Compute the distance of some slice along the slice normal.
75 **/ 76 **/
76 float ComputeSliceLocation(Instance& instance) const 77 float ComputeSliceLocation(Instance& instance) const
77 { 78 {
78 std::vector<float> ipp; 79 std::vector<float> ipp;
79 instance.SplitVectorOfFloats(ipp, "ImagePositionPatient"); 80 instance.SplitVectorOfFloats(ipp, "ImagePositionPatient"); // 0020-0032
80 if (ipp.size() != 3) 81 if (ipp.size() != 3)
81 { 82 {
82 throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat); 83 throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
83 } 84 }
84 85
127 { 128 {
128 uint8_t* p = reinterpret_cast<uint8_t*>(target_) + y * lineStride_; 129 uint8_t* p = reinterpret_cast<uint8_t*>(target_) + y * lineStride_;
129 130
130 if (instance_.GetPixelFormat() == format_) 131 if (instance_.GetPixelFormat() == format_)
131 { 132 {
132 memcpy(p, instance_.GetBuffer(y), 2 * instance_.GetWidth()); 133 memcpy(p, instance_.GetBuffer(y), GetBytesPerPixel(instance_.GetPixelFormat()) * instance_.GetWidth());
133 } 134 }
134 else if (instance_.GetPixelFormat() == PixelFormat_Grayscale8 && 135 else if (instance_.GetPixelFormat() == PixelFormat_Grayscale8 &&
135 format_ == PixelFormat_RGB24) 136 format_ == PixelFormat_RGB24)
136 { 137 {
137 const uint8_t* s = reinterpret_cast<const uint8_t*>(instance_.GetBuffer(y)); 138 const uint8_t* s = reinterpret_cast<const uint8_t*>(instance_.GetBuffer(y));
210 { 211 {
211 try 212 try
212 { 213 {
213 if (GetInstanceCount() == 0) 214 if (GetInstanceCount() == 0)
214 { 215 {
216 // Empty image, use some default value (should never happen)
217 voxelSizeX_ = 1;
218 voxelSizeY_ = 1;
219 voxelSizeZ_ = 1;
220 sliceThickness_ = 1;
221
215 return true; 222 return true;
216 } 223 }
217 224
218 Instance& i1 = GetInstance(0); 225 // Choose a reference slice
219 226 Instance& reference = GetInstance(0);
227
228 // Check that all the child instances share the same 3D parameters
220 for (unsigned int i = 0; i < GetInstanceCount(); i++) 229 for (unsigned int i = 0; i < GetInstanceCount(); i++)
221 { 230 {
222 Instance& i2 = GetInstance(i); 231 Instance& i2 = GetInstance(i);
223 232
224 if (std::string(i1.GetTagAsString("Columns")) != std::string(i2.GetTagAsString("Columns")) || 233 if (std::string(reference.GetTagAsString("Columns")) != std::string(i2.GetTagAsString("Columns")) ||
225 std::string(i1.GetTagAsString("Rows")) != std::string(i2.GetTagAsString("Rows")) || 234 std::string(reference.GetTagAsString("Rows")) != std::string(i2.GetTagAsString("Rows")) ||
226 std::string(i1.GetTagAsString("ImageOrientationPatient")) != std::string(i2.GetTagAsString("ImageOrientationPatient")) || 235 std::string(reference.GetTagAsString("ImageOrientationPatient")) != std::string(i2.GetTagAsString("ImageOrientationPatient")) ||
227 std::string(i1.GetTagAsString("SliceThickness")) != std::string(i2.GetTagAsString("SliceThickness")) || 236 std::string(reference.GetTagAsString("SliceThickness")) != std::string(i2.GetTagAsString("SliceThickness")) ||
228 std::string(i1.GetTagAsString("PixelSpacing")) != std::string(i2.GetTagAsString("PixelSpacing"))) 237 std::string(reference.GetTagAsString("PixelSpacing")) != std::string(i2.GetTagAsString("PixelSpacing")))
229 { 238 {
230 return false; 239 return false;
231 } 240 }
232 } 241 }
233 242
234 SliceLocator locator(GetInstance(0)); 243
244 // Extract X/Y voxel size and slice thickness
245 std::string s = GetInstance(0).GetTagAsString("PixelSpacing"); // 0028-0030
246 size_t pos = s.find('\\');
247 assert(pos != std::string::npos);
248 std::string sy = s.substr(0, pos);
249 std::string sx = s.substr(pos + 1);
250
251 try
252 {
253 voxelSizeX_ = boost::lexical_cast<float>(sx);
254 voxelSizeY_ = boost::lexical_cast<float>(sy);
255 }
256 catch (boost::bad_lexical_cast)
257 {
258 throw OrthancClientException(Orthanc::ErrorCode_BadFileFormat);
259 }
260
261 sliceThickness_ = GetInstance(0).GetTagAsFloat("SliceThickness"); // 0018-0050
262
263
264 // Compute the location of each slice to extract the voxel size along Z
265 voxelSizeZ_ = std::numeric_limits<float>::infinity();
266
267 SliceLocator locator(reference);
268 float referenceSliceLocation = locator.ComputeSliceLocation(reference);
269
235 std::set<float> l; 270 std::set<float> l;
236 for (unsigned int i = 0; i < GetInstanceCount(); i++) 271 for (unsigned int i = 0; i < GetInstanceCount(); i++)
237 { 272 {
238 l.insert(locator.ComputeSliceLocation(GetInstance(i))); 273 float location = locator.ComputeSliceLocation(GetInstance(i));
239 } 274 float distanceToReferenceSlice = fabs(location - referenceSliceLocation);
240 275
276 l.insert(location);
277
278 if (distanceToReferenceSlice > std::numeric_limits<float>::epsilon() &&
279 distanceToReferenceSlice < voxelSizeZ_)
280 {
281 voxelSizeZ_ = distanceToReferenceSlice;
282 }
283 }
284
285
286 // Make sure that 2 slices do not share the same Z location
241 return l.size() == GetInstanceCount(); 287 return l.size() == GetInstanceCount();
242 } 288 }
243 catch (OrthancClientException) 289 catch (OrthancClientException)
244 { 290 {
245 return false; 291 return false;
273 { 319 {
274 ReadSeries(); 320 ReadSeries();
275 status_ = Status3DImage_NotTested; 321 status_ = Status3DImage_NotTested;
276 url_ = std::string(connection_.GetOrthancUrl()) + "/series/" + id_; 322 url_ = std::string(connection_.GetOrthancUrl()) + "/series/" + id_;
277 323
278 isVoxelSizeRead_ = false;
279 voxelSizeX_ = 0; 324 voxelSizeX_ = 0;
280 voxelSizeY_ = 0; 325 voxelSizeY_ = 0;
281 voxelSizeZ_ = 0; 326 voxelSizeZ_ = 0;
327 sliceThickness_ = 0;
282 328
283 instances_.SetThreadCount(connection.GetThreadCount()); 329 instances_.SetThreadCount(connection.GetThreadCount());
284 } 330 }
285 331
286 332
321 if (GetInstanceCount() == 0) 367 if (GetInstanceCount() == 0)
322 return 0; 368 return 0;
323 else 369 else
324 return GetInstance(0).GetTagAsInt("Rows"); 370 return GetInstance(0).GetTagAsInt("Rows");
325 } 371 }
326
327 void Series::LoadVoxelSize()
328 {
329 if (isVoxelSizeRead_)
330 {
331 return;
332 }
333
334 Check3DImage();
335
336 if (GetInstanceCount() == 0)
337 {
338 // Empty image, use some default value
339 voxelSizeX_ = 1;
340 voxelSizeY_ = 1;
341 voxelSizeZ_ = 1;
342 }
343 else
344 {
345 try
346 {
347 std::string s = GetInstance(0).GetTagAsString("PixelSpacing");
348 size_t pos = s.find('\\');
349 assert(pos != std::string::npos);
350 std::string sy = s.substr(0, pos);
351 std::string sx = s.substr(pos + 1);
352
353 voxelSizeX_ = boost::lexical_cast<float>(sx);
354 voxelSizeY_ = boost::lexical_cast<float>(sy);
355 voxelSizeZ_ = GetInstance(0).GetTagAsFloat("SliceThickness");
356 }
357 catch (boost::bad_lexical_cast)
358 {
359 throw OrthancClientException(Orthanc::ErrorCode_NotImplemented);
360 }
361 }
362
363 isVoxelSizeRead_ = true;
364 }
365
366 372
367 const char* Series::GetMainDicomTag(const char* tag, const char* defaultValue) const 373 const char* Series::GetMainDicomTag(const char* tag, const char* defaultValue) const
368 { 374 {
369 if (series_["MainDicomTags"].isMember(tag)) 375 if (series_["MainDicomTags"].isMember(tag))
370 { 376 {
484 } 490 }
485 } 491 }
486 492
487 float Series::GetVoxelSizeX() 493 float Series::GetVoxelSizeX()
488 { 494 {
489 LoadVoxelSize(); 495 Check3DImage(); // Is3DImageInternal() will compute the voxel sizes
490 return voxelSizeX_; 496 return voxelSizeX_;
491 } 497 }
492 498
493 float Series::GetVoxelSizeY() 499 float Series::GetVoxelSizeY()
494 { 500 {
495 LoadVoxelSize(); 501 Check3DImage(); // Is3DImageInternal() will compute the voxel sizes
496 return voxelSizeY_; 502 return voxelSizeY_;
497 } 503 }
498 504
499 float Series::GetVoxelSizeZ() 505 float Series::GetVoxelSizeZ()
500 { 506 {
501 LoadVoxelSize(); 507 Check3DImage(); // Is3DImageInternal() will compute the voxel sizes
502 return voxelSizeZ_; 508 return voxelSizeZ_;
509 }
510
511 float Series::GetSliceThickness()
512 {
513 Check3DImage(); // Is3DImageInternal() will compute the voxel sizes
514 return sliceThickness_;
503 } 515 }
504 516
505 void Series::Load3DImage(void* target, 517 void Series::Load3DImage(void* target,
506 Orthanc::PixelFormat format, 518 Orthanc::PixelFormat format,
507 int64_t lineStride, 519 int64_t lineStride,