# HG changeset patch # User Sebastien Jodogne # Date 1402050908 -7200 # Node ID ff530685e46a711c71dd45720c3b4659c930bb79 # Parent 839be3022203030d9c88f03918198338daccf0bc fast version of image copy diff -r 839be3022203 -r ff530685e46a Core/DicomFormat/DicomImageInformation.cpp --- a/Core/DicomFormat/DicomImageInformation.cpp Fri Jun 06 11:45:16 2014 +0200 +++ b/Core/DicomFormat/DicomImageInformation.cpp Fri Jun 06 12:35:08 2014 +0200 @@ -55,11 +55,19 @@ { width_ = boost::lexical_cast(values.GetValue(DICOM_TAG_COLUMNS).AsString()); height_ = boost::lexical_cast(values.GetValue(DICOM_TAG_ROWS).AsString()); - samplesPerPixel_ = boost::lexical_cast(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString()); bitsAllocated_ = boost::lexical_cast(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString()); try { + samplesPerPixel_ = boost::lexical_cast(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString()); + } + catch (OrthancException&) + { + samplesPerPixel_ = 1; // Assume 1 color channel + } + + try + { bitsStored_ = boost::lexical_cast(values.GetValue(DICOM_TAG_BITS_STORED).AsString()); } catch (OrthancException&) @@ -147,4 +155,39 @@ isPlanar_ = (planarConfiguration != 0 ? true : false); isSigned_ = (pixelRepresentation != 0 ? true : false); } + + + bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const + { + if (IsPlanar()) + { + return false; + } + + if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_Grayscale8; + return true; + } + + if (GetBitsStored() == 8 && GetChannelCount() == 3 && !IsSigned()) + { + format = PixelFormat_RGB24; + return true; + } + + if (GetBitsStored() >= 9 && GetBitsStored() <= 16 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_Grayscale16; + return true; + } + + if (GetBitsStored() >= 9 && GetBitsStored() <= 16 && GetChannelCount() == 1 && IsSigned()) + { + format = PixelFormat_SignedGrayscale16; + return true; + } + + return false; + } } diff -r 839be3022203 -r ff530685e46a Core/DicomFormat/DicomImageInformation.h --- a/Core/DicomFormat/DicomImageInformation.h Fri Jun 06 11:45:16 2014 +0200 +++ b/Core/DicomFormat/DicomImageInformation.h Fri Jun 06 12:35:08 2014 +0200 @@ -102,11 +102,6 @@ return highBit_; } - unsigned int GetSamplesPerPixel() const - { - return samplesPerPixel_; - } - bool IsPlanar() const { return isPlanar_; @@ -116,5 +111,7 @@ { return highBit_ + 1 - bitsStored_; } + + bool ExtractPixelFormat(PixelFormat& format) const; }; } diff -r 839be3022203 -r ff530685e46a Core/DicomFormat/DicomIntegerPixelAccessor.cpp --- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Fri Jun 06 11:45:16 2014 +0200 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Fri Jun 06 12:35:08 2014 +0200 @@ -55,7 +55,7 @@ { frame_ = 0; frameOffset_ = (information_.GetHeight() * information_.GetWidth() * - information_.GetBytesPerPixel() * information_.GetSamplesPerPixel()); + information_.GetBytesPerPixel() * information_.GetChannelCount()); if (information_.GetNumberOfFrames() * frameOffset_ > size) { @@ -83,7 +83,7 @@ * means the order of the pixel values sent shall be R1, G1, B1, * R2, G2, B2, ..., etc. **/ - rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel() * information_.GetSamplesPerPixel(); + rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel() * information_.GetChannelCount(); } else { @@ -130,7 +130,7 @@ unsigned int y, unsigned int channel) const { - assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetSamplesPerPixel()); + assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetChannelCount()); const uint8_t* pixel = reinterpret_cast(pixelData_) + y * rowOffset_ + frame_ * frameOffset_; @@ -144,7 +144,7 @@ * means the order of the pixel values sent shall be R1, G1, B1, * R2, G2, B2, ..., etc. **/ - pixel += channel * information_.GetBytesPerPixel() + x * information_.GetSamplesPerPixel() * information_.GetBytesPerPixel(); + pixel += channel * information_.GetBytesPerPixel() + x * information_.GetChannelCount() * information_.GetBytesPerPixel(); } else { @@ -153,8 +153,8 @@ * this means the order of the pixel values sent is R1, R2, R3, * ..., G1, G2, G3, ..., B1, B2, B3, etc. **/ - assert(frameOffset_ % information_.GetSamplesPerPixel() == 0); - pixel += channel * frameOffset_ / information_.GetSamplesPerPixel() + x * information_.GetBytesPerPixel(); + assert(frameOffset_ % information_.GetChannelCount() == 0); + pixel += channel * frameOffset_ / information_.GetChannelCount() + x * information_.GetBytesPerPixel(); } uint32_t v; diff -r 839be3022203 -r ff530685e46a Core/DicomFormat/DicomIntegerPixelAccessor.h --- a/Core/DicomFormat/DicomIntegerPixelAccessor.h Fri Jun 06 11:45:16 2014 +0200 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h Fri Jun 06 12:35:08 2014 +0200 @@ -75,5 +75,10 @@ int32_t& max) const; int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const; + + const void* GetPixelData() const + { + return pixelData_; + } }; } diff -r 839be3022203 -r ff530685e46a Core/ImageFormats/ImageProcessing.cpp --- a/Core/ImageFormats/ImageProcessing.cpp Fri Jun 06 11:45:16 2014 +0200 +++ b/Core/ImageFormats/ImageProcessing.cpp Fri Jun 06 12:35:08 2014 +0200 @@ -60,10 +60,11 @@ for (unsigned int y = 0; y < source.GetHeight(); y++) { - memcpy(target.GetRow(y), source.GetRow(y), lineSize); + memcpy(target.GetRow(y), source.GetConstRow(y), lineSize); } } + void ImageProcessing::Convert(ImageAccessor& target, const ImageAccessor& source) { @@ -78,5 +79,23 @@ Copy(target, source); return; } + + throw OrthancException(ErrorCode_NotImplemented); } + + + void ImageProcessing::ShiftRight(ImageAccessor& image, + unsigned int shift) + { + if (image.GetWidth() == 0 || + image.GetHeight() == 0 || + shift == 0) + { + // Nothing to do + return; + } + + throw OrthancException(ErrorCode_NotImplemented); + } + } diff -r 839be3022203 -r ff530685e46a Core/ImageFormats/ImageProcessing.h --- a/Core/ImageFormats/ImageProcessing.h Fri Jun 06 11:45:16 2014 +0200 +++ b/Core/ImageFormats/ImageProcessing.h Fri Jun 06 12:35:08 2014 +0200 @@ -44,5 +44,8 @@ static void Convert(ImageAccessor& target, const ImageAccessor& source); + + static void ShiftRight(ImageAccessor& target, + unsigned int shift); }; } diff -r 839be3022203 -r ff530685e46a LinuxCompilation.txt --- a/LinuxCompilation.txt Fri Jun 06 11:45:16 2014 +0200 +++ b/LinuxCompilation.txt Fri Jun 06 12:35:08 2014 +0200 @@ -136,24 +136,25 @@ libsqlite3-dev libssl-dev zlib1g-dev \ libdcmtk2-dev libboost-all-dev libwrap0-dev libcharls-dev -With JPEG Lossless: +With JPEG: # cmake "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" \ -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DUSE_SYSTEM_JSONCPP=OFF \ -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ - -DENABLE_JPEG_LOSSLESS=ON \ ~/Orthanc -Without JPEG Lossless: +Without JPEG: # cmake "-DDCMTK_LIBRARIES=wrap;oflog" \ -DALLOW_DOWNLOADS=ON \ -DUSE_SYSTEM_MONGOOSE=OFF \ -DUSE_SYSTEM_JSONCPP=OFF \ -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \ + -DENABLE_JPEG=OFF \ + -DENABLE_JPEG_LOSSLESS=OFF \ ~/Orthanc diff -r 839be3022203 -r ff530685e46a OrthancServer/Internals/DicomImageDecoder.cpp --- a/OrthancServer/Internals/DicomImageDecoder.cpp Fri Jun 06 11:45:16 2014 +0200 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Fri Jun 06 12:35:08 2014 +0200 @@ -78,6 +78,7 @@ #include "../../Core/OrthancException.h" +#include "../../Core/ImageFormats/ImageProcessing.h" #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" #include "../ToDcmtkBridge.h" #include "../FromDcmtkBridge.h" @@ -180,7 +181,7 @@ return slowAccessor_->GetInformation().GetChannelCount(); } - const DicomIntegerPixelAccessor GetAccessor() const + const DicomIntegerPixelAccessor& GetAccessor() const { assert(slowAccessor_.get() != NULL); return *slowAccessor_; @@ -313,67 +314,23 @@ void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target, DcmDataset& dataset) { - OFString value; - - if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_COLUMNS), value).good()) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - unsigned int width = boost::lexical_cast(value.c_str()); - - if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_ROWS), value).good()) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - unsigned int height = boost::lexical_cast(value.c_str()); - - if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_BITS_STORED), value).good()) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - unsigned int bitsStored = boost::lexical_cast(value.c_str()); - - if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_REPRESENTATION), value).good()) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - bool isSigned = (boost::lexical_cast(value.c_str()) != 0); + DicomMap m; + FromDcmtkBridge::Convert(m, dataset); - unsigned int samplesPerPixel = 1; // By default - if (dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_SAMPLES_PER_PIXEL), value).good()) - { - samplesPerPixel = boost::lexical_cast(value.c_str()); - } - - target.SetHeight(height); - target.SetWidth(width); - - if (bitsStored == 8 && samplesPerPixel == 1 && !isSigned) - { - target.SetFormat(PixelFormat_Grayscale8); - } - else if (bitsStored == 8 && samplesPerPixel == 3 && !isSigned) + DicomImageInformation info(m); + PixelFormat format; + + if (!info.ExtractPixelFormat(format)) { - target.SetFormat(PixelFormat_RGB24); - } - else if (bitsStored >= 9 && bitsStored <= 16 && samplesPerPixel == 1 && !isSigned) - { - target.SetFormat(PixelFormat_Grayscale16); - } - else if (bitsStored >= 9 && bitsStored <= 16 && samplesPerPixel == 1 && isSigned) - { - target.SetFormat(PixelFormat_SignedGrayscale16); - } - else - { - LOG(WARNING) << "Unsupported DICOM image: " << bitsStored << "bpp, " - << samplesPerPixel << " channels, " << (isSigned ? "signed" : "unsigned"); + LOG(WARNING) << "Unsupported DICOM image: " << info.GetBitsStored() + << "bpp, " << info.GetChannelCount() << " channels, " + << (info.IsSigned() ? "signed" : "unsigned"); throw OrthancException(ErrorCode_NotImplemented); } + + target.SetHeight(info.GetHeight()); + target.SetWidth(info.GetWidth()); + target.SetFormat(format); } @@ -455,8 +412,8 @@ SetupImageBuffer(target, dataset); - if (target.GetWidth() != target.GetWidth() || - target.GetHeight() != target.GetHeight()) + if (source.GetWidth() != target.GetWidth() || + source.GetHeight() != target.GetHeight()) { throw OrthancException(ErrorCode_InternalError); } @@ -490,30 +447,63 @@ /** + * If the format of the DICOM buffer is natively supported, use a + * direct access to copy its values. + **/ + + ImageAccessor targetAccessor(target.GetAccessor()); + const DicomImageInformation& info = source.GetAccessor().GetInformation(); + + bool fastVersionSuccess = false; + PixelFormat sourceFormat; + if (info.ExtractPixelFormat(sourceFormat)) + { + try + { + ImageAccessor sourceImage; + sourceImage.AssignReadOnly(sourceFormat, + info.GetWidth(), + info.GetHeight(), + info.GetWidth() * info.GetBytesPerPixel(), + source.GetAccessor().GetPixelData()); + + ImageProcessing::Convert(targetAccessor, sourceImage); + ImageProcessing::ShiftRight(targetAccessor, info.GetShift()); + fastVersionSuccess = true; + } + catch (OrthancException&) + { + // Unsupported conversion + } + } + + + /** * Loop over the DICOM buffer, storing its value into the target * image. **/ - ImageAccessor accessor(target.GetAccessor()); - - switch (target.GetFormat()) + if (!fastVersionSuccess) { - case PixelFormat_RGB24: - case PixelFormat_RGBA32: - case PixelFormat_Grayscale8: - CopyPixels(accessor, source.GetAccessor()); + switch (target.GetFormat()) + { + case PixelFormat_RGB24: + case PixelFormat_RGBA32: + case PixelFormat_Grayscale8: + CopyPixels(targetAccessor, source.GetAccessor()); break; - case PixelFormat_Grayscale16: - CopyPixels(accessor, source.GetAccessor()); + case PixelFormat_Grayscale16: + CopyPixels(targetAccessor, source.GetAccessor()); break; - case PixelFormat_SignedGrayscale16: - CopyPixels(accessor, source.GetAccessor()); + case PixelFormat_SignedGrayscale16: + CopyPixels(targetAccessor, source.GetAccessor()); break; - default: + default: throw OrthancException(ErrorCode_InternalError); + } } } @@ -544,7 +534,7 @@ SetupImageBuffer(target, dataset); - ImageAccessor accessor(target.GetAccessor()); + ImageAccessor targetAccessor(target.GetAccessor()); /** * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK @@ -560,8 +550,8 @@ OFString decompressedColorModel; // Out DJ_RPLossless representationParameter; OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters, - &dataset, frame, startFragment, accessor.GetBuffer(), - accessor.GetSize(), decompressedColorModel); + &dataset, frame, startFragment, targetAccessor.GetBuffer(), + targetAccessor.GetSize(), decompressedColorModel); if (!c.good()) {