Skip to content

Commit 6a62927

Browse files
committed
Fixed issue with icon image of IMAGE content item.
Fixed issue with the optional icon image of an IMAGE content item that was not copied when the setValue() method was called. Depending on how the underlying DSRImageReferenceValue class was used, this resulted in the Icon Image Sequence (0088,0200) missing in the created DICOM dataset. Also added new regression tests that make sure that the icon image is created correctly and copied when needed. Thanks to forum user "pintagliata" for the original report.
1 parent 47a007c commit 6a62927

File tree

7 files changed

+247
-11
lines changed

7 files changed

+247
-11
lines changed

dcmsr/include/dcmtk/dcmsr/dsrimgvl.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
*
3-
* Copyright (C) 2000-2024, OFFIS e.V.
3+
* Copyright (C) 2000-2025, OFFIS e.V.
44
* All rights reserved. See COPYRIGHT file for details.
55
*
66
* This software and supporting documentation were developed by
@@ -161,12 +161,19 @@ class DCMTK_DCMSR_EXPORT DSRImageReferenceValue
161161
*/
162162
virtual OFBool isSegmentation() const;
163163

164+
/** check whether an icon image is associated with this image reference.
165+
* This method does not check whether the icon image is valid.
166+
** @return OFTrue if image reference has an icon image, OFFalse otherwise
167+
*/
168+
virtual OFBool hasIconImage() const;
169+
164170
/** print image reference.
165171
* The output of a typical image reference value looks like this: (CT image,"1.2.3") or
166172
* (CT image,"1.2.3"),(GSPS,"1.2.3.4") if a presentation state is present.
167173
* If the SOP class UID is unknown, the UID is printed instead of the related name.
168174
* Also, the list of referenced frame/segment numbers is shown, but not the two UIDs of
169-
* the real world value mapping object (if referenced).
175+
* the real world value mapping object (if referenced). The optional icon image is never
176+
* shown.
170177
** @param stream output stream to which the image reference value should be printed
171178
* @param flags flag used to customize the output (see DSRTypes::PF_xxx)
172179
** @return status, EC_Normal if successful, an error code otherwise
@@ -504,6 +511,12 @@ class DCMTK_DCMSR_EXPORT DSRImageReferenceValue
504511
*/
505512
OFCondition checkCurrentValue(const OFBool reportWarnings = OFFalse) const;
506513

514+
/** copy the given icon image to replace the currently stored one.
515+
* The currently stored icon image is always deleted first.
516+
* @param image pointer to the icon image to be copied (if not NULL)
517+
*/
518+
void copyIconImage(DicomImage *image);
519+
507520

508521
private:
509522

dcmsr/libsrc/dsrimgvl.cc

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
*
3-
* Copyright (C) 2000-2024, OFFIS e.V.
3+
* Copyright (C) 2000-2025, OFFIS e.V.
44
* All rights reserved. See COPYRIGHT file for details.
55
*
66
* This software and supporting documentation were developed by
@@ -89,9 +89,8 @@ DSRImageReferenceValue::DSRImageReferenceValue(const DSRImageReferenceValue &ref
8989
{
9090
/* do not check values since this would be unexpected to the user */
9191

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*/);
92+
/* create copy of icon image (if any) */
93+
copyIconImage(referenceValue.IconImage);
9594
}
9695

9796

@@ -124,8 +123,8 @@ DSRImageReferenceValue &DSRImageReferenceValue::operator=(const DSRImageReferenc
124123
SegmentList = referenceValue.SegmentList;
125124
PresentationState = referenceValue.PresentationState;
126125
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;
126+
/* create copy of icon image (if any) */
127+
copyIconImage(referenceValue.IconImage);
129128
}
130129
return *this;
131130
}
@@ -182,6 +181,12 @@ OFBool DSRImageReferenceValue::isSegmentation() const
182181
}
183182

184183

184+
OFBool DSRImageReferenceValue::hasIconImage() const
185+
{
186+
return (IconImage != NULL);
187+
}
188+
189+
185190
OFCondition DSRImageReferenceValue::print(STD_NAMESPACE ostream &stream,
186191
const size_t flags) const
187192
{
@@ -632,6 +637,8 @@ OFCondition DSRImageReferenceValue::setValue(const DSRImageReferenceValue &refer
632637
/* ignore status (return value) since the references are optional */
633638
setPresentationState(referenceValue.PresentationState, check);
634639
setRealWorldValueMapping(referenceValue.RealWorldValueMapping, check);
640+
/* create copy of icon image (if any) */
641+
copyIconImage(referenceValue.IconImage);
635642
}
636643
return result;
637644
}
@@ -785,6 +792,18 @@ OFCondition DSRImageReferenceValue::checkCurrentValue(const OFBool reportWarning
785792
}
786793

787794

795+
void DSRImageReferenceValue::copyIconImage(DicomImage *image)
796+
{
797+
/* first, delete the current icon image */
798+
delete IconImage;
799+
/* then create a copy of the given icon image (first frame only) */
800+
if (image != NULL)
801+
IconImage = image->createDicomImage(0 /*fstart*/, 1 /*fcount*/);
802+
else
803+
IconImage = NULL;
804+
}
805+
806+
788807
// comparison operators
789808

