/src/dcmtk/dcmimgle/libsrc/didocu.cc
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (C) 1996-2025, 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: DicomDocument (Source) |
19 | | * |
20 | | */ |
21 | | |
22 | | |
23 | | #include "dcmtk/config/osconfig.h" |
24 | | |
25 | | #include "dcmtk/dcmdata/dctk.h" |
26 | | |
27 | | #include "dcmtk/dcmimgle/didocu.h" |
28 | | #include "dcmtk/dcmimgle/diutils.h" |
29 | | |
30 | | |
31 | | /*----------------* |
32 | | * constructors * |
33 | | *----------------*/ |
34 | | |
35 | | DiDocument::DiDocument(const OFFilename &filename, |
36 | | const unsigned long flags, |
37 | | const unsigned long fstart, |
38 | | const unsigned long fcount) |
39 | 0 | : Object(NULL), |
40 | 0 | FileFormat(new DcmFileFormat()), |
41 | 0 | PixelData(NULL), |
42 | 0 | Xfer(EXS_Unknown), |
43 | 0 | FrameStart(fstart), |
44 | 0 | FrameCount(fcount), |
45 | 0 | Flags(flags), |
46 | 0 | PhotometricInterpretation() |
47 | 0 | { |
48 | 0 | if (FileFormat) |
49 | 0 | { |
50 | 0 | if (FileFormat->loadFile(filename).bad()) |
51 | 0 | { |
52 | 0 | DCMIMGLE_ERROR("can't read file '" << filename << "'"); |
53 | 0 | delete FileFormat; |
54 | 0 | FileFormat = NULL; |
55 | 0 | } else { |
56 | 0 | Object = FileFormat->getDataset(); |
57 | 0 | if (Object != NULL) |
58 | 0 | { |
59 | 0 | Xfer = OFstatic_cast(DcmDataset *, Object)->getOriginalXfer(); |
60 | 0 | convertPixelData(); |
61 | 0 | } |
62 | 0 | } |
63 | 0 | } |
64 | 0 | } |
65 | | |
66 | | |
67 | | DiDocument::DiDocument(DcmObject *object, |
68 | | const E_TransferSyntax xfer, |
69 | | const unsigned long flags, |
70 | | const unsigned long fstart, |
71 | | const unsigned long fcount) |
72 | 0 | : Object(NULL), |
73 | 0 | FileFormat(NULL), |
74 | 0 | PixelData(NULL), |
75 | 0 | Xfer(xfer), |
76 | 0 | FrameStart(fstart), |
77 | 0 | FrameCount(fcount), |
78 | 0 | Flags(flags), |
79 | 0 | PhotometricInterpretation() |
80 | 0 | { |
81 | 0 | if (object != NULL) |
82 | 0 | { |
83 | 0 | const DcmEVR classType = object->ident(); |
84 | | // check whether given DICOM object has a valid type |
85 | 0 | if (classType == EVR_fileFormat) |
86 | 0 | { |
87 | | // store reference to DICOM file format to be deleted on object destruction |
88 | 0 | if (Flags & CIF_TakeOverExternalDataset) |
89 | 0 | FileFormat = OFstatic_cast(DcmFileFormat *, object); |
90 | 0 | Object = OFstatic_cast(DcmFileFormat *, object)->getDataset(); |
91 | 0 | } |
92 | 0 | else if ((classType == EVR_dataset) || (classType == EVR_item)) |
93 | 0 | Object = object; |
94 | 0 | else |
95 | 0 | DCMIMGLE_ERROR("invalid DICOM object passed to constructor (wrong class)"); |
96 | 0 | if (Object != NULL) |
97 | 0 | { |
98 | | // try to determine the transfer syntax from the given object |
99 | 0 | if (Xfer == EXS_Unknown) |
100 | 0 | { |
101 | | // check type before casting the object |
102 | 0 | if (Object->ident() == EVR_dataset) |
103 | 0 | Xfer = OFstatic_cast(DcmDataset *, Object)->getOriginalXfer(); |
104 | 0 | else // could only be an item |
105 | 0 | DCMIMGLE_WARN("can't determine original transfer syntax from given DICOM object"); |
106 | 0 | } |
107 | 0 | convertPixelData(); |
108 | 0 | } |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | |
113 | | void DiDocument::convertPixelData() |
114 | 0 | { |
115 | 0 | DcmStack pstack; |
116 | 0 | OFCondition status; |
117 | 0 | DcmXfer xfer(Xfer); |
118 | 0 | DCMIMGLE_DEBUG("transfer syntax of DICOM dataset: " << xfer.getXferName() << " (" << xfer.getXferID() << ")"); |
119 | | // only search on main dataset level |
120 | 0 | if (search(DCM_PixelData, pstack)) |
121 | 0 | { |
122 | 0 | DcmObject *pobject = pstack.top(); |
123 | 0 | if (pobject != NULL) |
124 | 0 | { |
125 | | // check for correct class before type casting |
126 | 0 | if (pobject->ident() == EVR_PixelData) |
127 | 0 | { |
128 | 0 | PixelData = OFstatic_cast(DcmPixelData *, pobject); |
129 | | // check for a special (faulty) case where the original pixel data is uncompressed in |
130 | | // native format and the transfer syntax of the dataset refers to encapsulated format |
131 | 0 | if (Object->ident() == EVR_dataset) |
132 | 0 | { |
133 | 0 | E_TransferSyntax repType = EXS_Unknown; |
134 | 0 | const DcmRepresentationParameter *repParam = NULL; |
135 | 0 | PixelData->getOriginalRepresentationKey(repType, repParam); |
136 | 0 | if (xfer.usesEncapsulatedFormat() && DcmXfer(repType).usesNativeFormat()) |
137 | 0 | { |
138 | 0 | DCMIMGLE_WARN("pixel data is stored in native format, although " |
139 | 0 | << "the transfer syntax of the dataset refers to encapsulated format"); |
140 | 0 | } |
141 | 0 | } |
142 | | // convert pixel data to uncompressed format (if required) |
143 | 0 | if ((Flags & CIF_DecompressCompletePixelData) || !(Flags & CIF_UsePartialAccessToPixelData)) |
144 | 0 | { |
145 | 0 | if (Object->ident() == EVR_dataset) |
146 | 0 | { |
147 | | // Call DcmDataset::chooseRepresentation() to enable an update |
148 | | // of the transfer syntax attributes of the dataset instance |
149 | 0 | status = OFstatic_cast(DcmDataset *, Object)->chooseRepresentation(EXS_LittleEndianExplicit, NULL); |
150 | 0 | } |
151 | 0 | else |
152 | 0 | { |
153 | | // Object is a DcmItem instance. Directly call DcmPixelData::chooseRepresentation(). |
154 | 0 | pstack.clear(); |
155 | | // push reference to DICOM dataset on the stack (required for decompression process) |
156 | 0 | pstack.push(Object); |
157 | | // dummy stack entry |
158 | 0 | pstack.push(PixelData); |
159 | 0 | status = PixelData->chooseRepresentation(EXS_LittleEndianExplicit, NULL, pstack); |
160 | 0 | } |
161 | 0 | if (status.good()) |
162 | 0 | { |
163 | | // set transfer syntax to native format (uncompressed pixel data) |
164 | 0 | if (xfer.usesEncapsulatedFormat()) |
165 | 0 | { |
166 | 0 | Xfer = EXS_LittleEndianExplicit; |
167 | 0 | DCMIMGLE_DEBUG("decompressed complete pixel data in memory: " << PixelData->getLength(Xfer) << " bytes"); |
168 | 0 | } |
169 | 0 | } else { |
170 | 0 | DCMIMGLE_ERROR("can't change to unencapsulated representation for pixel data"); |
171 | 0 | DCMIMGLE_DEBUG("DcmPixelData::chooseRepresentation() returned: " << status.text()); |
172 | 0 | } |
173 | 0 | } |
174 | | // determine color model of the decompressed image |
175 | 0 | status = PixelData->getDecompressedColorModel(OFstatic_cast(DcmItem *, Object), PhotometricInterpretation); |
176 | 0 | if (status.bad()) |
177 | 0 | { |
178 | 0 | if (Flags & CIF_AcrNemaCompatibility) |
179 | 0 | { |
180 | 0 | DCMIMGLE_WARN("can't determine 'PhotometricInterpretation' of decompressed image " |
181 | 0 | << "... assuming MONOCHROME2 (ACR-NEMA compatibility)"); |
182 | 0 | } else |
183 | 0 | DCMIMGLE_ERROR("can't determine 'PhotometricInterpretation' of decompressed image"); |
184 | 0 | DCMIMGLE_DEBUG("DcmPixelData::getDecompressedColorModel() returned: " << status.text()); |
185 | 0 | } |
186 | 0 | } else { |
187 | 0 | DCMIMGLE_ERROR("invalid pixel data in DICOM dataset (wrong class)"); |
188 | 0 | DCMIMGLE_DEBUG("found PixelData " << DCM_PixelData << " as an instance of the class for VR '" |
189 | 0 | << OFSTRING_GUARD(DcmVR(pobject->ident()).getVRName()) << "' instead of '" |
190 | 0 | << OFSTRING_GUARD(DcmVR(EVR_PixelData).getVRName()) << "'"); |
191 | 0 | } |
192 | 0 | } else |
193 | 0 | DCMIMGLE_ERROR("invalid pixel data in DICOM dataset"); |
194 | 0 | } else |
195 | 0 | DCMIMGLE_ERROR("no pixel data found in DICOM dataset"); |
196 | 0 | } |
197 | | |
198 | | |
199 | | /*--------------* |
200 | | * destructor * |
201 | | *--------------*/ |
202 | | |
203 | | DiDocument::~DiDocument() |
204 | 0 | { |
205 | | // DICOM image loaded from file: delete file format (and data set) |
206 | 0 | if (FileFormat != NULL) |
207 | 0 | delete FileFormat; |
208 | | // DICOM image loaded from external data set: only delete if flag is set |
209 | 0 | else if (Flags & CIF_TakeOverExternalDataset) |
210 | 0 | delete Object; |
211 | 0 | } |
212 | | |
213 | | |
214 | | /********************************************************************/ |
215 | | |
216 | | |
217 | | DcmElement *DiDocument::search(const DcmTagKey &tag, |
218 | | DcmObject *obj) const |
219 | 0 | { |
220 | 0 | DcmStack stack; |
221 | 0 | if (obj == NULL) |
222 | 0 | obj = Object; |
223 | | // only search on main dataset level |
224 | 0 | if ((obj != NULL) && (obj->search(tag, stack, ESM_fromHere, OFFalse /* searchIntoSub */) == EC_Normal) && |
225 | 0 | (stack.top()->getLength(Xfer) > 0) && stack.top()->isElement()) |
226 | 0 | { |
227 | 0 | return OFstatic_cast(DcmElement *, stack.top()); |
228 | 0 | } |
229 | 0 | return NULL; |
230 | 0 | } |
231 | | |
232 | | |
233 | | /********************************************************************/ |
234 | | |
235 | | |
236 | | int DiDocument::search(const DcmTagKey &tag, |
237 | | DcmStack &pstack) const |
238 | 0 | { |
239 | 0 | if (pstack.empty()) |
240 | 0 | pstack.push(Object); |
241 | 0 | DcmObject *obj = pstack.top(); |
242 | 0 | if ((obj != NULL) && (obj->search(tag, pstack, ESM_fromHere, OFFalse /* searchIntoSub */) == EC_Normal) && |
243 | 0 | (pstack.top()->getLength(Xfer) > 0)) |
244 | 0 | return 1; |
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | | |
249 | | /********************************************************************/ |
250 | | |
251 | | |
252 | | unsigned long DiDocument::getVM(const DcmTagKey &tag) const |
253 | 0 | { |
254 | 0 | DcmElement *elem = search(tag); |
255 | 0 | if (elem != NULL) |
256 | 0 | return elem->getVM(); |
257 | 0 | return 0; |
258 | 0 | } |
259 | | |
260 | | |
261 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
262 | | Uint16 &returnVal, |
263 | | const unsigned long pos, |
264 | | DcmItem *item, |
265 | | const OFBool allowSigned) const |
266 | 0 | { |
267 | 0 | return getElemValue(search(tag, item), returnVal, pos, allowSigned); |
268 | 0 | } |
269 | | |
270 | | |
271 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
272 | | Sint16 &returnVal, |
273 | | const unsigned long pos, |
274 | | DcmItem *item) const |
275 | 0 | { |
276 | 0 | DcmElement *elem = search(tag, item); |
277 | 0 | if (elem != NULL) |
278 | 0 | { |
279 | 0 | if (elem->getSint16(returnVal, pos).good()) |
280 | 0 | return elem->getVM(); |
281 | 0 | } |
282 | 0 | return 0; |
283 | 0 | } |
284 | | |
285 | | |
286 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
287 | | Uint32 &returnVal, |
288 | | const unsigned long pos, |
289 | | DcmItem *item) const |
290 | 0 | { |
291 | 0 | DcmElement *elem = search(tag, item); |
292 | 0 | if (elem != NULL) |
293 | 0 | { |
294 | 0 | if (elem->getUint32(returnVal, pos).good()) |
295 | 0 | return elem->getVM(); |
296 | 0 | } |
297 | 0 | return 0; |
298 | 0 | } |
299 | | |
300 | | |
301 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
302 | | Sint32 &returnVal, |
303 | | const unsigned long pos, |
304 | | DcmItem *item) const |
305 | 0 | { |
306 | 0 | DcmElement *elem = search(tag, item); |
307 | 0 | if (elem != NULL) |
308 | 0 | { |
309 | 0 | if (elem->getSint32(returnVal, pos).good()) |
310 | 0 | return elem->getVM(); |
311 | 0 | } |
312 | 0 | return 0; |
313 | 0 | } |
314 | | |
315 | | |
316 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
317 | | double &returnVal, |
318 | | const unsigned long pos, |
319 | | DcmItem *item) const |
320 | 0 | { |
321 | 0 | DcmElement *elem = search(tag, item); |
322 | 0 | if (elem != NULL) |
323 | 0 | { |
324 | 0 | if (elem->getFloat64(returnVal, pos).good()) |
325 | 0 | return elem->getVM(); |
326 | 0 | } |
327 | 0 | return 0; |
328 | 0 | } |
329 | | |
330 | | |
331 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
332 | | const Uint16 *&returnVal, |
333 | | DcmItem *item) const |
334 | 0 | { |
335 | 0 | DcmElement *elem = search(tag, item); |
336 | 0 | if (elem != NULL) |
337 | 0 | { |
338 | 0 | Uint16 *val; |
339 | 0 | if (elem->getUint16Array(val).good()) |
340 | 0 | { |
341 | 0 | returnVal = val; |
342 | 0 | const DcmEVR vr = elem->getVR(); |
343 | 0 | if ((vr == EVR_OB) || (vr == EVR_OW) || (vr == EVR_lt)) |
344 | 0 | return elem->getLength(Xfer) / sizeof(Uint16); |
345 | 0 | return elem->getVM(); |
346 | 0 | } |
347 | 0 | } |
348 | 0 | return 0; |
349 | 0 | } |
350 | | |
351 | | |
352 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
353 | | const char *&returnVal, |
354 | | DcmItem *item) const |
355 | 0 | { |
356 | 0 | return getElemValue(search(tag, item), returnVal); |
357 | 0 | } |
358 | | |
359 | | |
360 | | unsigned long DiDocument::getValue(const DcmTagKey &tag, |
361 | | OFString &returnVal, |
362 | | const unsigned long pos, |
363 | | DcmItem *item) const |
364 | 0 | { |
365 | 0 | return getElemValue(search(tag, item), returnVal, pos); |
366 | 0 | } |
367 | | |
368 | | |
369 | | unsigned long DiDocument::getSequence(const DcmTagKey &tag, |
370 | | DcmSequenceOfItems *&seq, |
371 | | DcmItem *item) const |
372 | 0 | { |
373 | 0 | DcmElement *elem = search(tag, item); |
374 | 0 | if ((elem != NULL) && (elem->ident() == EVR_SQ)) |
375 | 0 | return (seq = OFstatic_cast(DcmSequenceOfItems *, elem))->card(); |
376 | 0 | return 0; |
377 | 0 | } |
378 | | |
379 | | |
380 | | unsigned long DiDocument::getElemValue(const DcmElement *elem, |
381 | | Uint16 &returnVal, |
382 | | const unsigned long pos, |
383 | | const OFBool allowSigned) |
384 | 0 | { |
385 | 0 | if (elem != NULL) |
386 | 0 | { |
387 | | // remove 'const' to use non-const methods |
388 | 0 | if (OFconst_cast(DcmElement *, elem)->getUint16(returnVal, pos).good()) |
389 | 0 | return OFconst_cast(DcmElement *, elem)->getVM(); |
390 | 0 | else if (allowSigned) |
391 | 0 | { |
392 | | // try to retrieve signed value ... |
393 | 0 | Sint16 value = 0; |
394 | 0 | if (OFconst_cast(DcmElement *, elem)->getSint16(value, pos).good()) |
395 | 0 | { |
396 | | // ... and cast to unsigned type |
397 | 0 | returnVal = OFstatic_cast(Uint16, value); |
398 | 0 | DCMIMGLE_TRACE("retrieved signed value (" << value << ") at position " << pos |
399 | 0 | << " from element " << OFconst_cast(DcmElement *, elem)->getTag() |
400 | 0 | << ", VR=" << DcmVR(OFconst_cast(DcmElement *, elem)->getVR()).getVRName() |
401 | 0 | << ", VM=" << OFconst_cast(DcmElement *, elem)->getVM()); |
402 | 0 | return OFconst_cast(DcmElement *, elem)->getVM(); |
403 | 0 | } |
404 | 0 | } |
405 | 0 | } |
406 | 0 | return 0; |
407 | 0 | } |
408 | | |
409 | | |
410 | | unsigned long DiDocument::getElemValue(const DcmElement *elem, |
411 | | const Uint16 *&returnVal) |
412 | 0 | { |
413 | 0 | if (elem != NULL) |
414 | 0 | { |
415 | 0 | Uint16 *val; |
416 | | // remove 'const' to use non-const methods |
417 | 0 | if (OFconst_cast(DcmElement *, elem)->getUint16Array(val).good()) |
418 | 0 | { |
419 | 0 | returnVal = val; |
420 | 0 | const DcmEVR vr = OFconst_cast(DcmElement *, elem)->getVR(); |
421 | 0 | if ((vr == EVR_OW) || (vr == EVR_lt)) |
422 | 0 | return OFconst_cast(DcmElement *, elem)->getLength(/*Xfer*/) / sizeof(Uint16); |
423 | 0 | return OFconst_cast(DcmElement *, elem)->getVM(); |
424 | 0 | } |
425 | 0 | } |
426 | 0 | return 0; |
427 | 0 | } |
428 | | |
429 | | |
430 | | unsigned long DiDocument::getElemValue(const DcmElement *elem, |
431 | | const char *&returnVal) |
432 | 0 | { |
433 | 0 | if (elem != NULL) |
434 | 0 | { |
435 | 0 | char *val; |
436 | | // remove 'const' to use non-const methods |
437 | 0 | if (OFconst_cast(DcmElement *, elem)->getString(val).good()) |
438 | 0 | { |
439 | 0 | returnVal = val; |
440 | 0 | return OFconst_cast(DcmElement *, elem)->getVM(); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | return 0; |
444 | 0 | } |
445 | | |
446 | | |
447 | | unsigned long DiDocument::getElemValue(const DcmElement *elem, |
448 | | OFString &returnVal, |
449 | | const unsigned long pos) |
450 | 0 | { |
451 | 0 | if (elem != NULL) |
452 | 0 | { |
453 | | // remove 'const' to use non-const methods |
454 | 0 | if (OFconst_cast(DcmElement *, elem)->getOFString(returnVal, pos).good()) |
455 | 0 | return OFconst_cast(DcmElement *, elem)->getVM(); |
456 | 0 | } |
457 | 0 | return 0; |
458 | 0 | } |