1 /*
2 *
3 * Copyright (C) 2000-2020, 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: dcmsr
15 *
16 * Author: Joerg Riesmeier
17 *
18 * Purpose:
19 * classes: DSRImageReferenceValue
20 *
21 */
22
23
24 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
25
26 #include "dcmtk/dcmsr/dsrimgvl.h"
27 #include "dcmtk/dcmsr/dsrxmld.h"
28
29 #include "dcmtk/dcmimgle/dcmimage.h"
30 #include "dcmtk/dcmimgle/diutils.h"
31 #include "dcmtk/dcmimage/diregist.h" /* add support for color images */
32 #include "dcmtk/dcmimage/diquant.h" /* for DcmQuant */
33
34 #include "dcmtk/dcmdata/dcdeftag.h"
35 #include "dcmtk/dcmdata/dcuid.h"
36
37
DSRImageReferenceValue()38 DSRImageReferenceValue::DSRImageReferenceValue()
39 : DSRCompositeReferenceValue(),
40 FrameList(),
41 SegmentList(),
42 PresentationState(),
43 RealWorldValueMapping(),
44 IconImage(NULL)
45 {
46 }
47
48
DSRImageReferenceValue(const OFString & sopClassUID,const OFString & sopInstanceUID,const OFBool check)49 DSRImageReferenceValue::DSRImageReferenceValue(const OFString &sopClassUID,
50 const OFString &sopInstanceUID,
51 const OFBool check)
52 : DSRCompositeReferenceValue(),
53 FrameList(),
54 SegmentList(),
55 PresentationState(),
56 RealWorldValueMapping(),
57 IconImage(NULL)
58 {
59 /* use the set method for checking purposes */
60 setReference(sopClassUID, sopInstanceUID, check);
61 }
62
63
DSRImageReferenceValue(const OFString & imageSOPClassUID,const OFString & imageSOPInstanceUID,const OFString & pstateSOPClassUID,const OFString & pstateSOPInstanceUID,const OFBool check)64 DSRImageReferenceValue::DSRImageReferenceValue(const OFString &imageSOPClassUID,
65 const OFString &imageSOPInstanceUID,
66 const OFString &pstateSOPClassUID,
67 const OFString &pstateSOPInstanceUID,
68 const OFBool check)
69 : DSRCompositeReferenceValue(),
70 FrameList(),
71 SegmentList(),
72 PresentationState(),
73 RealWorldValueMapping(),
74 IconImage(NULL)
75 {
76 /* use the set methods for checking purposes */
77 setReference(imageSOPClassUID, imageSOPInstanceUID, check);
78 setPresentationState(DSRCompositeReferenceValue(pstateSOPClassUID, pstateSOPInstanceUID, OFFalse /*check*/), check);
79 }
80
81
DSRImageReferenceValue(const DSRImageReferenceValue & referenceValue)82 DSRImageReferenceValue::DSRImageReferenceValue(const DSRImageReferenceValue &referenceValue)
83 : DSRCompositeReferenceValue(referenceValue),
84 FrameList(referenceValue.FrameList),
85 SegmentList(referenceValue.SegmentList),
86 PresentationState(referenceValue.PresentationState),
87 RealWorldValueMapping(referenceValue.RealWorldValueMapping),
88 IconImage(NULL)
89 {
90 /* do not check values since this would be unexpected to the user */
91
92 /* create copy of icon image (if any), first frame only */
93 if (referenceValue.IconImage != NULL)
94 IconImage = referenceValue.IconImage->createDicomImage(0 /*fstart*/, 1 /*fcount*/);
95 }
96
97
DSRImageReferenceValue(const DSRCompositeReferenceValue & imageReferenceValue,const DSRCompositeReferenceValue & pstateReferenceValue)98 DSRImageReferenceValue::DSRImageReferenceValue(const DSRCompositeReferenceValue &imageReferenceValue,
99 const DSRCompositeReferenceValue &pstateReferenceValue)
100 : DSRCompositeReferenceValue(imageReferenceValue),
101 FrameList(),
102 SegmentList(),
103 PresentationState(pstateReferenceValue),
104 RealWorldValueMapping(),
105 IconImage(NULL)
106 {
107 }
108
109
~DSRImageReferenceValue()110 DSRImageReferenceValue::~DSRImageReferenceValue()
111 {
112 deleteIconImage();
113 }
114
115
operator =(const DSRImageReferenceValue & referenceValue)116 DSRImageReferenceValue &DSRImageReferenceValue::operator=(const DSRImageReferenceValue &referenceValue)
117 {
118 /* check for self-assignment, which would create a memory leak */
119 if (this != &referenceValue)
120 {
121 DSRCompositeReferenceValue::operator=(referenceValue);
122 /* do not check since this would be unexpected to the user */
123 FrameList = referenceValue.FrameList;
124 SegmentList = referenceValue.SegmentList;
125 PresentationState = referenceValue.PresentationState;
126 RealWorldValueMapping = referenceValue.RealWorldValueMapping;
127 /* create copy of icon image (if any), first frame only */
128 IconImage = (referenceValue.IconImage != NULL) ? referenceValue.IconImage->createDicomImage(0 /*fstart*/, 1 /*fcount*/) : NULL;
129 }
130 return *this;
131 }
132
133
operator ==(const DSRImageReferenceValue & referenceValue) const134 OFBool DSRImageReferenceValue::operator==(const DSRImageReferenceValue &referenceValue) const
135 {
136 /* the optional icon image is not used for comparison */
137 return DSRCompositeReferenceValue::operator==(referenceValue) &&
138 (FrameList == referenceValue.FrameList) &&
139 (SegmentList == referenceValue.SegmentList) &&
140 (PresentationState == referenceValue.PresentationState) &&
141 (RealWorldValueMapping == referenceValue.RealWorldValueMapping);
142 }
143
144
operator !=(const DSRImageReferenceValue & referenceValue) const145 OFBool DSRImageReferenceValue::operator!=(const DSRImageReferenceValue &referenceValue) const
146 {
147 /* the optional icon image is not used for comparison */
148 return DSRCompositeReferenceValue::operator!=(referenceValue) ||
149 (FrameList != referenceValue.FrameList) ||
150 (SegmentList != referenceValue.SegmentList) ||
151 (PresentationState != referenceValue.PresentationState) ||
152 (RealWorldValueMapping != referenceValue.RealWorldValueMapping);
153 }
154
155
clear()156 void DSRImageReferenceValue::clear()
157 {
158 DSRCompositeReferenceValue::clear();
159 FrameList.clear();
160 SegmentList.clear();
161 PresentationState.clear();
162 RealWorldValueMapping.clear();
163 deleteIconImage();
164 }
165
166
isValid() const167 OFBool DSRImageReferenceValue::isValid() const
168 {
169 return DSRCompositeReferenceValue::isValid() && checkCurrentValue().good();
170 }
171
172
isShort(const size_t flags) const173 OFBool DSRImageReferenceValue::isShort(const size_t flags) const
174 {
175 return (FrameList.isEmpty() && SegmentList.isEmpty()) || !(flags & DSRTypes::HF_renderFullData);
176 }
177
178
isSegmentation() const179 OFBool DSRImageReferenceValue::isSegmentation() const
180 {
181 return isSegmentationObject(SOPClassUID);
182 }
183
184
print(STD_NAMESPACE ostream & stream,const size_t flags) const185 OFCondition DSRImageReferenceValue::print(STD_NAMESPACE ostream &stream,
186 const size_t flags) const
187 {
188 /* first, determine SOP class component */
189 OFString sopClassString = "\"" + SOPClassUID + "\"";
190 if (!(flags & DSRTypes::PF_printSOPClassUID))
191 {
192 if (flags & DSRTypes::PF_printLongSOPClassName)
193 {
194 /* look up name of known SOP classes */
195 const char *className = dcmFindNameOfUID(SOPClassUID.c_str());
196 if (className != NULL)
197 sopClassString = className;
198 } else {
199 /* create short name for SOP class, e.g. "CT image" */
200 const char *modality = dcmSOPClassUIDToModality(SOPClassUID.c_str());
201 if (modality != NULL)
202 sopClassString = OFString(modality) + " image";
203 }
204 }
205 /* and then, print it */
206 stream << "(" << sopClassString << ",";
207 /* print SOP instance component (if desired) */
208 if (flags & DSRTypes::PF_printSOPInstanceUID)
209 stream << "\"" << SOPInstanceUID << "\"";
210 /* print list of frame or segment numbers (if present) */
211 if (!FrameList.isEmpty())
212 {
213 stream << ",";
214 FrameList.print(stream, flags);
215 }
216 else if (!SegmentList.isEmpty())
217 {
218 stream << ",";
219 SegmentList.print(stream, flags);
220 }
221 stream << ")";
222 /* print information on presentation state (if present) */
223 if (PresentationState.isValid())
224 {
225 /* first, determine SOP class component */
226 OFString pstateClassString = "\"" + PresentationState.getSOPClassUID() + "\"";
227 if (!(flags & DSRTypes::PF_printSOPClassUID))
228 {
229 if (flags & DSRTypes::PF_printLongSOPClassName)
230 {
231 /* look up name of known SOP classes */
232 const char *className = dcmFindNameOfUID(PresentationState.getSOPClassUID().c_str());
233 if (className != NULL)
234 pstateClassString = className;
235 } else {
236 /* create short name for presentation state, e.g. "GSPS" */
237 const DSRTypes::E_PresentationStateType pstateType = DSRTypes::sopClassUIDToPresentationStateType(PresentationState.getSOPClassUID());
238 if (pstateType != DSRTypes::PT_invalid)
239 pstateClassString = DSRTypes::presentationStateTypeToShortName(pstateType);
240 }
241 }
242 /* and, then print it */
243 stream << ",(" << pstateClassString << ",";
244 /* also print SOP instance component (if desired) */
245 if (flags & DSRTypes::PF_printSOPInstanceUID)
246 stream << "\"" << PresentationState.getSOPInstanceUID() << "\"";
247 stream << ")";
248 }
249 return EC_Normal;
250 }
251
252
readXML(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)253 OFCondition DSRImageReferenceValue::readXML(const DSRXMLDocument &doc,
254 DSRXMLCursor cursor,
255 const size_t flags)
256 {
257 /* first read general composite reference information */
258 OFCondition result = DSRCompositeReferenceValue::readXML(doc, cursor, flags);
259 /* then read image related XML tags */
260 if (result.good())
261 {
262 cursor.gotoChild();
263 /* either frame or segment list (conditional) */
264 DSRXMLCursor childCursor = doc.getNamedNode(cursor, "frames", OFFalse /*required*/);
265 if (childCursor.valid())
266 {
267 OFString tmpString;
268 /* put element content to the frame list */
269 result = FrameList.putString(doc.getStringFromNodeContent(childCursor, tmpString).c_str());
270 } else {
271 childCursor = doc.getNamedNode(cursor, "segments", OFFalse /*required*/);
272 if (childCursor.valid())
273 {
274 OFString tmpString;
275 /* put element content to the segment list */
276 result = SegmentList.putString(doc.getStringFromNodeContent(childCursor, tmpString).c_str());
277 }
278 }
279 if (result.good())
280 {
281 /* presentation state object (optional) */
282 childCursor = doc.getNamedNode(cursor, "pstate", OFFalse /*required*/);
283 if (childCursor.valid())
284 result = PresentationState.readXML(doc, childCursor, flags);
285 }
286 if (result.good())
287 {
288 /* real world value mapping object (optional) */
289 childCursor = doc.getNamedNode(cursor, "mapping", OFFalse /*required*/);
290 if (childCursor.valid())
291 result = RealWorldValueMapping.readXML(doc, childCursor, flags);
292 }
293 }
294 return result;
295 }
296
297
writeXML(STD_NAMESPACE ostream & stream,const size_t flags) const298 OFCondition DSRImageReferenceValue::writeXML(STD_NAMESPACE ostream &stream,
299 const size_t flags) const
300 {
301 OFCondition result = DSRCompositeReferenceValue::writeXML(stream, flags);
302 /* either frame or segment list (conditional) */
303 if (((flags & DSRTypes::XF_writeEmptyTags) && SegmentList.isEmpty()) || !FrameList.isEmpty())
304 {
305 stream << "<frames>";
306 FrameList.print(stream);
307 stream << "</frames>" << OFendl;
308 }
309 else if ((flags & DSRTypes::XF_writeEmptyTags) || !SegmentList.isEmpty())
310 {
311 stream << "<segments>";
312 SegmentList.print(stream);
313 stream << "</segments>" << OFendl;
314 }
315 /* presentation state object (optional) */
316 if ((flags & DSRTypes::XF_writeEmptyTags) || PresentationState.isValid())
317 {
318 stream << "<pstate>" << OFendl;
319 if (PresentationState.isValid())
320 PresentationState.writeXML(stream, flags);
321 stream << "</pstate>" << OFendl;
322 }
323 /* real world value mapping object (optional) */
324 if ((flags & DSRTypes::XF_writeEmptyTags) || RealWorldValueMapping.isValid())
325 {
326 stream << "<mapping>" << OFendl;
327 if (RealWorldValueMapping.isValid())
328 RealWorldValueMapping.writeXML(stream, flags);
329 stream << "</mapping>" << OFendl;
330 }
331 return result;
332 }
333
334
readItem(DcmItem & dataset,const size_t flags)335 OFCondition DSRImageReferenceValue::readItem(DcmItem &dataset,
336 const size_t flags)
337 {
338 /* be very careful, delete any previously created icon image (should never apply) */
339 deleteIconImage();
340 /* read ReferencedSOPClassUID and ReferencedSOPInstanceUID */
341 OFCondition result = DSRCompositeReferenceValue::readItem(dataset, flags);
342 if (result.good())
343 {
344 /* read ReferencedFrameNumber (conditional) */
345 FrameList.read(dataset, flags);
346 /* read ReferencedSegmentNumber (conditional) */
347 SegmentList.read(dataset, flags);
348 /* read ReferencedSOPSequence (Presentation State, optional) */
349 PresentationState.readSequence(dataset, DCM_ReferencedSOPSequence, "3" /*type*/, flags);
350 /* read ReferencedRealWorldValueMappingInstanceSequence (optional) */
351 RealWorldValueMapping.readSequence(dataset, DCM_ReferencedRealWorldValueMappingInstanceSequence, "3" /*type*/, flags);
352 /* read IconImageSequence (optional) */
353 DcmSequenceOfItems *dseq = NULL;
354 /* use local status variable since the sequence is optional */
355 const OFCondition seqStatus = dataset.findAndGetSequence(DCM_IconImageSequence, dseq);
356 DSRTypes::checkElementValue(dseq, DCM_IconImageSequence, "1", "3", seqStatus, "IMAGE content item");
357 if (seqStatus.good())
358 {
359 /* check for empty sequence (allowed!) */
360 if (!dseq->isEmpty())
361 {
362 /* read first item */
363 DcmItem *ditem = dseq->getItem(0);
364 if ((ditem != NULL) && !ditem->isEmpty())
365 {
366 /* try to load/process the icon image */
367 IconImage = new DicomImage(ditem, EXS_LittleEndianExplicit);
368 if (IconImage != NULL)
369 {
370 if (IconImage->getStatus() != EIS_Normal)
371 result = SR_EC_CannotCreateIconImage;
372 } else
373 result = EC_MemoryExhausted;
374 } else
375 result = SR_EC_InvalidDocumentTree;
376 }
377 }
378 /* check data and report warnings if any */
379 checkListData(SOPClassUID, FrameList, SegmentList, OFTrue /*reportWarnings*/);
380 }
381 return result;
382 }
383
384
writeItem(DcmItem & dataset) const385 OFCondition DSRImageReferenceValue::writeItem(DcmItem &dataset) const
386 {
387 /* write ReferencedSOPClassUID and ReferencedSOPInstanceUID */
388 OFCondition result = DSRCompositeReferenceValue::writeItem(dataset);
389 /* write ReferencedFrameNumber or ReferencedSegmentNumber (conditional) */
390 if (result.good())
391 {
392 if (!FrameList.isEmpty())
393 result = FrameList.write(dataset);
394 else if (!SegmentList.isEmpty())
395 result = SegmentList.write(dataset);
396 }
397 /* write ReferencedSOPSequence (Presentation State, optional) */
398 if (result.good())
399 {
400 if (PresentationState.isValid())
401 result = PresentationState.writeSequence(dataset, DCM_ReferencedSOPSequence);
402 }
403 /* write ReferencedRealWorldValueMappingInstanceSequence (optional) */
404 if (result.good())
405 {
406 if (RealWorldValueMapping.isValid())
407 result = RealWorldValueMapping.writeSequence(dataset, DCM_ReferencedRealWorldValueMappingInstanceSequence);
408 }
409 /* write IconImageSequence (optional) */
410 if (result.good() && (IconImage != NULL))
411 {
412 DcmItem *ditem = NULL;
413 /* create sequence with a single item */
414 result = dataset.findOrCreateSequenceItem(DCM_IconImageSequence, ditem, 0 /*position*/);
415 if (result.good())
416 {
417 /* monochrome images can be written directly */
418 if (IconImage->isMonochrome())
419 {
420 /* write icon image to dataset */
421 if (IconImage->writeFrameToDataset(*ditem))
422 {
423 /* delete unwanted element NumberOfFrames (0028,0008) */
424 ditem->findAndDeleteElement(DCM_NumberOfFrames);
425 } else
426 result = EC_CorruptedData;
427 } else {
428 OFString tmpString;
429 /* color images need to be converted to "PALETTE COLOR" */
430 result = DcmQuant::createPaletteColorImage(*IconImage, *ditem, OFTrue /*writeAsOW*/, OFFalse /*write16BitEntries*/,
431 OFFalse /*floydSteinberg*/, 256 /*numberOfColors*/, tmpString /*description*/);
432 }
433 }
434 }
435 /* check data and report warnings if any */
436 checkListData(SOPClassUID, FrameList, SegmentList, OFTrue /*reportWarnings*/);
437 return result;
438 }
439
440
renderHTML(STD_NAMESPACE ostream & docStream,STD_NAMESPACE ostream & annexStream,size_t & annexNumber,const size_t flags) const441 OFCondition DSRImageReferenceValue::renderHTML(STD_NAMESPACE ostream &docStream,
442 STD_NAMESPACE ostream &annexStream,
443 size_t &annexNumber,
444 const size_t flags) const
445 {
446 /* reference: image */
447 docStream << "<a href=\"" << HTML_HYPERLINK_PREFIX_FOR_CGI;
448 docStream << "?image=" << SOPClassUID << "+" << SOPInstanceUID;
449 /* reference: pstate */
450 if (PresentationState.isValid())
451 {
452 docStream << "&pstate=" << PresentationState.getSOPClassUID();
453 docStream << "+" << PresentationState.getSOPInstanceUID();
454 }
455 /* reference: frames or segments */
456 if (!FrameList.isEmpty())
457 {
458 docStream << "&frames=";
459 FrameList.print(docStream, 0 /*flags*/, '+');
460 }
461 else if (!SegmentList.isEmpty())
462 {
463 docStream << "&segments=";
464 SegmentList.print(docStream, 0 /*flags*/, '+');
465 }
466 docStream << "\">";
467 /* text: image */
468 const char *modality = dcmSOPClassUIDToModality(SOPClassUID.c_str());
469 if (modality != NULL)
470 docStream << modality;
471 else
472 docStream << "unknown";
473 docStream << " image";
474 /* text: presentation state */
475 if (PresentationState.isValid())
476 docStream << " with presentation state";
477 docStream << "</a>";
478 if (!isShort(flags))
479 {
480 const char *lineBreak = (flags & DSRTypes::HF_renderSectionTitlesInline) ? " " :
481 (flags & DSRTypes::HF_XHTML11Compatibility) ? "<br />" : "<br>";
482 if (flags & DSRTypes::HF_currentlyInsideAnnex)
483 {
484 docStream << OFendl << "<p>" << OFendl;
485 /* render frame list (= print)*/
486 docStream << "<b>Referenced Frame Number:</b>" << lineBreak;
487 FrameList.print(docStream);
488 docStream << "</p>";
489 } else {
490 docStream << " ";
491 DSRTypes::createHTMLAnnexEntry(docStream, annexStream, "for more details see", annexNumber, flags);
492 annexStream << "<p>" << OFendl;
493 /* render frame list (= print)*/
494 annexStream << "<b>Referenced Frame Number:</b>" << lineBreak;
495 FrameList.print(annexStream);
496 annexStream << "</p>" << OFendl;
497 }
498 }
499 return EC_Normal;
500 }
501
502
createIconImage(const OFString & filename,const unsigned long frame,const unsigned long width,const unsigned long height)503 OFCondition DSRImageReferenceValue::createIconImage(const OFString &filename,
504 const unsigned long frame,
505 const unsigned long width,
506 const unsigned long height)
507 {
508 /* delete old icon image (if any) */
509 deleteIconImage();
510 OFCondition result = EC_IllegalParameter;
511 if (!filename.empty())
512 {
513 /* try to load specified DICOM image */
514 const unsigned long flags = CIF_UsePartialAccessToPixelData | CIF_NeverAccessEmbeddedOverlays;
515 DicomImage *image = new DicomImage(filename.c_str(), flags, frame, 1 /*fcount*/);
516 if (image != NULL)
517 {
518 /* set VOI window (for monochrome images) */
519 if (image->isMonochrome() && !image->setWindow(0))
520 image->setMinMaxWindow();
521 /* do the real work: create a down-scaled version of the DICOM image */
522 result = createIconImage(image, width, height);
523 delete image;
524 } else
525 result = EC_MemoryExhausted;
526 }
527 return result;
528 }
529
530
createIconImage(DcmObject * object,const E_TransferSyntax xfer,const unsigned long frame,const unsigned long width,const unsigned long height)531 OFCondition DSRImageReferenceValue::createIconImage(DcmObject *object,
532 const E_TransferSyntax xfer,
533 const unsigned long frame,
534 const unsigned long width,
535 const unsigned long height)
536 {
537 /* delete old icon image (if any) */
538 deleteIconImage();
539 OFCondition result = EC_IllegalParameter;
540 if (object != NULL)
541 {
542 /* try to load specified DICOM image */
543 const unsigned long flags = CIF_UsePartialAccessToPixelData | CIF_NeverAccessEmbeddedOverlays;
544 DicomImage *image = new DicomImage(object, xfer, flags, frame, 1 /*fcount*/);
545 if (image != NULL)
546 {
547 /* set VOI window (for monochrome images) */
548 if (image->isMonochrome() && !image->setWindow(0))
549 image->setMinMaxWindow();
550 /* do the real work: create a down-scaled version of the DICOM image */
551 result = createIconImage(image, width, height);
552 delete image;
553 } else
554 result = EC_MemoryExhausted;
555 }
556 return result;
557 }
558
559
createIconImage(const DicomImage * image,const unsigned long width,const unsigned long height)560 OFCondition DSRImageReferenceValue::createIconImage(const DicomImage *image,
561 const unsigned long width,
562 const unsigned long height)
563 {
564 /* delete old icon image (if any) */
565 deleteIconImage();
566 OFCondition result = EC_IllegalParameter;
567 if (image != NULL)
568 {
569 const EI_Status imageStatus = image->getStatus();
570 /* check whether image loading/processing was successful */
571 switch (imageStatus)
572 {
573 case EIS_Normal:
574 {
575 if (image->getFrameCount() > 1)
576 DCMSR_DEBUG("DICOM image passed for creating an icon image contains multiple frames");
577 /* create a down-scaled version of the DICOM image */
578 const int aspect = (width == 0) || (height == 0);
579 IconImage = image->createScaledImage(width, height, 1 /*interpolate*/, aspect);
580 result = (IconImage != NULL) ? EC_Normal : SR_EC_CannotCreateIconImage;
581 break;
582 }
583 case EIS_InvalidDocument:
584 case EIS_InvalidImage:
585 result = SR_EC_InvalidDocument;
586 break;
587 case EIS_MissingAttribute:
588 result = SR_EC_MandatoryAttributeMissing;
589 break;
590 case EIS_InvalidValue:
591 result = SR_EC_InvalidValue;
592 break;
593 case EIS_NotSupportedValue:
594 result = SR_EC_UnsupportedValue;
595 break;
596 case EIS_MemoryFailure:
597 result = EC_MemoryExhausted;
598 break;
599 default:
600 /* this is the fallback for all other kind of errors */
601 result = SR_EC_CannotCreateIconImage;
602 break;
603 }
604 }
605 return result;
606 }
607
608
deleteIconImage()609 void DSRImageReferenceValue::deleteIconImage()
610 {
611 delete IconImage;
612 IconImage = NULL;
613 }
614
615
getValue(DSRImageReferenceValue & referenceValue) const616 OFCondition DSRImageReferenceValue::getValue(DSRImageReferenceValue &referenceValue) const
617 {
618 referenceValue = *this;
619 return EC_Normal;
620 }
621
622
setValue(const DSRImageReferenceValue & referenceValue,const OFBool check)623 OFCondition DSRImageReferenceValue::setValue(const DSRImageReferenceValue &referenceValue,
624 const OFBool check)
625 {
626 OFCondition result = DSRCompositeReferenceValue::setValue(referenceValue, check);
627 if (result.good())
628 {
629 FrameList = referenceValue.FrameList;
630 SegmentList = referenceValue.SegmentList;
631 /* ignore status (return value) since the references are optional */
632 setPresentationState(referenceValue.PresentationState, check);
633 setRealWorldValueMapping(referenceValue.RealWorldValueMapping, check);
634 }
635 return result;
636 }
637
638
setPresentationState(const DSRCompositeReferenceValue & pstateValue,const OFBool check)639 OFCondition DSRImageReferenceValue::setPresentationState(const DSRCompositeReferenceValue &pstateValue,
640 const OFBool check)
641 {
642 OFCondition result = EC_Normal;
643 /* check whether the passed value is valid */
644 if (check)
645 result = checkPresentationState(pstateValue);
646 /* both UID values need to be empty or non-empty (optional) */
647 else if (pstateValue.getSOPClassUID().empty() != pstateValue.getSOPInstanceUID().empty())
648 result = SR_EC_InvalidValue;
649 if (result.good())
650 PresentationState = pstateValue;
651 return result;
652 }
653
654
setRealWorldValueMapping(const DSRCompositeReferenceValue & mappingValue,const OFBool check)655 OFCondition DSRImageReferenceValue::setRealWorldValueMapping(const DSRCompositeReferenceValue &mappingValue,
656 const OFBool check)
657 {
658 OFCondition result = EC_Normal;
659 /* check whether the passed value is valid */
660 if (check)
661 result = checkRealWorldValueMapping(mappingValue);
662 /* both UID values need to be empty or non-empty (optional) */
663 else if (mappingValue.getSOPClassUID().empty() != mappingValue.getSOPInstanceUID().empty())
664 result = SR_EC_InvalidValue;
665 if (result.good())
666 RealWorldValueMapping = mappingValue;
667 return result;
668 }
669
670
appliesToFrame(const Sint32 frameNumber) const671 OFBool DSRImageReferenceValue::appliesToFrame(const Sint32 frameNumber) const
672 {
673 OFBool result = OFTrue;
674 if (!FrameList.isEmpty())
675 result = FrameList.isElement(frameNumber);
676 return result;
677 }
678
679
appliesToSegment(const Uint16 segmentNumber) const680 OFBool DSRImageReferenceValue::appliesToSegment(const Uint16 segmentNumber) const
681 {
682 OFBool result = OFTrue;
683 if (!SegmentList.isEmpty())
684 result = SegmentList.isElement(segmentNumber);
685 return result;
686 }
687
688
isSegmentationObject(const OFString & sopClassUID) const689 OFBool DSRImageReferenceValue::isSegmentationObject(const OFString &sopClassUID) const
690 {
691 /* check for all segmentation SOP classes (according to DICOM PS 3.6-2020c) */
692 return (sopClassUID == UID_SegmentationStorage) || (sopClassUID == UID_SurfaceSegmentationStorage);
693 }
694
695
checkSOPClassUID(const OFString & sopClassUID) const696 OFCondition DSRImageReferenceValue::checkSOPClassUID(const OFString &sopClassUID) const
697 {
698 OFCondition result = DSRCompositeReferenceValue::checkSOPClassUID(sopClassUID);
699 if (result.good())
700 {
701 /* check for all valid/known SOP classes (according to DICOM PS 3.6) */
702 if (!dcmIsImageStorageSOPClassUID(sopClassUID.c_str()) && !isSegmentationObject(sopClassUID))
703 {
704 result = SR_EC_InvalidValue;
705 }
706 }
707 return result;
708 }
709
710
checkPresentationState(const DSRCompositeReferenceValue & referenceValue) const711 OFCondition DSRImageReferenceValue::checkPresentationState(const DSRCompositeReferenceValue &referenceValue) const
712 {
713 OFCondition result = EC_Normal;
714 /* the reference to a presentation state object is optional, so an empty value is also valid */
715 if (!referenceValue.isEmpty())
716 {
717 if (DSRTypes::sopClassUIDToPresentationStateType(referenceValue.getSOPClassUID()) == DSRTypes::PT_invalid)
718 result = SR_EC_InvalidValue;
719 }
720 return result;
721 }
722
723
checkRealWorldValueMapping(const DSRCompositeReferenceValue & referenceValue) const724 OFCondition DSRImageReferenceValue::checkRealWorldValueMapping(const DSRCompositeReferenceValue &referenceValue) const
725 {
726 OFCondition result = EC_Normal;
727 /* the reference to a real world value mapping object is optional, so an empty value is also valid */
728 if (!referenceValue.isEmpty())
729 {
730 if (referenceValue.getSOPClassUID() != UID_RealWorldValueMappingStorage)
731 result = SR_EC_InvalidValue;
732 }
733 return result;
734 }
735
736
737 // helper macro to avoid annoying check of boolean flag
738 #define REPORT_WARNING(msg) { if (reportWarnings) DCMSR_WARN(msg); }
739
checkListData(const OFString & sopClassUID,const DSRImageFrameList & frameList,const DSRImageSegmentList & segmentList,const OFBool reportWarnings) const740 OFCondition DSRImageReferenceValue::checkListData(const OFString &sopClassUID,
741 const DSRImageFrameList &frameList,
742 const DSRImageSegmentList &segmentList,
743 const OFBool reportWarnings) const
744 {
745 OFCondition result = EC_Normal;
746 /* check whether both lists of referenced frame and segment numbers are non-empty */
747 if (!frameList.isEmpty() && !segmentList.isEmpty())
748 {
749 /* this is just a warning since only one list will ever be written */
750 REPORT_WARNING("Both Referenced Frame Number and Referenced Segment Number present in IMAGE content item")
751 }
752 /* check whether referenced image is a segmentation object (see "type 1C" condition) */
753 if (!segmentList.isEmpty() && !isSegmentationObject(sopClassUID))
754 {
755 REPORT_WARNING("Referenced Segment Number present in IMAGE content item for non-segmentation object")
756 result = SR_EC_InvalidValue;
757 }
758 /* tbd: check whether referenced image is a multi-frame image? (see "type 1C" condition) */
759 return result;
760 }
761
762
checkCurrentValue() const763 OFCondition DSRImageReferenceValue::checkCurrentValue() const
764 {
765 OFCondition result = DSRCompositeReferenceValue::checkCurrentValue();
766 if (result.good())
767 result = checkPresentationState(PresentationState);
768 if (result.good())
769 result = checkRealWorldValueMapping(RealWorldValueMapping);
770 if (result.good())
771 result = checkListData(SOPClassUID, FrameList, SegmentList);
772 return result;
773 }
774