Mercurial > hg > orthanc
changeset 6483:42ef092375fd pixel-anon
class DicomPixelMasker moved to separate plugin orthanc-pixels-masker
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 25 Nov 2025 14:08:09 +0100 |
| parents | 8f511144bd9f |
| children | 46462739f6d2 |
| files | OrthancFramework/Resources/CodeGeneration/CheckDcmtkTransferSyntaxes.py OrthancFramework/Resources/CodeGeneration/DicomTransferSyntaxes.json OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxes.py OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxesEnumerations.mustache OrthancFramework/Sources/DicomParsing/DicomPixelMasker.cpp OrthancFramework/Sources/DicomParsing/DicomPixelMasker.h OrthancFramework/Sources/Enumerations_TransferSyntaxes.impl.h |
| diffstat | 8 files changed, 43 insertions(+), 926 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancFramework/Resources/CodeGeneration/CheckDcmtkTransferSyntaxes.py Tue Nov 25 14:03:32 2025 +0100 +++ b/OrthancFramework/Resources/CodeGeneration/CheckDcmtkTransferSyntaxes.py Tue Nov 25 14:08:09 2025 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
--- a/OrthancFramework/Resources/CodeGeneration/DicomTransferSyntaxes.json Tue Nov 25 14:03:32 2025 +0100 +++ b/OrthancFramework/Resources/CodeGeneration/DicomTransferSyntaxes.json Tue Nov 25 14:08:09 2025 +0100 @@ -5,8 +5,7 @@ "Value" : "LittleEndianImplicit", "Retired" : false, "DCMTK" : "EXS_LittleEndianImplicit", - "GDCM" : "gdcm::TransferSyntax::ImplicitVRLittleEndian", - "Raw": true, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::ImplicitVRLittleEndian" }, { @@ -15,8 +14,7 @@ "Value" : "LittleEndianExplicit", "Retired" : false, "DCMTK" : "EXS_LittleEndianExplicit", - "GDCM" : "gdcm::TransferSyntax::ExplicitVRLittleEndian", - "Raw": true, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::ExplicitVRLittleEndian" }, { @@ -24,8 +22,7 @@ "Name" : "Deflated Explicit VR Little Endian", "Value" : "DeflatedLittleEndianExplicit", "Retired" : false, - "DCMTK" : "EXS_DeflatedLittleEndianExplicit", - "Raw": false, "Lossless": true + "DCMTK" : "EXS_DeflatedLittleEndianExplicit" }, { @@ -33,8 +30,7 @@ "Name" : "Explicit VR Big Endian", "Value" : "BigEndianExplicit", "Retired" : false, - "DCMTK" : "EXS_BigEndianExplicit", - "Raw": true, "Lossless": true + "DCMTK" : "EXS_BigEndianExplicit" }, { @@ -45,8 +41,7 @@ "Note" : "Default Transfer Syntax for Lossy JPEG 8-bit Image Compression", "DCMTK" : "EXS_JPEGProcess1", "DCMTK360" : "EXS_JPEGProcess1TransferSyntax", - "GDCM" : "gdcm::TransferSyntax::JPEGBaselineProcess1", - "Raw": false, "Lossless": false + "GDCM" : "gdcm::TransferSyntax::JPEGBaselineProcess1" }, { @@ -57,8 +52,7 @@ "Note" : "Default Transfer Syntax for Lossy JPEG (lossy, 8/12 bit), 12-bit Image Compression (Process 4 only)", "DCMTK" : "EXS_JPEGProcess2_4", "DCMTK360" : "EXS_JPEGProcess2_4TransferSyntax", - "GDCM" : "gdcm::TransferSyntax::JPEGExtendedProcess2_4", - "Raw": false, "Lossless": false + "GDCM" : "gdcm::TransferSyntax::JPEGExtendedProcess2_4" }, { @@ -67,8 +61,7 @@ "Value" : "JPEGProcess3_5", "Retired" : true, "DCMTK" : "EXS_JPEGProcess3_5", - "DCMTK360" : "EXS_JPEGProcess3_5TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess3_5TransferSyntax" }, { @@ -77,8 +70,7 @@ "Value" : "JPEGProcess6_8", "Retired" : true, "DCMTK" : "EXS_JPEGProcess6_8", - "DCMTK360" : "EXS_JPEGProcess6_8TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess6_8TransferSyntax" }, { @@ -87,8 +79,7 @@ "Value" : "JPEGProcess7_9", "Retired" : true, "DCMTK" : "EXS_JPEGProcess7_9", - "DCMTK360" : "EXS_JPEGProcess7_9TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess7_9TransferSyntax" }, { @@ -97,8 +88,7 @@ "Value" : "JPEGProcess10_12", "Retired" : true, "DCMTK" : "EXS_JPEGProcess10_12", - "DCMTK360" : "EXS_JPEGProcess10_12TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess10_12TransferSyntax" }, { @@ -107,8 +97,7 @@ "Value" : "JPEGProcess11_13", "Retired" : true, "DCMTK" : "EXS_JPEGProcess11_13", - "DCMTK360" : "EXS_JPEGProcess11_13TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess11_13TransferSyntax" }, { @@ -118,8 +107,7 @@ "Retired" : false, "DCMTK" : "EXS_JPEGProcess14", "DCMTK360" : "EXS_JPEGProcess14TransferSyntax", - "GDCM" : "gdcm::TransferSyntax::JPEGLosslessProcess14", - "Raw": false, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::JPEGLosslessProcess14" }, { @@ -128,8 +116,7 @@ "Value" : "JPEGProcess15", "Retired" : true, "DCMTK" : "EXS_JPEGProcess15", - "DCMTK360" : "EXS_JPEGProcess15TransferSyntax", - "Raw": false, "Lossless": true + "DCMTK360" : "EXS_JPEGProcess15TransferSyntax" }, { @@ -138,8 +125,7 @@ "Value" : "JPEGProcess16_18", "Retired" : true, "DCMTK" : "EXS_JPEGProcess16_18", - "DCMTK360" : "EXS_JPEGProcess16_18TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess16_18TransferSyntax" }, { @@ -148,8 +134,7 @@ "Value" : "JPEGProcess17_19", "Retired" : true, "DCMTK" : "EXS_JPEGProcess17_19", - "DCMTK360" : "EXS_JPEGProcess17_19TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess17_19TransferSyntax" }, { @@ -158,8 +143,7 @@ "Value" : "JPEGProcess20_22", "Retired" : true, "DCMTK" : "EXS_JPEGProcess20_22", - "DCMTK360" : "EXS_JPEGProcess20_22TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess20_22TransferSyntax" }, { @@ -168,8 +152,7 @@ "Value" : "JPEGProcess21_23", "Retired" : true, "DCMTK" : "EXS_JPEGProcess21_23", - "DCMTK360" : "EXS_JPEGProcess21_23TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess21_23TransferSyntax" }, { @@ -178,8 +161,7 @@ "Value" : "JPEGProcess24_26", "Retired" : true, "DCMTK" : "EXS_JPEGProcess24_26", - "DCMTK360" : "EXS_JPEGProcess24_26TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess24_26TransferSyntax" }, { @@ -188,8 +170,7 @@ "Value" : "JPEGProcess25_27", "Retired" : true, "DCMTK" : "EXS_JPEGProcess25_27", - "DCMTK360" : "EXS_JPEGProcess25_27TransferSyntax", - "Raw": false, "Lossless": false + "DCMTK360" : "EXS_JPEGProcess25_27TransferSyntax" }, { @@ -198,8 +179,7 @@ "Value" : "JPEGProcess28", "Retired" : true, "DCMTK" : "EXS_JPEGProcess28", - "DCMTK360" : "EXS_JPEGProcess28TransferSyntax", - "Raw": false, "Lossless": true + "DCMTK360" : "EXS_JPEGProcess28TransferSyntax" }, { @@ -208,8 +188,7 @@ "Value" : "JPEGProcess29", "Retired" : true, "DCMTK" : "EXS_JPEGProcess29", - "DCMTK360" : "EXS_JPEGProcess29TransferSyntax", - "Raw": false, "Lossless": true + "DCMTK360" : "EXS_JPEGProcess29TransferSyntax" }, { @@ -220,8 +199,7 @@ "Note" : "Default Transfer Syntax for Lossless JPEG Image Compression", "DCMTK" : "EXS_JPEGProcess14SV1", "DCMTK360" : "EXS_JPEGProcess14SV1TransferSyntax", - "GDCM" : "gdcm::TransferSyntax::JPEGLosslessProcess14_1", - "Raw": false, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::JPEGLosslessProcess14_1" }, { @@ -230,8 +208,7 @@ "Value" : "JPEGLSLossless", "Retired" : false, "DCMTK" : "EXS_JPEGLSLossless", - "GDCM" : "gdcm::TransferSyntax::JPEGLSLossless", - "Raw": false, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::JPEGLSLossless" }, { @@ -240,8 +217,7 @@ "Value" : "JPEGLSLossy", "Retired" : false, "DCMTK" : "EXS_JPEGLSLossy", - "GDCM" : "gdcm::TransferSyntax::JPEGLSNearLossless", - "Raw": false, "Lossless": false + "GDCM" : "gdcm::TransferSyntax::JPEGLSNearLossless" }, { @@ -250,8 +226,7 @@ "Value" : "JPEG2000LosslessOnly", "Retired" : false, "DCMTK" : "EXS_JPEG2000LosslessOnly", - "GDCM" : "gdcm::TransferSyntax::JPEG2000Lossless", - "Raw": false, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::JPEG2000Lossless" }, { @@ -260,8 +235,7 @@ "Value" : "JPEG2000", "Retired" : false, "DCMTK" : "EXS_JPEG2000", - "GDCM" : "gdcm::TransferSyntax::JPEG2000", - "Raw": false, "Lossless": false + "GDCM" : "gdcm::TransferSyntax::JPEG2000" }, { @@ -270,8 +244,7 @@ "Value" : "JPEG2000MulticomponentLosslessOnly", "Retired" : false, "DCMTK" : "EXS_JPEG2000MulticomponentLosslessOnly", - "GDCM" : "gdcm::TransferSyntax::JPEG2000Part2Lossless", - "Raw": false, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::JPEG2000Part2Lossless" }, { @@ -280,8 +253,7 @@ "Value" : "JPEG2000Multicomponent", "Retired" : false, "DCMTK" : "EXS_JPEG2000Multicomponent", - "GDCM" : "gdcm::TransferSyntax::JPEG2000Part2", - "Raw": false, "Lossless": false + "GDCM" : "gdcm::TransferSyntax::JPEG2000Part2" }, { @@ -289,8 +261,7 @@ "Name" : "JPIP Referenced", "Value" : "JPIPReferenced", "Retired" : false, - "DCMTK" : "EXS_JPIPReferenced", - "Raw": false, "Lossless": true + "DCMTK" : "EXS_JPIPReferenced" }, { @@ -298,8 +269,7 @@ "Name" : "JPIP Referenced Deflate", "Value" : "JPIPReferencedDeflate", "Retired" : false, - "DCMTK" : "EXS_JPIPReferencedDeflate", - "Raw": false, "Lossless": true + "DCMTK" : "EXS_JPIPReferencedDeflate" }, { @@ -307,8 +277,7 @@ "Name" : "MPEG2 Main Profile / Main Level", "Value" : "MPEG2MainProfileAtMainLevel", "Retired" : false, - "DCMTK" : "EXS_MPEG2MainProfileAtMainLevel", - "Raw": false, "Lossless": false + "DCMTK" : "EXS_MPEG2MainProfileAtMainLevel" }, { @@ -316,8 +285,7 @@ "Name" : "MPEG2 Main Profile / High Level", "Value" : "MPEG2MainProfileAtHighLevel", "Retired" : false, - "DCMTK" : "EXS_MPEG2MainProfileAtHighLevel", - "Raw": false, "Lossless": false + "DCMTK" : "EXS_MPEG2MainProfileAtHighLevel" }, { @@ -326,8 +294,7 @@ "Value" : "MPEG4HighProfileLevel4_1", "Retired" : false, "DCMTK" : "EXS_MPEG4HighProfileLevel4_1", - "SinceDCMTK" : "361", - "Raw": false, "Lossless": false + "SinceDCMTK" : "361" }, { @@ -336,8 +303,7 @@ "Value" : "MPEG4BDcompatibleHighProfileLevel4_1", "Retired" : false, "DCMTK" : "EXS_MPEG4BDcompatibleHighProfileLevel4_1", - "SinceDCMTK" : "361", - "Raw": false, "Lossless": false + "SinceDCMTK" : "361" }, { @@ -346,8 +312,7 @@ "Value" : "MPEG4HighProfileLevel4_2_For2DVideo", "Retired" : false, "DCMTK" : "EXS_MPEG4HighProfileLevel4_2_For2DVideo", - "SinceDCMTK" : "361", - "Raw": false, "Lossless": false + "SinceDCMTK" : "361" }, { @@ -356,8 +321,7 @@ "Value" : "MPEG4HighProfileLevel4_2_For3DVideo", "Retired" : false, "DCMTK" : "EXS_MPEG4HighProfileLevel4_2_For3DVideo", - "SinceDCMTK" : "361", - "Raw": false, "Lossless": false + "SinceDCMTK" : "361" }, { @@ -366,8 +330,7 @@ "Value" : "MPEG4StereoHighProfileLevel4_2", "Retired" : false, "DCMTK" : "EXS_MPEG4StereoHighProfileLevel4_2", - "SinceDCMTK" : "361", - "Raw": false, "Lossless": false + "SinceDCMTK" : "361" }, { @@ -376,8 +339,7 @@ "Value" : "HEVCMainProfileLevel5_1", "Retired" : false, "DCMTK" : "EXS_HEVCMainProfileLevel5_1", - "SinceDCMTK" : "362", - "Raw": false, "Lossless": false + "SinceDCMTK" : "362" }, { @@ -386,8 +348,7 @@ "Value" : "HEVCMain10ProfileLevel5_1", "Retired" : false, "DCMTK" : "EXS_HEVCMain10ProfileLevel5_1", - "SinceDCMTK" : "362", - "Raw": false, "Lossless": false + "SinceDCMTK" : "362" }, { @@ -396,8 +357,7 @@ "Value" : "RLELossless", "Retired" : false, "DCMTK" : "EXS_RLELossless", - "GDCM" : "gdcm::TransferSyntax::RLELossless", - "Raw": false, "Lossless": true + "GDCM" : "gdcm::TransferSyntax::RLELossless" }, {
--- a/OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py Tue Nov 25 14:03:32 2025 +0100 +++ b/OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py Tue Nov 25 14:08:09 2025 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
--- a/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxes.py Tue Nov 25 14:03:32 2025 +0100 +++ b/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxes.py Tue Nov 25 14:08:09 2025 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
--- a/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxesEnumerations.mustache Tue Nov 25 14:03:32 2025 +0100 +++ b/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxesEnumerations.mustache Tue Nov 25 14:08:09 2025 +0100 @@ -82,44 +82,4 @@ target.insert(DicomTransferSyntax_{{Value}}); {{/Syntaxes}} } - - - bool IsLossyTransferSyntax(DicomTransferSyntax syntax) - { - switch (syntax) - { - {{#Syntaxes}} - case DicomTransferSyntax_{{Value}}: - {{#Lossless}} - return false; - {{/Lossless}} - {{^Lossless}} - return true; - {{/Lossless}} - - {{/Syntaxes}} - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - } - - - bool IsRawTransferSyntax(DicomTransferSyntax syntax) - { - switch (syntax) - { - {{#Syntaxes}} - case DicomTransferSyntax_{{Value}}: - {{#Raw}} - return true; - {{/Raw}} - {{^Raw}} - return false; - {{/Raw}} - - {{/Syntaxes}} - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - } }
--- a/OrthancFramework/Sources/DicomParsing/DicomPixelMasker.cpp Tue Nov 25 14:03:32 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,386 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2023 Osimis S.A., Belgium - * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/>. - **/ - - -#include "../PrecompiledHeaders.h" - -#include "DicomPixelMasker.h" -#include "../OrthancException.h" -#include "../SerializationToolbox.h" -#include "../Logging.h" - -namespace Orthanc -{ - static const char* KEY_MASK_PIXELS = "MaskPixelData"; - static const char* KEY_MASK_TYPE = "MaskType"; - static const char* KEY_MASK_TYPE_FILL = "Fill"; - static const char* KEY_MASK_TYPE_MEAN_FILTER = "MeanFilter"; - static const char* KEY_FILTER_WIDTH = "FilterWidth"; - static const char* KEY_FILL_VALUE = "FillValue"; - static const char* KEY_REGIONS = "Regions"; - static const char* KEY_REGION_TYPE = "RegionType"; - static const char* KEY_REGION_2D = "2D"; - static const char* KEY_REGION_3D = "3D"; - static const char* KEY_ORIGIN = "Origin"; - static const char* KEY_END = "End"; - static const char* KEY_TARGET_SERIES = "TargetSeries"; - static const char* KEY_TARGET_INSTANCES = "TargetInstances"; - - DicomPixelMasker::DicomPixelMasker() - { - } - - DicomPixelMasker::~DicomPixelMasker() - { - for (std::list<BaseRegion*>::iterator it = regions_.begin(); it != regions_.end(); ++it) - { - delete *it; - } - } - - DicomPixelMasker::BaseRegion::BaseRegion() : - mode_(DicomPixelMaskerMode_Undefined), - fillValue_(0), - filterWidth_(0) - { - } - - DicomPixelMasker::Region2D::Region2D(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) : - x1_(x1), - y1_(y1), - x2_(x2), - y2_(y2) - { - } - - DicomPixelMasker::Region3D::Region3D(double x1, double y1, double z1, double x2, double y2, double z2) : - x1_(x1), - y1_(y1), - z1_(z1), - x2_(x2), - y2_(y2), - z2_(z2) - { - } - - bool DicomPixelMasker::BaseRegion::IsTargeted(const ParsedDicomFile& file) const - { - DicomInstanceHasher hasher = file.GetHasher(); - const std::string& seriesId = hasher.HashSeries(); - const std::string& instanceId = hasher.HashInstance(); - - if (targetSeries_.size() > 0 && targetSeries_.find(seriesId) == targetSeries_.end()) - { - return false; - } - - if (targetInstances_.size() > 0 && targetInstances_.find(instanceId) == targetInstances_.end()) - { - return false; - } - - return true; - } - - bool DicomPixelMasker::Region2D::GetPixelMaskArea(unsigned int& x1, unsigned int& y1, unsigned int& x2, unsigned int& y2, const ParsedDicomFile& file, unsigned int frameIndex) const - { - if (IsTargeted(file)) - { - x1 = x1_; - y1 = y1_; - x2 = x2_; - y2 = y2_; - return true; - } - - return false; - } - - static void GetDoubleVector(std::vector<double>& target, const std::string& strValue, const DicomTag& tag, size_t expectedSize) - { - target.clear(); - - std::vector<std::string> strVector; - Toolbox::SplitString(strVector, strValue, '\\'); - - if (strVector.size() != expectedSize) - { - throw OrthancException(ErrorCode_InexistentTag, "Unable to perform 3D -> 2D conversion, tag " + tag.Format() + " length is invalid"); - } - - for (size_t i = 0; i < strVector.size(); ++i) - { - try - { - target.push_back(boost::lexical_cast<double>(strVector[i])); - } - catch (boost::bad_lexical_cast&) - { - throw OrthancException(ErrorCode_InexistentTag, "Unable to perform 3D -> 2D conversion, tag " + tag.Format() + " contains invalid value " + strVector[i]); - } - } - } - - static void GetDoubleVector(std::vector<double>& target, const ParsedDicomFile& file, const DicomTag& tag, size_t expectedSize) - { - std::string str; - if (!file.GetTagValue(str, tag)) - { - throw OrthancException(ErrorCode_InexistentTag, "Unable to perform 3D -> 2D conversion, missing tag" + tag.Format()); - } - - GetDoubleVector(target, str, tag, expectedSize); - } - - bool DicomPixelMasker::Region3D::GetPixelMaskArea(unsigned int& x1, unsigned int& y1, unsigned int& x2, unsigned int& y2, const ParsedDicomFile& file, unsigned int frameIndex) const - { - if (IsTargeted(file)) - { - DicomMap tags; - file.ExtractDicomSummary(tags, 256); - - std::vector<double> imagePositionPatient; - std::vector<double> imageOrientationPatient; - std::vector<double> pixelSpacing; - double sliceSpacing = 0.0; - - if (file.HasTag(DICOM_TAG_IMAGE_POSITION_PATIENT) && file.HasTag(DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) - { - GetDoubleVector(imagePositionPatient, file, DICOM_TAG_IMAGE_POSITION_PATIENT, 3); - GetDoubleVector(imageOrientationPatient, file, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, 6); - } - else if (file.HasTag(DICOM_TAG_DETECTOR_INFORMATION_SEQUENCE)) // find it in the detector info sequence (for some multi frame instances like NM or scintigraphy) TODO-PIXEL-ANON: to validate - { - const Json::Value& jsonSequence = tags.GetValue(DICOM_TAG_DETECTOR_INFORMATION_SEQUENCE).GetSequenceContent(); - if (jsonSequence.size() == 1) - { - std::string strImagePositionPatient = jsonSequence[0]["0020,0032"]["Value"].asString(); - std::string strImageOrientationPatient = jsonSequence[0]["0020,0037"]["Value"].asString(); - - GetDoubleVector(imagePositionPatient, strImagePositionPatient, DICOM_TAG_IMAGE_POSITION_PATIENT, 3); - GetDoubleVector(imageOrientationPatient, strImageOrientationPatient, DICOM_TAG_IMAGE_ORIENTATION_PATIENT, 6); - } - else - { - throw OrthancException(ErrorCode_InternalError, "Unable to find ImagePositionPatient in DetectorInformationSequence, invalid sequence size"); - } - } - else - { - throw OrthancException(ErrorCode_InternalError, "Unable to find ImagePositionPatient or ImageOrientationPatient"); - } - - GetDoubleVector(pixelSpacing, file, DICOM_TAG_PIXEL_SPACING, 2); - - if (file.HasTag(DICOM_TAG_SPACING_BETWEEN_SLICES)) - { - std::string strSliceSpacing; - if (file.GetTagValue(strSliceSpacing, DICOM_TAG_SPACING_BETWEEN_SLICES)) - { - sliceSpacing = boost::lexical_cast<double>(strSliceSpacing); - } - } - - double z = imagePositionPatient[2]; - - if (sliceSpacing != 0.0) - { - z = z - frameIndex * sliceSpacing; - } - - // note: To simplify, for the z, we only check that imagePositionPatient is between the authorized z values. - // This won't be perfectly true for weird images with slices that are not parallel but let's wait for someone to complain ... - if (z < std::min(z1_, z2_) || - z > std::max(z1_, z2_)) - { - return false; - } - - - double deltaX1 = x1_ - imagePositionPatient[0]; - double deltaY1 = y1_ - imagePositionPatient[1]; - double deltaZ1 = z1_ - z; - double deltaX2 = x2_ - imagePositionPatient[0]; - double deltaY2 = y2_ - imagePositionPatient[1]; - double deltaZ2 = z2_ - z; - - double ix1 = (deltaX1 * imageOrientationPatient[0] + deltaY1 * imageOrientationPatient[1] + deltaZ1 * imageOrientationPatient[2]) / pixelSpacing[0]; - double iy1 = (deltaX1 * imageOrientationPatient[3] + deltaY1 * imageOrientationPatient[4] + deltaZ1 * imageOrientationPatient[5]) / pixelSpacing[1]; - double ix2 = (deltaX2 * imageOrientationPatient[0] + deltaY2 * imageOrientationPatient[1] + deltaZ2 * imageOrientationPatient[2]) / pixelSpacing[0]; - double iy2 = (deltaX2 * imageOrientationPatient[3] + deltaY2 * imageOrientationPatient[4] + deltaZ2 * imageOrientationPatient[5]) / pixelSpacing[1]; - - std::string strRows; - std::string strColumns; - - if (!file.GetTagValue(strRows, DICOM_TAG_ROWS) || !file.GetTagValue(strColumns, DICOM_TAG_COLUMNS)) - { - throw OrthancException(ErrorCode_InexistentTag, "Unable to perform 3D -> 2D conversion, missing ROWS or COLUMNS tag"); - } - - // clip on image size - double rows = boost::lexical_cast<double>(strRows); - double columns = boost::lexical_cast<double>(strColumns); - - x1 = static_cast<unsigned int>(std::max(0.0, std::min(ix1, ix2))); - y1 = static_cast<unsigned int>(std::max(0.0, std::min(iy1, iy2))); - x2 = static_cast<unsigned int>(std::min(columns, std::max(ix1, ix2))); - y2 = static_cast<unsigned int>(std::min(rows, std::max(iy1, iy2))); - - return true; - } - - return false; - } - - void DicomPixelMasker::Apply(ParsedDicomFile& toModify) - { - for (std::list<BaseRegion*>::const_iterator itr = regions_.begin(); itr != regions_.end(); ++itr) - { - const BaseRegion* r = *itr; - - for (unsigned int i = 0; i < toModify.GetFramesCount(); ++i) - { - unsigned int x1, y1, x2, y2; - - if (r->GetPixelMaskArea(x1, y1, x2, y2, toModify, i)) - { - ImageAccessor imageRegion; - toModify.GetRawFrame(i)->GetRegion(imageRegion, x1, y1, x2 - x1, y2 - y1); - - if (r->GetMode() == DicomPixelMaskerMode_MeanFilter) - { - ImageProcessing::MeanFilter(imageRegion, r->GetFilterWidth(), r->GetFilterWidth()); - } - else if (r->GetMode() == DicomPixelMaskerMode_Fill) - { - ImageProcessing::Set(imageRegion, r->GetFillValue()); - } - } - } - } - } - - void DicomPixelMasker::ParseRequest(const Json::Value& request) - { - if (request.isMember(KEY_MASK_PIXELS) && request[KEY_MASK_PIXELS].isObject()) - { - const Json::Value& maskPixelsJson = request[KEY_MASK_PIXELS]; - - if (maskPixelsJson.isMember(KEY_REGIONS) && maskPixelsJson[KEY_REGIONS].isArray()) - { - const Json::Value& regionsJson = maskPixelsJson[KEY_REGIONS]; - - for (Json::ArrayIndex i = 0; i < regionsJson.size(); ++i) - { - const Json::Value& regionJson = regionsJson[i]; - - std::unique_ptr<BaseRegion> region; - - if (regionJson.isMember(KEY_REGION_TYPE) && regionJson[KEY_REGION_TYPE].isString()) - { - if (regionJson[KEY_REGION_TYPE].asString() == KEY_REGION_2D) - { - if (regionJson.isMember(KEY_ORIGIN) && regionJson[KEY_ORIGIN].isArray() && regionJson[KEY_ORIGIN].size() == 2 && - regionJson.isMember(KEY_END) && regionJson[KEY_END].isArray() && regionJson[KEY_END].size() == 2) - { - unsigned int x = regionJson[KEY_ORIGIN][0].asUInt(); - unsigned int y = regionJson[KEY_ORIGIN][1].asUInt(); - unsigned int width = regionJson[KEY_END][0].asUInt() - x; - unsigned int height = regionJson[KEY_END][1].asUInt() - y; - - region.reset(new Region2D(x, y, width, height)); - } - else - { - throw OrthancException(ErrorCode_BadFileFormat, "2D Region: invalid coordinates"); - } - - } - else if (regionJson[KEY_REGION_TYPE].asString() == KEY_REGION_3D) - { - if (regionJson.isMember(KEY_ORIGIN) && regionJson[KEY_ORIGIN].isArray() && regionJson[KEY_ORIGIN].size() == 3 && - regionJson.isMember(KEY_END) && regionJson[KEY_END].isArray() && regionJson[KEY_END].size() == 3) - { - double x1 = regionJson[KEY_ORIGIN][0].asDouble(); - double y1 = regionJson[KEY_ORIGIN][1].asDouble(); - double z1 = regionJson[KEY_ORIGIN][2].asDouble(); - double x2 = regionJson[KEY_END][0].asDouble(); - double y2 = regionJson[KEY_END][1].asDouble(); - double z2 = regionJson[KEY_END][2].asDouble(); - - region.reset(new Region3D(x1, y1, z1, x2, y2, z2)); - } - else - { - throw OrthancException(ErrorCode_BadFileFormat, "2D Region: invalid coordinates"); - } - } - else - { - throw OrthancException(ErrorCode_BadFileFormat, std::string(KEY_REGION_TYPE) + " unrecognized value '" + regionJson[KEY_REGION_TYPE].asString() +"'"); - } - } - - if (regionJson.isMember(KEY_MASK_TYPE) && regionJson[KEY_MASK_TYPE].isString()) - { - if (regionJson[KEY_MASK_TYPE].asString() == KEY_MASK_TYPE_FILL) - { - if (regionJson.isMember(KEY_FILL_VALUE) && regionJson[KEY_FILL_VALUE].isInt()) - { - region->SetFillValue(regionJson[KEY_FILL_VALUE].asInt()); - } - } - else if (regionJson[KEY_MASK_TYPE].asString() == KEY_MASK_TYPE_MEAN_FILTER) - { - if (regionJson.isMember(KEY_FILTER_WIDTH) && regionJson[KEY_FILTER_WIDTH].isUInt()) - { - region->SetMeanFilter(regionJson[KEY_FILTER_WIDTH].asUInt()); - } - } - else - { - throw OrthancException(ErrorCode_BadFileFormat, std::string(KEY_MASK_TYPE) + " should be '" + KEY_MASK_TYPE_FILL +"' or '" + KEY_MASK_TYPE_MEAN_FILTER + "'."); - } - } - - if (regionJson.isMember(KEY_TARGET_SERIES) && regionJson[KEY_TARGET_SERIES].isArray()) - { - std::set<std::string> targetSeries; - SerializationToolbox::ReadSetOfStrings(targetSeries, regionJson, KEY_TARGET_SERIES); - region->SetTargetSeries(targetSeries); - } - - if (regionJson.isMember(KEY_TARGET_INSTANCES) && regionJson[KEY_TARGET_INSTANCES].isArray()) - { - std::set<std::string> targetInstances; - SerializationToolbox::ReadSetOfStrings(targetInstances, regionJson, KEY_TARGET_INSTANCES); - region->SetTargetInstances(targetInstances); - } - - regions_.push_back(region.release()); - } - } - } - - } -}
--- a/OrthancFramework/Sources/DicomParsing/DicomPixelMasker.h Tue Nov 25 14:03:32 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2023 Osimis S.A., Belgium - * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/>. - **/ - - -#pragma once - -#include "ParsedDicomFile.h" -#include "../Images/ImageProcessing.h" - -#include <set> - - -namespace Orthanc -{ - enum DicomPixelMaskerMode - { - DicomPixelMaskerMode_Fill, - DicomPixelMaskerMode_MeanFilter, - - DicomPixelMaskerMode_Undefined - }; - - class ORTHANC_PUBLIC DicomPixelMasker : public boost::noncopyable - { - class BaseRegion - { - DicomPixelMaskerMode mode_; - int32_t fillValue_; // pixel value - uint32_t filterWidth_; // filter width - std::set<std::string> targetSeries_; - std::set<std::string> targetInstances_; - - protected: - bool IsTargeted(const ParsedDicomFile& file) const; - BaseRegion(); - - public: - - virtual ~BaseRegion() - { - } - - virtual bool GetPixelMaskArea(unsigned int& x1, unsigned int& y1, unsigned int& x2, unsigned int& y2, const ParsedDicomFile& file, unsigned int frameIndex) const = 0; - - DicomPixelMaskerMode GetMode() const - { - return mode_; - } - - int32_t GetFillValue() const - { - assert(mode_ == DicomPixelMaskerMode_Fill); - return fillValue_; - } - - int32_t GetFilterWidth() const - { - assert(mode_ == DicomPixelMaskerMode_MeanFilter); - return filterWidth_; - } - - void SetFillValue(int32_t value) - { - mode_ = DicomPixelMaskerMode_Fill; - fillValue_ = value; - } - - void SetMeanFilter(uint32_t value) - { - mode_ = DicomPixelMaskerMode_MeanFilter; - filterWidth_ = value; - } - - void SetTargetSeries(const std::set<std::string> targetSeries) - { - targetSeries_ = targetSeries; - } - - void SetTargetInstances(const std::set<std::string> targetInstances) - { - targetInstances_ = targetInstances; - } - }; - - class Region2D : public BaseRegion - { - unsigned int x1_; - unsigned int y1_; - unsigned int x2_; - unsigned int y2_; - - public: - Region2D(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2); - - virtual bool GetPixelMaskArea(unsigned int& x1, unsigned int& y1, unsigned int& x2, unsigned int& y2, const ParsedDicomFile& file, unsigned int frameIndex) const ORTHANC_OVERRIDE; - }; - - class Region3D : public BaseRegion - { - double x1_; - double y1_; - double z1_; - double x2_; - double y2_; - double z2_; - - public: - Region3D(double x1, double y1, double z1, double x2, double y2, double z2); - - virtual bool GetPixelMaskArea(unsigned int& x1, unsigned int& y1, unsigned int& x2, unsigned int& y2, const ParsedDicomFile& file, unsigned int frameIndex) const ORTHANC_OVERRIDE; - }; - - private: - std::list<BaseRegion*> regions_; - - public: - DicomPixelMasker(); - - ~DicomPixelMasker(); - - void Apply(ParsedDicomFile& toModify); - - void ParseRequest(const Json::Value& request); - }; -}
--- a/OrthancFramework/Sources/Enumerations_TransferSyntaxes.impl.h Tue Nov 25 14:03:32 2025 +0100 +++ b/OrthancFramework/Sources/Enumerations_TransferSyntaxes.impl.h Tue Nov 25 14:08:09 2025 +0100 @@ -602,276 +602,4 @@ target.insert(DicomTransferSyntax_RFC2557MimeEncapsulation); target.insert(DicomTransferSyntax_XML); } - - - bool IsLossyTransferSyntax(DicomTransferSyntax syntax) - { - switch (syntax) - { - case DicomTransferSyntax_LittleEndianImplicit: - return false; - - case DicomTransferSyntax_LittleEndianExplicit: - return false; - - case DicomTransferSyntax_DeflatedLittleEndianExplicit: - return false; - - case DicomTransferSyntax_BigEndianExplicit: - return false; - - case DicomTransferSyntax_JPEGProcess1: - return true; - - case DicomTransferSyntax_JPEGProcess2_4: - return true; - - case DicomTransferSyntax_JPEGProcess3_5: - return true; - - case DicomTransferSyntax_JPEGProcess6_8: - return true; - - case DicomTransferSyntax_JPEGProcess7_9: - return true; - - case DicomTransferSyntax_JPEGProcess10_12: - return true; - - case DicomTransferSyntax_JPEGProcess11_13: - return true; - - case DicomTransferSyntax_JPEGProcess14: - return false; - - case DicomTransferSyntax_JPEGProcess15: - return false; - - case DicomTransferSyntax_JPEGProcess16_18: - return true; - - case DicomTransferSyntax_JPEGProcess17_19: - return true; - - case DicomTransferSyntax_JPEGProcess20_22: - return true; - - case DicomTransferSyntax_JPEGProcess21_23: - return true; - - case DicomTransferSyntax_JPEGProcess24_26: - return true; - - case DicomTransferSyntax_JPEGProcess25_27: - return true; - - case DicomTransferSyntax_JPEGProcess28: - return false; - - case DicomTransferSyntax_JPEGProcess29: - return false; - - case DicomTransferSyntax_JPEGProcess14SV1: - return false; - - case DicomTransferSyntax_JPEGLSLossless: - return false; - - case DicomTransferSyntax_JPEGLSLossy: - return true; - - case DicomTransferSyntax_JPEG2000LosslessOnly: - return false; - - case DicomTransferSyntax_JPEG2000: - return true; - - case DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly: - return false; - - case DicomTransferSyntax_JPEG2000Multicomponent: - return true; - - case DicomTransferSyntax_JPIPReferenced: - return false; - - case DicomTransferSyntax_JPIPReferencedDeflate: - return false; - - case DicomTransferSyntax_MPEG2MainProfileAtMainLevel: - return true; - - case DicomTransferSyntax_MPEG2MainProfileAtHighLevel: - return true; - - case DicomTransferSyntax_MPEG4HighProfileLevel4_1: - return true; - - case DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1: - return true; - - case DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo: - return true; - - case DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo: - return true; - - case DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2: - return true; - - case DicomTransferSyntax_HEVCMainProfileLevel5_1: - return true; - - case DicomTransferSyntax_HEVCMain10ProfileLevel5_1: - return true; - - case DicomTransferSyntax_RLELossless: - return false; - - case DicomTransferSyntax_RFC2557MimeEncapsulation: - return true; - - case DicomTransferSyntax_XML: - return true; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - } - - - bool IsRawTransferSyntax(DicomTransferSyntax syntax) - { - switch (syntax) - { - case DicomTransferSyntax_LittleEndianImplicit: - return true; - - case DicomTransferSyntax_LittleEndianExplicit: - return true; - - case DicomTransferSyntax_DeflatedLittleEndianExplicit: - return false; - - case DicomTransferSyntax_BigEndianExplicit: - return true; - - case DicomTransferSyntax_JPEGProcess1: - return false; - - case DicomTransferSyntax_JPEGProcess2_4: - return false; - - case DicomTransferSyntax_JPEGProcess3_5: - return false; - - case DicomTransferSyntax_JPEGProcess6_8: - return false; - - case DicomTransferSyntax_JPEGProcess7_9: - return false; - - case DicomTransferSyntax_JPEGProcess10_12: - return false; - - case DicomTransferSyntax_JPEGProcess11_13: - return false; - - case DicomTransferSyntax_JPEGProcess14: - return false; - - case DicomTransferSyntax_JPEGProcess15: - return false; - - case DicomTransferSyntax_JPEGProcess16_18: - return false; - - case DicomTransferSyntax_JPEGProcess17_19: - return false; - - case DicomTransferSyntax_JPEGProcess20_22: - return false; - - case DicomTransferSyntax_JPEGProcess21_23: - return false; - - case DicomTransferSyntax_JPEGProcess24_26: - return false; - - case DicomTransferSyntax_JPEGProcess25_27: - return false; - - case DicomTransferSyntax_JPEGProcess28: - return false; - - case DicomTransferSyntax_JPEGProcess29: - return false; - - case DicomTransferSyntax_JPEGProcess14SV1: - return false; - - case DicomTransferSyntax_JPEGLSLossless: - return false; - - case DicomTransferSyntax_JPEGLSLossy: - return false; - - case DicomTransferSyntax_JPEG2000LosslessOnly: - return false; - - case DicomTransferSyntax_JPEG2000: - return false; - - case DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly: - return false; - - case DicomTransferSyntax_JPEG2000Multicomponent: - return false; - - case DicomTransferSyntax_JPIPReferenced: - return false; - - case DicomTransferSyntax_JPIPReferencedDeflate: - return false; - - case DicomTransferSyntax_MPEG2MainProfileAtMainLevel: - return false; - - case DicomTransferSyntax_MPEG2MainProfileAtHighLevel: - return false; - - case DicomTransferSyntax_MPEG4HighProfileLevel4_1: - return false; - - case DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1: - return false; - - case DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo: - return false; - - case DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo: - return false; - - case DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2: - return false; - - case DicomTransferSyntax_HEVCMainProfileLevel5_1: - return false; - - case DicomTransferSyntax_HEVCMain10ProfileLevel5_1: - return false; - - case DicomTransferSyntax_RLELossless: - return false; - - case DicomTransferSyntax_RFC2557MimeEncapsulation: - return false; - - case DicomTransferSyntax_XML: - return false; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - } }
