Mercurial > hg > orthanc
changeset 4947:dfbe764995cf
added ParsedDicomFile::DecodeAllOverlays()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 21 Mar 2022 08:59:20 +0100 |
parents | 51e4947aa3b3 |
children | 5f6b13202e85 |
files | OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h OrthancFramework/Sources/Images/ImageProcessing.cpp OrthancFramework/Sources/Images/ImageProcessing.h |
diffstat | 4 files changed, 138 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Sun Mar 20 18:03:32 2022 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Mon Mar 21 08:59:20 2022 +0100 @@ -1920,7 +1920,7 @@ } - void ParsedDicomFile::ListOverlays(std::set<unsigned int>& groups) const + void ParsedDicomFile::ListOverlays(std::set<uint16_t>& groups) const { DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset(); @@ -1953,7 +1953,7 @@ ImageAccessor* ParsedDicomFile::DecodeOverlay(int& originX, int& originY, - unsigned int group) const + uint16_t group) const { // https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.9.2.html @@ -2014,6 +2014,67 @@ } + ImageAccessor* ParsedDicomFile::DecodeAllOverlays(int& originX, + int& originY) const + { + std::set<uint16_t> groups; + ListOverlays(groups); + + if (groups.empty()) + { + originX = 0; + originY = 0; + return new Image(PixelFormat_Grayscale8, 0, 0, false); + } + else + { + std::set<uint16_t>::const_iterator it = groups.begin(); + assert(it != groups.end()); + + std::unique_ptr<ImageAccessor> result(DecodeOverlay(originX, originY, *it)); + assert(result.get() != NULL); + ++it; + + int right = originX + static_cast<int>(result->GetWidth()); + int bottom = originY + static_cast<int>(result->GetHeight()); + + while (it != groups.end()) + { + int ox, oy; + std::unique_ptr<ImageAccessor> overlay(DecodeOverlay(ox, oy, *it)); + assert(overlay.get() != NULL); + + int mergedX = std::min(originX, ox); + int mergedY = std::min(originY, oy); + right = std::max(right, ox + static_cast<int>(overlay->GetWidth())); + bottom = std::max(bottom, oy + static_cast<int>(overlay->GetHeight())); + + assert(right >= mergedX && bottom >= mergedY); + unsigned int width = static_cast<unsigned int>(right - mergedX); + unsigned int height = static_cast<unsigned int>(bottom - mergedY); + + std::unique_ptr<ImageAccessor> merged(new Image(PixelFormat_Grayscale8, width, height, false)); + ImageProcessing::Set(*merged, 0); + + ImageAccessor a; + merged->GetRegion(a, originX - mergedX, originY - mergedY, result->GetWidth(), result->GetHeight()); + ImageProcessing::Maximum(a, *result); + + merged->GetRegion(a, ox - mergedX, oy - mergedY, overlay->GetWidth(), overlay->GetHeight()); + ImageProcessing::Maximum(a, *overlay); + + originX = mergedX; + originY = mergedY; + result.reset(merged.release()); + + ++it; + } + + return result.release(); + } + } + + #if ORTHANC_BUILDING_FRAMEWORK_LIBRARY == 1 // Alias for binary compatibility with Orthanc Framework 1.7.2 => don't use it anymore void ParsedDicomFile::DatasetToJson(Json::Value& target,
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Sun Mar 20 18:03:32 2022 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h Mon Mar 21 08:59:20 2022 +0100 @@ -301,10 +301,13 @@ double& rescaleSlope, unsigned int frame) const; - void ListOverlays(std::set<unsigned int>& groups) const; + void ListOverlays(std::set<uint16_t>& groups) const; ImageAccessor* DecodeOverlay(int& originX, int& originY, - unsigned int group) const; + uint16_t group) const; + + ImageAccessor* DecodeAllOverlays(int& originX, + int& originY) const; }; }
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp Sun Mar 20 18:03:32 2022 +0100 +++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp Mon Mar 21 08:59:20 2022 +0100 @@ -2964,4 +2964,71 @@ throw OrthancException(ErrorCode_NotImplemented); } } + + + template <typename PixelType, + typename Functor> + static void ApplyImageOntoImage(Functor f, + ImageAccessor& image /* inout */, + const ImageAccessor& other) + { + const unsigned int width = image.GetWidth(); + const unsigned int height = image.GetHeight(); + + if (width != other.GetWidth() || + height != other.GetHeight()) + { + throw OrthancException(ErrorCode_IncompatibleImageSize); + } + else if (image.GetFormat() != other.GetFormat() || + GetBytesPerPixel(image.GetFormat()) != sizeof(PixelType)) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat); + } + else + { + for (unsigned int y = 0; y < height; y++) + { + PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y)); + const PixelType* q = reinterpret_cast<const PixelType*>(other.GetConstRow(y)); + + for (unsigned int x = 0; x < width; x++, p++, q++) + { + f(*p, *q); + } + } + } + } + + + void ImageProcessing::Maximum(ImageAccessor& image, + const ImageAccessor& other) + { + struct F + { + void operator() (uint8_t& a, const uint8_t& b) + { + a = std::max(a, b); + } + + void operator() (uint16_t& a, const uint16_t& b) + { + a = std::max(a, b); + } + }; + + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + ApplyImageOntoImage<uint8_t, F>(F(), image, other); + return; + + case PixelFormat_Grayscale16: + ApplyImageOntoImage<uint16_t, F>(F(), image, other); + return; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } }
--- a/OrthancFramework/Sources/Images/ImageProcessing.h Sun Mar 20 18:03:32 2022 +0100 +++ b/OrthancFramework/Sources/Images/ImageProcessing.h Mon Mar 21 08:59:20 2022 +0100 @@ -218,5 +218,8 @@ static void ConvertJpegYCbCrToRgb(ImageAccessor& image /* inplace */); static void SwapEndianness(ImageAccessor& image /* inplace */); + + static void Maximum(ImageAccessor& image /* inout */, + const ImageAccessor& other); }; }