Mercurial > hg > orthanc
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, |