Coverage Report

Created: 2026-11-12 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmimgle/libsrc/dimomod.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1996-2018, OFFIS e.V.
4
 *  All rights reserved.  See COPYRIGHT file for details.
5
 *
6
 *  This software and supporting documentation were developed by
7
 *
8
 *    OFFIS e.V.
9
 *    R&D Division Health
10
 *    Escherweg 2
11
 *    D-26121 Oldenburg, Germany
12
 *
13
 *
14
 *  Module:  dcmimgle
15
 *
16
 *  Author:  Joerg Riesmeier
17
 *
18
 *  Purpose: DicomMonochromeModality (Source)
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"
24
#include "dcmtk/dcmdata/dcdeftag.h"
25
#include "dcmtk/dcmdata/dcuid.h"
26
27
#include "dcmtk/dcmimgle/dimomod.h"
28
#include "dcmtk/dcmimgle/didocu.h"
29
#include "dcmtk/dcmimgle/diinpx.h"
30
31
32
/*----------------*
33
 *  constructors  *
34
 *----------------*/
35
36
DiMonoModality::DiMonoModality(const DiDocument *docu,
37
                               DiInputPixel *pixel)
38
0
  : Representation(EPR_MaxSigned),
39
0
    MinValue(0),
40
0
    MaxValue(0),
41
0
    Bits(0),
42
0
    UsedBits(0),
43
0
    AbsMinimum(0),
44
0
    AbsMaximum(0),
45
0
    RescaleIntercept(0),
46
0
    RescaleSlope(0),
47
0
    LookupTable(0),
48
0
    Rescaling(0),
49
0
    TableData(NULL)
50
0
{
51
0
    if (Init(docu, pixel))
52
0
    {
53
0
        if (docu->getFlags() & CIF_IgnoreModalityTransformation)        // ignore modality LUT and rescaling
54
0
        {
55
0
            DCMIMGLE_INFO("configuration flag set ... ignoring possible modality transform");
56
0
        }
57
0
        else if (!(docu->getFlags() & CIF_UsePresentationState))        // ignore modality LUT and rescaling
58
0
        {
59
0
            const char *sopClassUID = NULL;                             // check for XA and XRF image (ignore MLUT)
60
0
            if (!docu->getValue(DCM_SOPClassUID, sopClassUID) || (sopClassUID == NULL))
61
0
                sopClassUID = "";
62
0
            if ((strcmp(sopClassUID, UID_XRayAngiographicImageStorage) == 0) ||
63
0
                (strcmp(sopClassUID, UID_XRayRadiofluoroscopicImageStorage) == 0) ||
64
0
                (strcmp(sopClassUID, UID_RETIRED_XRayAngiographicBiPlaneImageStorage) == 0))
65
0
            {
66
                /* David Clunie in comp.protocols.dicom (2000-12-13):
67
                    "Modality LUTs in XA and XRF objects are totally screwy and
68
                     do not follow the normal rules. [...] A Modality LUT may be
69
                     included with the image to allow it to be scaled back to its
70
                     proportional value to X-Ray beam intensity. In other words,
71
                     for the objects that use this module (XA and XRF), the
72
                     Modality LUT is used BACKWARDS. It is used to convert stored
73
                     pixels to X-Ray beam intensity space, but it is NOT APPLIED
74
                     to stored pixels for the purpose of display (or more
75
                     specifically prior to application of the VOI LUT Module
76
                     attributes to the stored pixel data)."
77
                */
78
0
                DCMIMGLE_INFO("processing XA or XRF image ... ignoring possible modality transform");
79
0
            } else {
80
0
                EL_BitsPerTableEntry descMode = ELM_UseValue;          // first search on main level
81
0
                if (docu->getFlags() & CIF_IgnoreModalityLutBitDepth)
82
0
                    descMode = ELM_IgnoreValue;
83
0
                else if (docu->getFlags() & CIF_CheckLutBitDepth)
84
0
                    descMode = ELM_CheckValue;
85
0
                TableData = new DiLookupTable(docu, DCM_ModalityLUTSequence, DCM_LUTDescriptor, DCM_LUTData,
86
0
                    DCM_LUTExplanation, descMode);
87
0
                checkTable();
88
0
                if (LookupTable)
89
0
                    DCMIMGLE_DEBUG("found modality LUT on main dataset level");
90
0
                Rescaling = (docu->getValue(DCM_RescaleIntercept, RescaleIntercept) > 0);
91
0
                Rescaling &= (docu->getValue(DCM_RescaleSlope, RescaleSlope) > 0);
92
0
                if (Rescaling)
93
0
                    DCMIMGLE_DEBUG("found 'RescaleSlope/Intercept' on main dataset level");
94
0
                else if (!LookupTable)                                 // then check for functional groups sequence
95
0
                {
96
0
                    DcmSequenceOfItems *seq = NULL;
97
0
                    if (docu->getSequence(DCM_SharedFunctionalGroupsSequence, seq))
98
0
                    {
99
0
                        DcmItem *item = seq->getItem(0);
100
0
                        if ((item != NULL) && docu->getSequence(DCM_PixelValueTransformationSequence, seq, item))
101
0
                        {
102
0
                            item = seq->getItem(0);
103
0
                            if (item != NULL)
104
0
                            {
105
0
                                Rescaling = (docu->getValue(DCM_RescaleIntercept, RescaleIntercept, 0, item) > 0);
106
0
                                Rescaling &= (docu->getValue(DCM_RescaleSlope, RescaleSlope, 0, item) > 0);
107
0
                                if (Rescaling)
108
0
                                    DCMIMGLE_DEBUG("found 'RescaleSlope/Intercept' in 'SharedFunctionalGroupsSequence'");
109
0
                            }
110
0
                        }
111
0
                    }
112
0
                }
113
0
                checkRescaling(pixel);
114
0
                if (Rescaling || LookupTable)                          // check for possibly inappropriate use of MLUT
115
0
                {
116
                    /* David Clunie in comp.protocols.dicom (2012-10-28):
117
                        "By the way, in general, it can be difficult to decide
118
                         whether or not to apply the conceptual Modality LUT
119
                         step before windowing, even if it is specified by
120
                         Rescale Slope/Intercept values rather than an actual
121
                         LUT. For example, in MR images to which Philips has
122
                         added the rescale values, these should not be applied
123
                         before their window values; likewise in PET images,
124
                         especially those with GML Units and rescale values to
125
                         SUV (small decimal numbers), the window values are
126
                         historically usually in stored pixel values rather than
127
                         SUVs.
128
                         Making the correct decision may require comparing the
129
                         range of possible rescaled output values (across the
130
                         domain of possible input stored pixel values) with the
131
                         specific window values, to see if the latter "make
132
                         sense".
133
                    */
134
0
                    if (strcmp(sopClassUID, UID_MRImageStorage) == 0)
135
0
                        DCMIMGLE_WARN("processing MR image ... applying modality transform may create unexpected result");
136
0
                    else if (strcmp(sopClassUID, UID_PositronEmissionTomographyImageStorage) == 0)
137
0
                        DCMIMGLE_WARN("processing PET image ... applying modality transform may create unexpected result");
138
0
                    else if (strcmp(sopClassUID, UID_RTDoseStorage) == 0)
139
0
                        DCMIMGLE_WARN("processing RTDOSE object ... applying modality transform may create unexpected result");
140
0
                }
141
0
            }
142
0
        }
143
0
        determineRepresentation(docu);
144
0
    }
145
0
}
146
147
148
DiMonoModality::DiMonoModality(const DiDocument *docu,
149
                               DiInputPixel *pixel,
150
                               const double slope,
151
                               const double intercept)
152
0
  : Representation(EPR_MaxSigned),
153
0
    MinValue(0),
154
0
    MaxValue(0),
155
0
    Bits(0),
156
0
    UsedBits(0),
157
0
    AbsMinimum(0),
158
0
    AbsMaximum(0),
159
0
    RescaleIntercept(intercept),
160
0
    RescaleSlope(slope),
161
0
    LookupTable(0),
162
0
    Rescaling(0),
163
0
    TableData(NULL)
164
0
{
165
0
    if (Init(docu, pixel))
166
0
    {
167
0
        Rescaling = 1;
168
0
        checkRescaling(pixel);
169
0
        determineRepresentation(docu);
170
0
    }
171
0
}
172
173
174
DiMonoModality::DiMonoModality(const DiDocument *docu,
175
                               DiInputPixel *pixel,
176
                               const DcmUnsignedShort &data,
177
                               const DcmUnsignedShort &descriptor,
178
                               const DcmLongString *explanation)
179
0
  : Representation(EPR_MaxSigned),
180
0
    MinValue(0),
181
0
    MaxValue(0),
182
0
    Bits(0),
183
0
    UsedBits(0),
184
0
    AbsMinimum(0),
185
0
    AbsMaximum(0),
186
0
    RescaleIntercept(0),
187
0
    RescaleSlope(0),
188
0
    LookupTable(0),
189
0
    Rescaling(0),
190
0
    TableData(NULL)
191
0
{
192
0
    if (Init(docu, pixel))
193
0
    {
194
0
        EL_BitsPerTableEntry descMode = ELM_UseValue;
195
0
        if (docu->getFlags() & CIF_IgnoreModalityLutBitDepth)
196
0
            descMode = ELM_IgnoreValue;
197
0
        else if (docu->getFlags() & CIF_CheckLutBitDepth)
198
0
            descMode = ELM_CheckValue;
199
0
        TableData = new DiLookupTable(data, descriptor, explanation, descMode);
200
0
        checkTable();
201
0
        determineRepresentation(docu);
202
0
    }
203
0
}
204
205
206
DiMonoModality::DiMonoModality(const int bits)
207
0
  : Representation(EPR_MaxSigned),
208
0
    MinValue(0),
209
0
    MaxValue(0),
