/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 | } |