comparison Plugin/ParsedDicomImage.cpp @ 31:111689a2c177

author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 24 Jun 2015 16:20:41 +0200
parents a6492d20b2a8
children abdde1dfb3eb
comparison
equal deleted inserted replaced
30:ad7b6757965a 31:111689a2c177
22 22
23 #include "../Orthanc/Core/OrthancException.h" 23 #include "../Orthanc/Core/OrthancException.h"
24 #include "../Orthanc/Core/Toolbox.h" 24 #include "../Orthanc/Core/Toolbox.h"
25 #include "../Orthanc/Core/ImageFormats/ImageProcessing.h" 25 #include "../Orthanc/Core/ImageFormats/ImageProcessing.h"
26 #include "../Orthanc/Core/ImageFormats/ImageBuffer.h" 26 #include "../Orthanc/Core/ImageFormats/ImageBuffer.h"
27 #include "../Orthanc/Core/ImageFormats/PngReader.h"
27 #include "JpegWriter.h" 28 #include "JpegWriter.h"
28 #include "ViewerToolbox.h" 29 #include "ViewerToolbox.h"
29 30
30 #include <gdcmImageReader.h> 31 #include <gdcmImageReader.h>
31 #include <gdcmImageChangePlanarConfiguration.h> 32 #include <gdcmImageChangePlanarConfiguration.h>
36 #include "../Orthanc/Resources/ThirdParty/base64/base64.h" 37 #include "../Orthanc/Resources/ThirdParty/base64/base64.h"
37 38
38 39
39 namespace OrthancPlugins 40 namespace OrthancPlugins
40 { 41 {
41 struct ParsedDicomImage::PImpl 42 class ParsedDicomImage::PImpl
42 { 43 {
44 private:
45 OrthancPluginContext* context_;
46 std::string instanceId_;
43 gdcm::ImageReader reader_; 47 gdcm::ImageReader reader_;
44 std::auto_ptr<gdcm::ImageChangePhotometricInterpretation> photometric_; 48 std::auto_ptr<gdcm::ImageChangePhotometricInterpretation> photometric_;
45 std::auto_ptr<gdcm::ImageChangePlanarConfiguration> interleaved_; 49 std::auto_ptr<gdcm::ImageChangePlanarConfiguration> interleaved_;
46 std::string decoded_; 50 std::string decoded_;
51 Orthanc::PngReader png_;
52 bool insidePng_;
53 bool isDecoded_;
54
55 bool DecodeUsingGdcm()
56 {
57 // Change photometric interpretation, if required
58 {
59 const gdcm::Image& image = GetImage();
60 if (image.GetPixelFormat().GetSamplesPerPixel() == 1)
61 {
62 if (image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME1 &&
63 image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME2)
64 {
65 photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
66 photometric_->SetInput(image);
67 photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2);
68 if (!photometric_->Change() ||
69 GetImage().GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME2)
70 {
71 OrthancPluginLogWarning(context_, "GDCM cannot change the photometric interpretation");
72 return false;
73 }
74 }
75 }
76 else
77 {
78 if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
79 image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
80 {
81 photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
82 photometric_->SetInput(image);
83 photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::RGB);
84 if (!photometric_->Change() ||
85 GetImage().GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
86 {
87 OrthancPluginLogWarning(context_, "GDCM cannot change the photometric interpretation");
88 return false;
89 }
90 }
91 }
92 }
93
94 // Possibly convert planar configuration to interleaved
95 {
96 const gdcm::Image& image = GetImage();
97 if (image.GetPlanarConfiguration() != 0 &&
98 image.GetPixelFormat().GetSamplesPerPixel() != 1)
99 {
100 interleaved_.reset(new gdcm::ImageChangePlanarConfiguration());
101 interleaved_->SetInput(image);
102 if (!interleaved_->Change() ||
103 GetImage().GetPlanarConfiguration() != 0)
104 {
105 OrthancPluginLogWarning(context_, "GDCM cannot change the planar configuration to interleaved");
106 return false;
107 }
108 }
109 }
110
111 // Decode the image to the memory buffer
112 {
113 const gdcm::Image& image = GetImage();
114 decoded_.resize(image.GetBufferLength());
115
116 if (decoded_.size() > 0)
117 {
118 image.GetBuffer(&decoded_[0]);
119 }
120 }
121
122 return true;
123 }
124
125
126 bool DecodeUsingOrthanc()
127 {
128 /**
129 * This is a DICOM image that cannot be properly decoded by
130 * GDCM. Let's give a try with the Orthanc built-in decoder.
131 **/
132 std::string file = "/instances/" + instanceId_;
133
134 const gdcm::Image& image = GetImage();
135 if (image.GetPixelFormat().GetSamplesPerPixel() == 3 ||
136 image.GetPixelFormat().GetSamplesPerPixel() == 4)
137 {
138 file += "/preview";
139 }
140 else
141 {
142 file += "/image-uint16";
143 }
144
145 std::string png;
146 if (!GetStringFromOrthanc(png, context_, file))
147 {
148 return false;
149 }
150 else
151 {
152 try
153 {
154 png_.ReadFromMemory(png);
155 insidePng_ = true;
156 return true;
157 }
158 catch (Orthanc::OrthancException&)
159 {
160 return false;
161 }
162 }
163 }
164
165
166 bool Decode()
167 {
168 if (isDecoded_)
169 {
170 return true;
171 }
172
173 if (DecodeUsingGdcm())
174 {
175 isDecoded_ = true;
176 return true;
177 }
178
179 // GDCM cannot decode this image, try and use Orthanc built-in functions
180 photometric_.reset();
181 interleaved_.reset();
182 decoded_.clear();
183
184 if (DecodeUsingOrthanc())
185 {
186 isDecoded_ = true;
187 return true;
188 }
189 else
190 {
191 return false;
192 }
193 }
194
195
196 public:
197 PImpl(OrthancPluginContext* context,
198 const std::string& instanceId) :
199 context_(context),
200 instanceId_(instanceId),
201 insidePng_(false),
202 isDecoded_(false)
203 {
204 }
205
206
207 const gdcm::DataSet& GetDataSet() const
208 {
209 return reader_.GetFile().GetDataSet();
210 }
211
47 212
48 const gdcm::Image& GetImage() const 213 const gdcm::Image& GetImage() const
49 { 214 {
50 if (interleaved_.get() != NULL) 215 if (interleaved_.get() != NULL)
51 { 216 {
59 224
60 return reader_.GetImage(); 225 return reader_.GetImage();
61 } 226 }
62 227
63 228
64 const gdcm::DataSet& GetDataSet() const 229 void Parse(const std::string& dicom)
65 { 230 {
66 return reader_.GetFile().GetDataSet(); 231 // Prepare a memory stream over the DICOM instance
232 std::stringstream stream(dicom);
233
234 // Parse the DICOM instance using GDCM
235 reader_.SetStream(stream);
236 if (!reader_.Read())
237 {
238 throw Orthanc::OrthancException("GDCM cannot extract an image from this DICOM instance");
239 }
240 }
241
242
243 bool GetAccessor(Orthanc::ImageAccessor& accessor)
244 {
245 if (!Decode())
246 {
247 return false;
248 }
249
250 if (insidePng_)
251 {
252 // The image was decoded using Orthanc's built-in REST API
253 accessor = png_;
254 return true;
255 }
256
257 const gdcm::Image& image = GetImage();
258
259 size_t size = decoded_.size();
260 void* buffer = (size ? &decoded_[0] : NULL);
261 unsigned int height = image.GetRows();
262 unsigned int width = image.GetColumns();
263
264 if (image.GetPixelFormat().GetSamplesPerPixel() == 1 &&
265 (image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME1 ||
266 image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME2))
267 {
268 switch (image.GetPixelFormat())
269 {
270 case gdcm::PixelFormat::UINT16:
271 accessor.AssignWritable(Orthanc::PixelFormat_Grayscale16, width, height, 2 * width, buffer);
272 return true;
273
274 case gdcm::PixelFormat::INT16:
275 accessor.AssignWritable(Orthanc::PixelFormat_SignedGrayscale16, width, height, 2 * width, buffer);
276 return true;
277
278 case gdcm::PixelFormat::UINT8:
279 accessor.AssignWritable(Orthanc::PixelFormat_Grayscale8, width, height, width, buffer);
280 return true;
281
282 default:
283 return false;
284 }
285 }
286 else if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
287 image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB)
288 {
289 switch (image.GetPixelFormat())
290 {
291 case gdcm::PixelFormat::UINT8:
292 accessor.AssignWritable(Orthanc::PixelFormat_RGB24, width, height, 3 * width, buffer);
293 return true;
294
295 default:
296 return false;
297 }
298 }
299
300 return false;
67 } 301 }
68 }; 302 };
69 303
70 304
71 template <typename TargetType, typename SourceType> 305 template <typename TargetType, typename SourceType>
110 } 344 }
111 } 345 }
112 } 346 }
113 347
114 348
115 void ParsedDicomImage::Setup(const std::string& dicom) 349 ParsedDicomImage::ParsedDicomImage(OrthancPluginContext* context,
350 const std::string& instanceId) :
351 pimpl_(new PImpl(context, instanceId))
116 { 352 {
117 // Prepare a memory stream over the DICOM instance 353 std::string file = "/instances/" + instanceId + "/file";
118 std::stringstream stream(dicom); 354
119 355 std::string dicom;
120 // Parse the DICOM instance using GDCM 356 if (!GetStringFromOrthanc(dicom, context, file))
121 pimpl_->reader_.SetStream(stream); 357 {
122 if (!pimpl_->reader_.Read()) 358 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
123 { 359 }
124 throw Orthanc::OrthancException("GDCM cannot extract an image from this DICOM instance"); 360
125 } 361 pimpl_->Parse(dicom);
126
127 // Change photometric interpretation, if required
128 {
129 const gdcm::Image& image = pimpl_->GetImage();
130 if (image.GetPixelFormat().GetSamplesPerPixel() == 1)
131 {
132 if (image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME1 &&
133 image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME2)
134 {
135 pimpl_->photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
136 pimpl_->photometric_->SetInput(image);
137 pimpl_->photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2);
138 if (!pimpl_->photometric_->Change())
139 {
140 throw Orthanc::OrthancException("GDCM cannot change the photometric interpretation");
141 }
142 }
143 }
144 else
145 {
146 if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
147 image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
148 {
149 pimpl_->photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
150 pimpl_->photometric_->SetInput(image);
151 pimpl_->photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::RGB);
152 if (!pimpl_->photometric_->Change())
153 {
154 throw Orthanc::OrthancException("GDCM cannot change the photometric interpretation");
155 }
156 }
157 }
158 }
159
160 // Possibly convert planar configuration to interleaved
161 {
162 const gdcm::Image& image = pimpl_->GetImage();
163 if (image.GetPlanarConfiguration() != 0 &&
164 image.GetPixelFormat().GetSamplesPerPixel() != 1)
165 {
166 pimpl_->interleaved_.reset(new gdcm::ImageChangePlanarConfiguration());
167 pimpl_->interleaved_->SetInput(image);
168 if (!pimpl_->interleaved_->Change())
169 {
170 throw Orthanc::OrthancException("GDCM cannot change the planar configuration to interleaved");
171 }
172 }
173 }
174
175 // Decode the image to the memory buffer
176 {
177 const gdcm::Image& image = pimpl_->GetImage();
178 pimpl_->decoded_.resize(image.GetBufferLength());
179
180 if (pimpl_->decoded_.size() > 0)
181 {
182 image.GetBuffer(&pimpl_->decoded_[0]);
183 }
184 }
185 }
186
187
188 ParsedDicomImage::ParsedDicomImage(const std::string& dicom) : pimpl_(new PImpl)
189 {
190 Setup(dicom);
191 } 362 }
192 363
193 364
194 bool ParsedDicomImage::GetTag(std::string& result, 365 bool ParsedDicomImage::GetTag(std::string& result,
195 uint16_t group, 366 uint16_t group,
216 387
217 return false; 388 return false;
218 } 389 }
219 390
220 391
221 bool ParsedDicomImage::GetAccessor(Orthanc::ImageAccessor& accessor)
222 {
223 const gdcm::Image& image = pimpl_->GetImage();
224
225 size_t size = pimpl_->decoded_.size();
226 void* buffer = (size ? &pimpl_->decoded_[0] : NULL);
227 unsigned int height = image.GetRows();
228 unsigned int width = image.GetColumns();
229
230 if (image.GetPixelFormat().GetSamplesPerPixel() == 1 &&
231 (image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME1 ||
232 image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME2))
233 {
234 switch (image.GetPixelFormat())
235 {
236 case gdcm::PixelFormat::UINT16:
237 accessor.AssignWritable(Orthanc::PixelFormat_Grayscale16, width, height, 2 * width, buffer);
238 return true;
239
240 case gdcm::PixelFormat::INT16:
241 accessor.AssignWritable(Orthanc::PixelFormat_SignedGrayscale16, width, height, 2 * width, buffer);
242 return true;
243
244 case gdcm::PixelFormat::UINT8:
245 accessor.AssignWritable(Orthanc::PixelFormat_Grayscale8, width, height, width, buffer);
246 return true;
247
248 default:
249 return false;
250 }
251 }
252 else if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
253 image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB)
254 {
255 switch (image.GetPixelFormat())
256 {
257 case gdcm::PixelFormat::UINT8:
258 accessor.AssignWritable(Orthanc::PixelFormat_RGB24, width, height, 3 * width, buffer);
259 return true;
260
261 default:
262 return false;
263 }
264 }
265
266 return false;
267 }
268
269 bool ParsedDicomImage::GetCornerstoneMetadata(Json::Value& json) 392 bool ParsedDicomImage::GetCornerstoneMetadata(Json::Value& json)
270 { 393 {
271 using namespace Orthanc; 394 using namespace Orthanc;
272 395
273 ImageAccessor accessor; 396 ImageAccessor accessor;
274 if (!GetAccessor(accessor)) 397 if (!pimpl_->GetAccessor(accessor))
275 { 398 {
276 return false; 399 return false;
277 } 400 }
278 401
279 float windowCenter, windowWidth; 402 float windowCenter, windowWidth;
353 uint8_t compressionLevel /* between 0 and 9 */) 476 uint8_t compressionLevel /* between 0 and 9 */)
354 { 477 {
355 using namespace Orthanc; 478 using namespace Orthanc;
356 479
357 ImageAccessor accessor; 480 ImageAccessor accessor;
358 if (!GetAccessor(accessor)) 481 if (!pimpl_->GetAccessor(accessor))
359 { 482 {
360 return false; 483 return false;
361 } 484 }
362 485
363 result = Json::objectValue; 486 result = Json::objectValue;
420 uint8_t quality /* between 0 and 100 */) 543 uint8_t quality /* between 0 and 100 */)
421 { 544 {
422 using namespace Orthanc; 545 using namespace Orthanc;
423 546
424 ImageAccessor accessor; 547 ImageAccessor accessor;
425 if (!GetAccessor(accessor)) 548 if (!pimpl_->GetAccessor(accessor))
426 { 549 {
427 return false; 550 return false;
428 } 551 }
429 552
430 result = Json::objectValue; 553 result = Json::objectValue;