790809
OFBool operator==(const DSRImageReferenceValue &lhs,

dcmsr/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ DCMTK_ADD_TEST_EXECUTABLE(dcmsr_tests
77
tsrdoc.cc
88
tsrdoctr.cc
99
tsrlist.cc
10+
tsrimgvl.cc
1011
tsrnumvl.cc
1112
tsrtpl.cc
1213
tsrtree.cc

dcmsr/tests/Makefile.dep

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,85 @@ tsrdoctr.o: tsrdoctr.cc ../../config/include/dcmtk/config/osconfig.h \
626626
../include/dcmtk/dcmsr/dsrreftn.h ../include/dcmtk/dcmsr/dsrimgtn.h \
627627
../include/dcmtk/dcmsr/dsrnumtn.h ../include/dcmtk/dcmsr/dsrtextn.h \
628628
../include/dcmtk/dcmsr/dsrstrvl.h
629+
tsrimgvl.o: tsrimgvl.cc ../../config/include/dcmtk/config/osconfig.h \
630+
../../ofstd/include/dcmtk/ofstd/oftest.h \
631+
../../ofstd/include/dcmtk/ofstd/ofconapp.h \
632+
../../ofstd/include/dcmtk/ofstd/oftypes.h \
633+
../../ofstd/include/dcmtk/ofstd/ofdefine.h \
634+
../../ofstd/include/dcmtk/ofstd/ofcast.h \
635+
../../ofstd/include/dcmtk/ofstd/ofexport.h \
636+
../../ofstd/include/dcmtk/ofstd/ofstdinc.h \
637+
../../ofstd/include/dcmtk/ofstd/ofcmdln.h \
638+
../../ofstd/include/dcmtk/ofstd/ofexbl.h \
639+
../../ofstd/include/dcmtk/ofstd/oftraits.h \
640+
../../ofstd/include/dcmtk/ofstd/oflist.h \
641+
../../ofstd/include/dcmtk/ofstd/ofstring.h \
642+
../../ofstd/include/dcmtk/ofstd/ofstream.h \
643+
../../ofstd/include/dcmtk/ofstd/ofconsol.h \
644+
../../ofstd/include/dcmtk/ofstd/ofthread.h \
645+
../../ofstd/include/dcmtk/ofstd/offile.h \
646+
../../ofstd/include/dcmtk/ofstd/ofstd.h \
647+
../../ofstd/include/dcmtk/ofstd/ofcond.h \
648+
../../ofstd/include/dcmtk/ofstd/ofdiag.h \
649+
../../ofstd/include/dcmtk/ofstd/diag/push.def \
650+
../../ofstd/include/dcmtk/ofstd/diag/useafree.def \
651+
../../ofstd/include/dcmtk/ofstd/diag/pop.def \
652+
../../ofstd/include/dcmtk/ofstd/oflimits.h \
653+
../../ofstd/include/dcmtk/ofstd/oferror.h \
654+
../../ofstd/include/dcmtk/ofstd/ofexit.h \
655+
../../dcmdata/include/dcmtk/dcmdata/dcuid.h \
656+
../../dcmdata/include/dcmtk/dcmdata/dcdefine.h \
657+
../../oflog/include/dcmtk/oflog/oflog.h \
658+
../../oflog/include/dcmtk/oflog/logger.h \
659+
../../oflog/include/dcmtk/oflog/config.h \
660+
../../oflog/include/dcmtk/oflog/config/defines.h \
661+
../../oflog/include/dcmtk/oflog/helpers/threadcf.h \
662+
../../oflog/include/dcmtk/oflog/loglevel.h \
663+
../../ofstd/include/dcmtk/ofstd/ofvector.h \
664+
../../oflog/include/dcmtk/oflog/tstring.h \
665+
../../oflog/include/dcmtk/oflog/tchar.h \
666+
../../oflog/include/dcmtk/oflog/spi/apndatch.h \
667+
../../oflog/include/dcmtk/oflog/appender.h \
668+
../../ofstd/include/dcmtk/ofstd/ofmem.h \
669+
../../ofstd/include/dcmtk/ofstd/ofutil.h \
670+
../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \
671+
../../oflog/include/dcmtk/oflog/layout.h \
672+
../../oflog/include/dcmtk/oflog/streams.h \
673+
../../oflog/include/dcmtk/oflog/helpers/pointer.h \
674+
../../oflog/include/dcmtk/oflog/thread/syncprim.h \
675+
../../oflog/include/dcmtk/oflog/spi/filter.h \
676+
../../oflog/include/dcmtk/oflog/helpers/lockfile.h \
677+
../../oflog/include/dcmtk/oflog/spi/logfact.h \
678+
../../oflog/include/dcmtk/oflog/logmacro.h \
679+
../../oflog/include/dcmtk/oflog/helpers/snprintf.h \
680+
../../oflog/include/dcmtk/oflog/tracelog.h \
681+
../../dcmdata/include/dcmtk/dcmdata/dcdatset.h \
682+
../../dcmdata/include/dcmtk/dcmdata/dcitem.h \
683+
../../dcmdata/include/dcmtk/dcmdata/dctypes.h \
684+
../../dcmdata/include/dcmtk/dcmdata/dcobject.h \
685+
../../ofstd/include/dcmtk/ofstd/ofglobal.h \
686+
../../dcmdata/include/dcmtk/dcmdata/dcerror.h \
687+
../../dcmdata/include/dcmtk/dcmdata/dcxfer.h \
688+
../../dcmdata/include/dcmtk/dcmdata/dcvr.h \
689+
../../ofstd/include/dcmtk/ofstd/ofdeprec.h \
690+
../../dcmdata/include/dcmtk/dcmdata/dctag.h \
691+
../../dcmdata/include/dcmtk/dcmdata/dctagkey.h \
692+
../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \
693+
../../dcmdata/include/dcmtk/dcmdata/dcstack.h \
694+
../../dcmdata/include/dcmtk/dcmdata/dclist.h \
695+
../../dcmdata/include/dcmtk/dcmdata/dcpcache.h \
696+
../../dcmdata/include/dcmtk/dcmdata/dcdeftag.h \
697+
../include/dcmtk/dcmsr/dsrimgvl.h ../include/dcmtk/dcmsr/dsrtypes.h \
698+
../include/dcmtk/dcmsr/dsdefine.h \
699+
../../dcmdata/include/dcmtk/dcmdata/dcelem.h \
700+
../../dcmdata/include/dcmtk/dcmdata/dcsequen.h \
701+
../include/dcmtk/dcmsr/dsrcomvl.h ../include/dcmtk/dcmsr/dsrimgfr.h \
702+
../include/dcmtk/dcmsr/dsrtlist.h ../include/dcmtk/dcmsr/dsrimgse.h \
703+
../include/dcmtk/dcmsr/dsrimgtn.h ../include/dcmtk/dcmsr/dsrdoctn.h \
704+
../include/dcmtk/dcmsr/dsrtree.h ../include/dcmtk/dcmsr/dsrtncsr.h \
705+
../include/dcmtk/dcmsr/dsrposcn.h ../include/dcmtk/dcmsr/dsrtnant.h \
706+
../../ofstd/include/dcmtk/ofstd/ofstack.h \
707+
../include/dcmtk/dcmsr/dsrcodvl.h ../include/dcmtk/dcmsr/codes/dcm.h
629708
tsrlist.o: tsrlist.cc ../../config/include/dcmtk/config/osconfig.h \
630709
../../ofstd/include/dcmtk/ofstd/oftest.h \
631710
../../ofstd/include/dcmtk/ofstd/ofconapp.h \

dcmsr/tests/Makefile.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ LIBDIRS = -L$(top_srcdir)/libcmr -L$(top_srcdir)/libsrc -L$(ofstddir)/libsrc \
2727
LOCALLIBS = -lcmr -ldcmsr -ldcmimage -ldcmimgle -ldcmdata -loflog -lofstd -loficonv \
2828
$(TIFFLIBS) $(PNGLIBS) $(XMLLIBS) $(ZLIBLIBS) $(CHARCONVLIBS) $(MATHLIBS)
2929

30-
tstobjs = tests.o tsrtree.o tsrdoctr.o tsrdoc.o tsrcodvl.o tsrnumvl.o tsrtpl.o \
31-
tsrcmr.o tsrlist.o
30+
tstobjs = tests.o tsrtree.o tsrdoctr.o tsrdoc.o tsrcodvl.o tsrimgvl.o tsrnumvl.o \
31+
tsrtpl.o tsrcmr.o tsrlist.o
3232
objs = mkreport.o $(tstobjs)
3333
progs = mkreport tests
3434

dcmsr/tests/tests.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
*
3-
* Copyright (C) 2012-2022, OFFIS e.V.
3+
* Copyright (C) 2012-2025, OFFIS e.V.
44
* All rights reserved. See COPYRIGHT file for details.
55
*
66
* This software and supporting documentation were developed by
@@ -83,6 +83,8 @@ OFTEST_REGISTER(dcmsr_determineCodeValueType);
8383
OFTEST_REGISTER(dcmsr_writeCodeSequence);
8484
OFTEST_REGISTER(dcmsr_compareCodedEntry);
8585
OFTEST_REGISTER(dcmsr_useBasicCodedEntry);
86+
OFTEST_REGISTER(dcmsr_createMonochromeIconImage);
87+
OFTEST_REGISTER(dcmsr_createColorIconImage);
8688
OFTEST_REGISTER(dcmsr_setNumericMeasurementValue);
8789
OFTEST_REGISTER(dcmsr_emptyMeasurementValueSequence);
8890
OFTEST_REGISTER(dcmsr_setAndGetFloatingPointRepresentation);

dcmsr/tests/tsrimgvl.cc

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
*
3+
* Copyright (C) 2025, J. Riesmeier, Oldenburg, Germany
4+
* All rights reserved. See COPYRIGHT file for details.
5+
*
6+
* This software and supporting documentation are maintained 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+
* test program for class DSRImageReferenceValue
20+
*
21+
*/
22+
23+
24+
#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
25+
26+
#include "dcmtk/ofstd/oftest.h"
27+
28+
#include "dcmtk/dcmdata/dcuid.h"
29+
#include "dcmtk/dcmdata/dcdatset.h"
30+
#include "dcmtk/dcmdata/dcdeftag.h"
31+
#include "dcmtk/dcmdata/dctypes.h"
32+
33+
#include "dcmtk/dcmsr/dsrimgvl.h"
34+
#include "dcmtk/dcmsr/dsrimgtn.h"
35+
#include "dcmtk/dcmsr/codes/dcm.h"
36+
37+
38+
OFTEST(dcmsr_createMonochromeIconImage)
39+
{
40+
DSRImageReferenceValue imgValue(UID_CTImageStorage, "1.2.3.4.5.6.7.8.9.0.98");
41+
OFCHECK(!imgValue.hasIconImage());
42+
43+
/* create a monochrome image dataset */
44+
DcmDataset dataset;
45+
Uint8 pixelData[256];
46+
for (int i = 0; i < 256; i++)
47+
pixelData[i] = OFstatic_cast(Uint8, i);
48+
OFCHECK(dataset.putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2").good());
49+
OFCHECK(dataset.putAndInsertUint16(DCM_SamplesPerPixel, 1).good());
50+
OFCHECK(dataset.putAndInsertUint16(DCM_PixelRepresentation, 0).good());
51+
OFCHECK(dataset.putAndInsertUint16(DCM_Rows, 16).good());
52+
OFCHECK(dataset.putAndInsertUint16(DCM_Columns, 16).good());
53+
OFCHECK(dataset.putAndInsertUint16(DCM_BitsAllocated, 8).good());
54+
OFCHECK(dataset.putAndInsertUint16(DCM_BitsStored, 8).good());
55+
OFCHECK(dataset.putAndInsertUint16(DCM_HighBit, 7).good());
56+
OFCHECK(dataset.putAndInsertUint8Array(DCM_PixelData, pixelData, sizeof(pixelData)).good());
57+
58+
/* create an icon image with 64x64 pixels */
59+
OFCHECK(imgValue.createIconImage(&dataset, EXS_Unknown /*xfer*/, 0 /*frame*/, 64 /*width*/, 64 /*height*/).good());
60+
OFCHECK(imgValue.hasIconImage());
61+
62+
/* set value to image tree node and write to item */
63+
DcmItem item;
64+
DSRImageTreeNode imgNode(DSRTypes::RT_contains);
65+
OFCHECK(imgNode.setConceptName(CODE_DCM_BestInSet).good());
66+
OFCHECK(imgNode.setValue(imgValue).good());
67+
OFCHECK(imgNode.hasIconImage());
68+
OFCHECK(imgNode.write(item).good());
69+
/* output content of the item (in debug mode only) */
70+
if (DCM_dcmsrLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
71+
item.print(COUT, DCMTypes::PF_shortenLongTagValues /*flags*/, 2 /*level*/);
72+
73+
/* delete the icon image */
74+
imgNode.deleteIconImage();
75+
OFCHECK(!imgNode.hasIconImage());
76+
}
77+
78+
79+
OFTEST(dcmsr_createColorIconImage)
80+
{
81+
DSRImageReferenceValue imgValue(UID_CTImageStorage, "1.2.3.4.5.6.7.8.9.0.99");
82+
OFCHECK(!imgValue.hasIconImage());
83+
84+
/* create a color image dataset */
85+
DcmDataset dataset;
86+
Uint8 pixelData[256][3];
87+
for (int i = 0; i < 256; i++)
88+
{
89+
pixelData[i][0] = OFstatic_cast(Uint8, i);
90+
pixelData[i][1] = OFstatic_cast(Uint8, i);
91+
pixelData[i][2] = OFstatic_cast(Uint8, i);
92+
}
93+
OFCHECK(dataset.putAndInsertString(DCM_PhotometricInterpretation, "RGB").good());
94+
OFCHECK(dataset.putAndInsertUint16(DCM_SamplesPerPixel, 3).good());
95+
OFCHECK(dataset.putAndInsertUint16(DCM_PixelRepresentation, 0).good());
96+
OFCHECK(dataset.putAndInsertUint16(DCM_PlanarConfiguration, 0).good());
97+
OFCHECK(dataset.putAndInsertUint16(DCM_Rows, 16).good());
98+
OFCHECK(dataset.putAndInsertUint16(DCM_Columns, 16).good());
99+
OFCHECK(dataset.putAndInsertUint16(DCM_BitsAllocated, 8).good());
100+
OFCHECK(dataset.putAndInsertUint16(DCM_BitsStored, 8).good());
101+
OFCHECK(dataset.putAndInsertUint16(DCM_HighBit, 7).good());
102+
OFCHECK(dataset.putAndInsertUint8Array(DCM_PixelData, pixelData[0], sizeof(pixelData)).good());
103+
104+
/* create an icon image with 64x64 pixels */
105+
OFCHECK(imgValue.createIconImage(&dataset, EXS_Unknown /*xfer*/, 0 /*frame*/, 64 /*width*/, 64 /*height*/).good());
106+
OFCHECK(imgValue.hasIconImage());
107+
108+
/* set value to image tree node and write to item */
109+
DcmItem item;
110+
DSRImageTreeNode imgNode(DSRTypes::RT_hasProperties);
111+
OFCHECK(imgNode.setConceptName(CODE_DCM_BestIllustrationOfFinding).good());
112+
OFCHECK(imgNode.setValue(imgValue).good());
113+
OFCHECK(imgNode.hasIconImage());
114+
OFCHECK(imgNode.write(item).good());
115+
/* output content of the item (in debug mode only) */
116+
if (DCM_dcmsrLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
117+
item.print(COUT, DCMTypes::PF_shortenLongTagValues /*flags*/, 2 /*level*/);
118+
119+
/* delete the icon image */
120+
imgNode.deleteIconImage();
121+
OFCHECK(!imgNode.hasIconImage());
122+
}

0 commit comments

Comments
 (0)