Mercurial > hg > orthanc
comparison OrthancServer/Internals/DicomImageDecoder.cpp @ 854:ff530685e46a jpeg
fast version of image copy
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 06 Jun 2014 12:35:08 +0200 |
parents | 839be3022203 |
children | ebc41566f742 |
comparison
equal
deleted
inserted
replaced
853:839be3022203 | 854:ff530685e46a |
---|---|
76 =========================================================================*/ | 76 =========================================================================*/ |
77 | 77 |
78 | 78 |
79 | 79 |
80 #include "../../Core/OrthancException.h" | 80 #include "../../Core/OrthancException.h" |
81 #include "../../Core/ImageFormats/ImageProcessing.h" | |
81 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" | 82 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" |
82 #include "../ToDcmtkBridge.h" | 83 #include "../ToDcmtkBridge.h" |
83 #include "../FromDcmtkBridge.h" | 84 #include "../FromDcmtkBridge.h" |
84 | 85 |
85 #include <glog/logging.h> | 86 #include <glog/logging.h> |
178 { | 179 { |
179 assert(slowAccessor_.get() != NULL); | 180 assert(slowAccessor_.get() != NULL); |
180 return slowAccessor_->GetInformation().GetChannelCount(); | 181 return slowAccessor_->GetInformation().GetChannelCount(); |
181 } | 182 } |
182 | 183 |
183 const DicomIntegerPixelAccessor GetAccessor() const | 184 const DicomIntegerPixelAccessor& GetAccessor() const |
184 { | 185 { |
185 assert(slowAccessor_.get() != NULL); | 186 assert(slowAccessor_.get() != NULL); |
186 return *slowAccessor_; | 187 return *slowAccessor_; |
187 } | 188 } |
188 | 189 |
311 | 312 |
312 | 313 |
313 void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target, | 314 void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target, |
314 DcmDataset& dataset) | 315 DcmDataset& dataset) |
315 { | 316 { |
316 OFString value; | 317 DicomMap m; |
317 | 318 FromDcmtkBridge::Convert(m, dataset); |
318 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_COLUMNS), value).good()) | 319 |
319 { | 320 DicomImageInformation info(m); |
320 throw OrthancException(ErrorCode_BadFileFormat); | 321 PixelFormat format; |
321 } | 322 |
322 | 323 if (!info.ExtractPixelFormat(format)) |
323 unsigned int width = boost::lexical_cast<unsigned int>(value.c_str()); | 324 { |
324 | 325 LOG(WARNING) << "Unsupported DICOM image: " << info.GetBitsStored() |
325 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_ROWS), value).good()) | 326 << "bpp, " << info.GetChannelCount() << " channels, " |
326 { | 327 << (info.IsSigned() ? "signed" : "unsigned"); |
327 throw OrthancException(ErrorCode_BadFileFormat); | |
328 } | |
329 | |
330 unsigned int height = boost::lexical_cast<unsigned int>(value.c_str()); | |
331 | |
332 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_BITS_STORED), value).good()) | |
333 { | |
334 throw OrthancException(ErrorCode_BadFileFormat); | |
335 } | |
336 | |
337 unsigned int bitsStored = boost::lexical_cast<unsigned int>(value.c_str()); | |
338 | |
339 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_REPRESENTATION), value).good()) | |
340 { | |
341 throw OrthancException(ErrorCode_BadFileFormat); | |
342 } | |
343 | |
344 bool isSigned = (boost::lexical_cast<unsigned int>(value.c_str()) != 0); | |
345 | |
346 unsigned int samplesPerPixel = 1; // By default | |
347 if (dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_SAMPLES_PER_PIXEL), value).good()) | |
348 { | |
349 samplesPerPixel = boost::lexical_cast<unsigned int>(value.c_str()); | |
350 } | |
351 | |
352 target.SetHeight(height); | |
353 target.SetWidth(width); | |
354 | |
355 if (bitsStored == 8 && samplesPerPixel == 1 && !isSigned) | |
356 { | |
357 target.SetFormat(PixelFormat_Grayscale8); | |
358 } | |
359 else if (bitsStored == 8 && samplesPerPixel == 3 && !isSigned) | |
360 { | |
361 target.SetFormat(PixelFormat_RGB24); | |
362 } | |
363 else if (bitsStored >= 9 && bitsStored <= 16 && samplesPerPixel == 1 && !isSigned) | |
364 { | |
365 target.SetFormat(PixelFormat_Grayscale16); | |
366 } | |
367 else if (bitsStored >= 9 && bitsStored <= 16 && samplesPerPixel == 1 && isSigned) | |
368 { | |
369 target.SetFormat(PixelFormat_SignedGrayscale16); | |
370 } | |
371 else | |
372 { | |
373 LOG(WARNING) << "Unsupported DICOM image: " << bitsStored << "bpp, " | |
374 << samplesPerPixel << " channels, " << (isSigned ? "signed" : "unsigned"); | |
375 throw OrthancException(ErrorCode_NotImplemented); | 328 throw OrthancException(ErrorCode_NotImplemented); |
376 } | 329 } |
330 | |
331 target.SetHeight(info.GetHeight()); | |
332 target.SetWidth(info.GetWidth()); | |
333 target.SetFormat(format); | |
377 } | 334 } |
378 | 335 |
379 | 336 |
380 bool DicomImageDecoder::IsJpegLossless(const DcmDataset& dataset) | 337 bool DicomImageDecoder::IsJpegLossless(const DcmDataset& dataset) |
381 { | 338 { |
453 * Resize the target image, with some sanity checks. | 410 * Resize the target image, with some sanity checks. |
454 **/ | 411 **/ |
455 | 412 |
456 SetupImageBuffer(target, dataset); | 413 SetupImageBuffer(target, dataset); |
457 | 414 |
458 if (target.GetWidth() != target.GetWidth() || | 415 if (source.GetWidth() != target.GetWidth() || |
459 target.GetHeight() != target.GetHeight()) | 416 source.GetHeight() != target.GetHeight()) |
460 { | 417 { |
461 throw OrthancException(ErrorCode_InternalError); | 418 throw OrthancException(ErrorCode_InternalError); |
462 } | 419 } |
463 | 420 |
464 bool ok; | 421 bool ok; |
488 throw OrthancException(ErrorCode_InternalError); | 445 throw OrthancException(ErrorCode_InternalError); |
489 } | 446 } |
490 | 447 |
491 | 448 |
492 /** | 449 /** |
450 * If the format of the DICOM buffer is natively supported, use a | |
451 * direct access to copy its values. | |
452 **/ | |
453 | |
454 ImageAccessor targetAccessor(target.GetAccessor()); | |
455 const DicomImageInformation& info = source.GetAccessor().GetInformation(); | |
456 | |
457 bool fastVersionSuccess = false; | |
458 PixelFormat sourceFormat; | |
459 if (info.ExtractPixelFormat(sourceFormat)) | |
460 { | |
461 try | |
462 { | |
463 ImageAccessor sourceImage; | |
464 sourceImage.AssignReadOnly(sourceFormat, | |
465 info.GetWidth(), | |
466 info.GetHeight(), | |
467 info.GetWidth() * info.GetBytesPerPixel(), | |
468 source.GetAccessor().GetPixelData()); | |
469 | |
470 ImageProcessing::Convert(targetAccessor, sourceImage); | |
471 ImageProcessing::ShiftRight(targetAccessor, info.GetShift()); | |
472 fastVersionSuccess = true; | |
473 } | |
474 catch (OrthancException&) | |
475 { | |
476 // Unsupported conversion | |
477 } | |
478 } | |
479 | |
480 | |
481 /** | |
493 * Loop over the DICOM buffer, storing its value into the target | 482 * Loop over the DICOM buffer, storing its value into the target |
494 * image. | 483 * image. |
495 **/ | 484 **/ |
496 | 485 |
497 ImageAccessor accessor(target.GetAccessor()); | 486 if (!fastVersionSuccess) |
498 | 487 { |
499 switch (target.GetFormat()) | 488 switch (target.GetFormat()) |
500 { | 489 { |
501 case PixelFormat_RGB24: | 490 case PixelFormat_RGB24: |
502 case PixelFormat_RGBA32: | 491 case PixelFormat_RGBA32: |
503 case PixelFormat_Grayscale8: | 492 case PixelFormat_Grayscale8: |
504 CopyPixels<uint8_t>(accessor, source.GetAccessor()); | 493 CopyPixels<uint8_t>(targetAccessor, source.GetAccessor()); |
505 break; | 494 break; |
506 | 495 |
507 case PixelFormat_Grayscale16: | 496 case PixelFormat_Grayscale16: |
508 CopyPixels<uint16_t>(accessor, source.GetAccessor()); | 497 CopyPixels<uint16_t>(targetAccessor, source.GetAccessor()); |
509 break; | 498 break; |
510 | 499 |
511 case PixelFormat_SignedGrayscale16: | 500 case PixelFormat_SignedGrayscale16: |
512 CopyPixels<int16_t>(accessor, source.GetAccessor()); | 501 CopyPixels<int16_t>(targetAccessor, source.GetAccessor()); |
513 break; | 502 break; |
514 | 503 |
515 default: | 504 default: |
516 throw OrthancException(ErrorCode_InternalError); | 505 throw OrthancException(ErrorCode_InternalError); |
506 } | |
517 } | 507 } |
518 } | 508 } |
519 | 509 |
520 | 510 |
521 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 | 511 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 |
542 throw OrthancException(ErrorCode_BadFileFormat); | 532 throw OrthancException(ErrorCode_BadFileFormat); |
543 } | 533 } |
544 | 534 |
545 SetupImageBuffer(target, dataset); | 535 SetupImageBuffer(target, dataset); |
546 | 536 |
547 ImageAccessor accessor(target.GetAccessor()); | 537 ImageAccessor targetAccessor(target.GetAccessor()); |
548 | 538 |
549 /** | 539 /** |
550 * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK | 540 * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK |
551 * are exactly the same, except for the "supportedTransferSyntax()" | 541 * are exactly the same, except for the "supportedTransferSyntax()" |
552 * virtual function. | 542 * virtual function. |
558 | 548 |
559 Uint32 startFragment = 0; // Default | 549 Uint32 startFragment = 0; // Default |
560 OFString decompressedColorModel; // Out | 550 OFString decompressedColorModel; // Out |
561 DJ_RPLossless representationParameter; | 551 DJ_RPLossless representationParameter; |
562 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters, | 552 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters, |
563 &dataset, frame, startFragment, accessor.GetBuffer(), | 553 &dataset, frame, startFragment, targetAccessor.GetBuffer(), |
564 accessor.GetSize(), decompressedColorModel); | 554 targetAccessor.GetSize(), decompressedColorModel); |
565 | 555 |
566 if (!c.good()) | 556 if (!c.good()) |
567 { | 557 { |
568 throw OrthancException(ErrorCode_InternalError); | 558 throw OrthancException(ErrorCode_InternalError); |
569 } | 559 } |