Mercurial > hg > orthanc
comparison OrthancServer/FromDcmtkBridge.cpp @ 682:efc4928be6fb
Recover pixel data for more transfer syntaxes (notably JPEG)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 30 Jan 2014 15:05:14 +0100 |
parents | 27acbc5985f5 |
children | 2d0a347e8cfc |
comparison
equal
deleted
inserted
replaced
680:571583642ce2 | 682:efc4928be6fb |
---|---|
128 | 128 |
129 #include <boost/math/special_functions/round.hpp> | 129 #include <boost/math/special_functions/round.hpp> |
130 #include <glog/logging.h> | 130 #include <glog/logging.h> |
131 #include <dcmtk/dcmdata/dcostrmb.h> | 131 #include <dcmtk/dcmdata/dcostrmb.h> |
132 | 132 |
133 | |
134 static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; | |
135 | |
136 | |
137 | |
133 namespace Orthanc | 138 namespace Orthanc |
134 { | 139 { |
135 void ParsedDicomFile::Setup(const char* buffer, size_t size) | 140 void ParsedDicomFile::Setup(const char* buffer, size_t size) |
136 { | 141 { |
137 DcmInputBufferStream is; | 142 DcmInputBufferStream is; |
212 | 217 |
213 output.AnswerJson(v); | 218 output.AnswerJson(v); |
214 } | 219 } |
215 | 220 |
216 | 221 |
217 static void AnswerPixelData(RestApiOutput& output, | 222 static unsigned int GetPixelDataBlockCount(DcmPixelData& pixelData, |
218 DcmPixelData& pixelData, | 223 E_TransferSyntax transferSyntax) |
219 E_TransferSyntax transferSyntax) | |
220 { | 224 { |
221 DcmPixelSequence* pixelSequence = NULL; | 225 DcmPixelSequence* pixelSequence = NULL; |
222 if (pixelData.getEncapsulatedRepresentation | 226 if (pixelData.getEncapsulatedRepresentation |
223 (transferSyntax, NULL, pixelSequence).good() && pixelSequence) | 227 (transferSyntax, NULL, pixelSequence).good() && pixelSequence) |
224 { | 228 { |
225 for (unsigned long i = 0; i < pixelSequence->card(); i++) | 229 return pixelSequence->card(); |
226 { | 230 } |
227 DcmPixelItem* pixelItem = NULL; | 231 else |
228 if (pixelSequence->getItem(pixelItem, i).good() && pixelItem) | 232 { |
229 { | 233 return 1; |
230 Uint8* b = NULL; | |
231 if (pixelItem->getUint8Array(b).good() && b) | |
232 { | |
233 // JPEG-LS (lossless) | |
234 // http://gdcm.sourceforge.net/wiki/index.php/Tools/ffmpeg#JPEG_LS | |
235 // http://www.stat.columbia.edu/~jakulin/jpeg-ls/ | |
236 // http://itohws03.ee.noda.sut.ac.jp/~matsuda/mrp/ | |
237 | |
238 printf("ITEM: %d %d\n", transferSyntax, pixelItem->getLength()); | |
239 char buf[64]; | |
240 sprintf(buf, "/tmp/toto-%06ld.jpg", i); | |
241 FILE* fp = fopen(buf, "wb"); | |
242 fwrite(b, pixelItem->getLength(), 1, fp); | |
243 fclose(fp); | |
244 } | |
245 } | |
246 } | |
247 } | 234 } |
248 } | 235 } |
249 | 236 |
250 | 237 |
251 static void AnswerDicomField(RestApiOutput& output, | 238 static void AnswerDicomField(RestApiOutput& output, |
252 DcmElement& element, | 239 DcmElement& element, |
253 E_TransferSyntax transferSyntax) | 240 E_TransferSyntax transferSyntax) |
254 { | 241 { |
255 // This element is not a sequence. Test if it is pixel data. | |
256 if (element.getTag().getGTag() == DICOM_TAG_PIXEL_DATA.GetGroup() && | |
257 element.getTag().getETag() == DICOM_TAG_PIXEL_DATA.GetElement()) | |
258 { | |
259 try | |
260 { | |
261 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(element); | |
262 AnswerPixelData(output, pixelData, transferSyntax); | |
263 //return; | |
264 } | |
265 catch (std::bad_cast&) | |
266 { | |
267 } | |
268 } | |
269 | |
270 | |
271 // This element is nor a sequence, neither a pixel-data | 242 // This element is nor a sequence, neither a pixel-data |
272 std::string buffer; | 243 std::string buffer; |
273 buffer.resize(65536); | 244 buffer.resize(65536); |
274 Uint32 length = element.getLength(transferSyntax); | 245 Uint32 length = element.getLength(transferSyntax); |
275 Uint32 offset = 0; | 246 Uint32 offset = 0; |
276 | 247 |
277 output.GetLowLevelOutput().SendOkHeader("application/octet-stream", true, length, NULL); | 248 output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL); |
278 | 249 |
279 while (offset < length) | 250 while (offset < length) |
280 { | 251 { |
281 Uint32 nbytes; | 252 Uint32 nbytes; |
282 if (length - offset < buffer.size()) | 253 if (length - offset < buffer.size()) |
302 } | 273 } |
303 } | 274 } |
304 | 275 |
305 output.MarkLowLevelOutputDone(); | 276 output.MarkLowLevelOutputDone(); |
306 } | 277 } |
278 | |
279 | |
280 static bool AnswerPixelData(RestApiOutput& output, | |
281 DcmItem& dicom, | |
282 E_TransferSyntax transferSyntax, | |
283 const std::string* blockUri) | |
284 { | |
285 DcmTag k(DICOM_TAG_PIXEL_DATA.GetGroup(), | |
286 DICOM_TAG_PIXEL_DATA.GetElement()); | |
287 | |
288 DcmElement *element = NULL; | |
289 if (!dicom.findAndGetElement(k, element).good() || | |
290 element == NULL) | |
291 { | |
292 return false; | |
293 } | |
294 | |
295 try | |
296 { | |
297 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); | |
298 if (blockUri == NULL) | |
299 { | |
300 // The user asks how many blocks are presents in this pixel data | |
301 unsigned int blocks = GetPixelDataBlockCount(pixelData, transferSyntax); | |
302 | |
303 Json::Value result(Json::arrayValue); | |
304 for (unsigned int i = 0; i < blocks; i++) | |
305 { | |
306 result.append(boost::lexical_cast<std::string>(i)); | |
307 } | |
308 | |
309 output.AnswerJson(result); | |
310 return true; | |
311 } | |
312 | |
313 | |
314 unsigned int block = boost::lexical_cast<unsigned int>(*blockUri); | |
315 | |
316 if (block < GetPixelDataBlockCount(pixelData, transferSyntax)) | |
317 { | |
318 DcmPixelSequence* pixelSequence = NULL; | |
319 if (pixelData.getEncapsulatedRepresentation | |
320 (transferSyntax, NULL, pixelSequence).good() && pixelSequence) | |
321 { | |
322 // This is the case for JPEG transfer syntaxes | |
323 if (block < pixelSequence->card()) | |
324 { | |
325 DcmPixelItem* pixelItem = NULL; | |
326 if (pixelSequence->getItem(pixelItem, block).good() && pixelItem) | |
327 { | |
328 if (pixelItem->getLength() == 0) | |
329 { | |
330 output.AnswerBuffer(NULL, 0, CONTENT_TYPE_OCTET_STREAM); | |
331 return true; | |
332 } | |
333 | |
334 Uint8* buffer = NULL; | |
335 if (pixelItem->getUint8Array(buffer).good() && buffer) | |
336 { | |
337 output.AnswerBuffer(buffer, pixelItem->getLength(), CONTENT_TYPE_OCTET_STREAM); | |
338 return true; | |
339 } | |
340 } | |
341 } | |
342 } | |
343 else | |
344 { | |
345 // This is the case for raw, uncompressed image buffers | |
346 assert(*blockUri == "0"); | |
347 AnswerDicomField(output, *element, transferSyntax); | |
348 } | |
349 } | |
350 } | |
351 catch (boost::bad_lexical_cast&) | |
352 { | |
353 // The URI entered by the user is not a number | |
354 } | |
355 catch (std::bad_cast&) | |
356 { | |
357 // This should never happen | |
358 } | |
359 | |
360 return false; | |
361 } | |
362 | |
363 | |
307 | 364 |
308 static void SendPathValueForLeaf(RestApiOutput& output, | 365 static void SendPathValueForLeaf(RestApiOutput& output, |
309 const std::string& tag, | 366 const std::string& tag, |
310 DcmItem& dicom, | 367 DcmItem& dicom, |
311 E_TransferSyntax transferSyntax) | 368 E_TransferSyntax transferSyntax) |
334 | 391 |
335 void ParsedDicomFile::SendPathValue(RestApiOutput& output, | 392 void ParsedDicomFile::SendPathValue(RestApiOutput& output, |
336 const UriComponents& uri) | 393 const UriComponents& uri) |
337 { | 394 { |
338 DcmItem* dicom = file_->getDataset(); | 395 DcmItem* dicom = file_->getDataset(); |
396 E_TransferSyntax transferSyntax = file_->getDataset()->getOriginalXfer(); | |
397 | |
398 // Special case: Accessing the pixel data | |
399 if (uri.size() == 1 || | |
400 uri.size() == 2) | |
401 { | |
402 DcmTagKey tag; | |
403 ParseTagAndGroup(tag, uri[0]); | |
404 | |
405 if (tag.getGroup() == DICOM_TAG_PIXEL_DATA.GetGroup() && | |
406 tag.getElement() == DICOM_TAG_PIXEL_DATA.GetElement()) | |
407 { | |
408 AnswerPixelData(output, *dicom, transferSyntax, uri.size() == 1 ? NULL : &uri[1]); | |
409 return; | |
410 } | |
411 } | |
339 | 412 |
340 // Go down in the tag hierarchy according to the URI | 413 // Go down in the tag hierarchy according to the URI |
341 for (size_t pos = 0; pos < uri.size() / 2; pos++) | 414 for (size_t pos = 0; pos < uri.size() / 2; pos++) |
342 { | 415 { |
343 size_t index; | 416 size_t index; |
367 { | 440 { |
368 SendPathValueForDictionary(output, *dicom); | 441 SendPathValueForDictionary(output, *dicom); |
369 } | 442 } |
370 else | 443 else |
371 { | 444 { |
372 SendPathValueForLeaf(output, uri.back(), *dicom, file_->getDataset()->getOriginalXfer()); | 445 SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); |
373 } | 446 } |
374 } | 447 } |
375 | 448 |
376 | 449 |
377 | 450 |
758 void ParsedDicomFile::Answer(RestApiOutput& output) | 831 void ParsedDicomFile::Answer(RestApiOutput& output) |
759 { | 832 { |
760 std::string serialized; | 833 std::string serialized; |
761 if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset())) | 834 if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, file_->getDataset())) |
762 { | 835 { |
763 output.AnswerBuffer(serialized, "application/octet-stream"); | 836 output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM); |
764 } | 837 } |
765 } | 838 } |
766 | 839 |
767 | 840 |
768 | 841 |