210
0
    Bits(bits),
211
0
    UsedBits(0),
212
0
    AbsMinimum(0),
213
0
    AbsMaximum(DicomImageClass::maxval(bits)),
214
0
    RescaleIntercept(0),
215
0
    RescaleSlope(0),
216
0
    LookupTable(0),
217
0
    Rescaling(0),
218
0
    TableData(NULL)
219
0
{
220
0
}
221
222
223
/*--------------*
224
 *  destructor  *
225
 *--------------*/
226
227
DiMonoModality::~DiMonoModality()
228
0
{
229
0
    delete TableData;
230
0
}
231
232
233
/*********************************************************************/
234
235
236
int DiMonoModality::Init(const DiDocument *docu,
237
                         DiInputPixel *pixel)
238
0
{
239
0
    if ((docu != NULL) && (pixel != NULL))
240
0
    {
241
0
        pixel->determineMinMax();
242
0
        MinValue = pixel->getMinValue(1 /* selected range of pixels only */);
243
0
        MaxValue = pixel->getMaxValue(1 /* selected range of pixels only */);
244
0
        Bits = pixel->getBits();
245
0
        AbsMinimum = pixel->getAbsMinimum();
246
0
        AbsMaximum = pixel->getAbsMaximum();
247
0
        Uint16 us;
248
0
        if (docu->getValue(DCM_SamplesPerPixel, us) > 0)
249
0
        {
250
0
            if (us != 1)
251
0
                DCMIMGLE_WARN("invalid value for 'SamplesPerPixel' (" << us << ") ... assuming 1");
252
0
        } else
253
0
            DCMIMGLE_WARN("missing value for 'SamplesPerPixel' ... assuming 1");
254
0
        return 1;
255
0
    }
256
0
    return 0;
257
0
}
258
259
260
void DiMonoModality::checkTable()
261
0
{
262
0
    if (TableData != NULL)
263
0
    {
264
0
        LookupTable = TableData->isValid();
265
0
        if (LookupTable)
266
0
        {
267
0
            MinValue = TableData->getMinValue();
268
0
            MaxValue = TableData->getMaxValue();
269
0
            Bits = TableData->getBits();
270
0
            AbsMinimum = 0;
271
0
            AbsMaximum = DicomImageClass::maxval(Bits);
272
0
        }
273
0
    }
274
0
}
275
276
277
void DiMonoModality::checkRescaling(const DiInputPixel *pixel)
278
0
{
279
0
    if (Rescaling)
280
0
    {
281
0
        if (LookupTable)
282
0
        {
283
0
            DCMIMGLE_WARN("redundant values for 'RescaleSlope/Intercept' ... using modality LUT transformation");
284
0
            Rescaling = 0;
285
0
        } else {
286
0
            if (RescaleSlope == 0)
287
0
            {
288
0
                DCMIMGLE_WARN("invalid value for 'RescaleSlope' (" << RescaleSlope << ") ... ignoring modality transformation");
289
0
                Rescaling = 0;
290
0
            } else {
291
0
                if (RescaleSlope < 0)                                       // negative slope value
292
0
                {
293
0
                    const double temp = MinValue;
294
0
                    MinValue = MaxValue * RescaleSlope + RescaleIntercept;
295
0
                    MaxValue = temp * RescaleSlope + RescaleIntercept;
296
0
                    AbsMinimum = pixel->getAbsMaximum() * RescaleSlope + RescaleIntercept;
297
0
                    AbsMaximum = pixel->getAbsMinimum() * RescaleSlope + RescaleIntercept;
298
0
                } else {                                                    // positive slope value
299
0
                    MinValue = MinValue * RescaleSlope + RescaleIntercept;
300
0
                    MaxValue = MaxValue * RescaleSlope + RescaleIntercept;
301
0
                    AbsMinimum = pixel->getAbsMinimum() * RescaleSlope + RescaleIntercept;
302
0
                    AbsMaximum = pixel->getAbsMaximum() * RescaleSlope + RescaleIntercept;
303
0
                }
304
0
                Bits = DicomImageClass::rangeToBits(AbsMinimum, AbsMaximum);
305
0
            }
306
0
        }
307
0
    }
308
0
}
309
310
311
void DiMonoModality::determineRepresentation(const DiDocument *docu)
312
0
{
313
0
    UsedBits = DicomImageClass::rangeToBits(MinValue, MaxValue);
314
0
    if ((docu != NULL) && (docu->getFlags() & CIF_UseAbsolutePixelRange))
315
0
        Representation = DicomImageClass::determineRepresentation(AbsMinimum, AbsMaximum);
316
0
    else
317
0
        Representation = DicomImageClass::determineRepresentation(MinValue, MaxValue);
318
0
    DCMIMGLE_TRACE("internal representation for monochrome images: "
319
0
        << DicomImageClass::getRepresentationBits(Representation) << " bits ("
320
0
        << (DicomImageClass::isRepresentationSigned(Representation) ? "signed" : "unsigned") << ")");
321
0
}