1 /*
2 *
3 * Copyright (C) 2019-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: dcmect
15 *
16 * Author: Michael Onken
17 *
18 * Purpose: Tests for creating and loading Enhanced CT objects
19 *
20 */
21
22 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
23
24 #include "dcmtk/ofstd/ofmem.h"
25 #include "dcmtk/ofstd/ofstrutl.h"
26 #include "dcmtk/ofstd/oftempf.h"
27 #include "dcmtk/ofstd/oftest.h"
28
29 #include "dcmtk/dcmect/enhanced_ct.h"
30
31 #include "dcmtk/dcmfg/concatenationcreator.h"
32 #include "dcmtk/dcmfg/concatenationloader.h"
33 #include "dcmtk/dcmfg/fgctacquisitiondetails.h"
34 #include "dcmtk/dcmfg/fgctacquisitiontype.h"
35 #include "dcmtk/dcmfg/fgctadditionalxraysource.h"
36 #include "dcmtk/dcmfg/fgctexposure.h"
37 #include "dcmtk/dcmfg/fgctgeometry.h"
38 #include "dcmtk/dcmfg/fgctimageframetype.h"
39 #include "dcmtk/dcmfg/fgctposition.h"
40 #include "dcmtk/dcmfg/fgctreconstruction.h"
41 #include "dcmtk/dcmfg/fgcttabledynamics.h"
42 #include "dcmtk/dcmfg/fgctxraydetails.h"
43 #include "dcmtk/dcmfg/fgfracon.h"
44 #include "dcmtk/dcmfg/fgframeanatomy.h"
45 #include "dcmtk/dcmfg/fgframevoilut.h"
46 #include "dcmtk/dcmfg/fgirradiationeventid.h"
47 #include "dcmtk/dcmfg/fgpixeltransform.h"
48 #include "dcmtk/dcmfg/fgpixmsr.h"
49 #include "dcmtk/dcmfg/fgplanor.h"
50 #include "dcmtk/dcmfg/fgplanpo.h"
51 #include "dcmtk/dcmfg/fgrealworldvaluemapping.h"
52 #include "dcmtk/dcmfg/fgtemporalposition.h"
53
54 static OFLogger tRoundLogger = OFLog::getLogger("dcmtk.test.t_roundtrip");
55
56 // Do not change values below since
57 // a) The expected dataset (dump) is made for these values
58 // b) Total pixel data length in this test must not be bigger than
59 // 4 GB, otherwise the calls to writeDataset() will fail.
60 // The test dcmect/tests/t_huge_concat.cc allows for exercising
61 // "unlimited" pixel data size using writeConcatenation() on all
62 // occassions.
63
64 static const Uint16 NUM_ROWS = 2;
65 static const Uint16 NUM_COLS = 2;
66 static const Uint16 NUM_FRAMES = 2;
67 static const size_t NUM_PIXELS_PER_FRAME = NUM_COLS * NUM_ROWS;
68 static const size_t NUM_FRAMES_CONCAT = 1;
69
70 static OFString EXPECTED_DUMP;
71
72 static void prepareExpectedDump();
73 static EctEnhancedCT* create();
74 static void configureIOD(EctEnhancedCT* ct);
75 static void setGenericValues(EctEnhancedCT* ct);
76 static void addSharedFGs(EctEnhancedCT* ct);
77 static void addFrames(EctEnhancedCT* ct);
78 static void addDimensions(EctEnhancedCT* ct);
79 static OFString write(EctEnhancedCT* ct, DcmDataset& ds);
80 static void checkCreatedObject(const OFString& ds_dump);
81 static void writeAndCheckConcatenation(EctEnhancedCT* ct, OFList<OFFilename>& concats);
82 static void checkConcatenationInstance(size_t numInstance, EctEnhancedCT* srcInstance, DcmDataset* concatInstance);
83 static void loadAndCheckConcatenation(const OFList<OFFilename>& concats);
84
OFTEST(dcmect_roundtrip)85 OFTEST(dcmect_roundtrip)
86 {
87 /* make sure data dictionary is loaded */
88 if (!dcmDataDict.isDictionaryLoaded())
89 {
90 OFCHECK(dcmDataDict.isDictionaryLoaded());
91 return;
92 }
93
94 // Creation
95 EctEnhancedCT* ct = create();
96 configureIOD(ct);
97 setGenericValues(ct);
98 addSharedFGs(ct);
99 addFrames(ct);
100 addDimensions(ct);
101
102 // Write to dataset and compare its dump with expected result
103 DcmFileFormat dcmff;
104 DcmDataset* ds = dcmff.getDataset();
105 prepareExpectedDump();
106 OFString dset_dump = write(ct, *ds);
107 checkCreatedObject(dset_dump);
108
109 // Save to disk, and re-load to test import
110 OFTempFile tf;
111 OFString temp_fn = tf.getFilename();
112 OFCHECK(!temp_fn.empty());
113 OFCHECK(ct->saveFile(temp_fn.c_str(), EXS_LittleEndianExplicit).good());
114
115 // Read object from dataset into EctEnhancedCT object, write again to dataset and
116 // check whether daset before saving to file is identical to object after loading from
117 // disk and writing to dataset
118 delete ct;
119 ct = NULL;
120 ds->clear();
121 OFCHECK(EctEnhancedCT::loadFile(temp_fn, ct).good());
122 OFCHECK(ct != NULL);
123 if (ct)
124 {
125 dset_dump = write(ct, *ds);
126 checkCreatedObject(dset_dump);
127 }
128 OFList<OFFilename> concats;
129 if (ct)
130 {
131 writeAndCheckConcatenation(ct, concats);
132 delete ct;
133 }
134 OFCHECK(!concats.empty());
135 if (!concats.empty())
136 {
137 loadAndCheckConcatenation(concats);
138 }
139 OFListIterator(OFFilename) it = concats.begin();
140 while (it != concats.end())
141 {
142 OFStandard::deleteFile(*it);
143 it++;
144 }
145 }
146
create()147 static EctEnhancedCT* create()
148 {
149 IODEnhGeneralEquipmentModule::EquipmentInfo eq("Open Connections", "OC CT", "4711", "0.1");
150 EctEnhancedCT* ct = NULL;
151 OFCondition result;
152 result = EctEnhancedCT::create(ct,
153 NUM_ROWS,
154 NUM_COLS,
155 OFFalse,
156 EctTypes::E_ImageType1_Original,
157 EctTypes::DT_ImageType3_Volume,
158 EctTypes::DT_ImageType4_Maximum,
159 "1" /* instance number */,
160 EctTypes::E_ContQuali_Research,
161 EctTypes::E_PixelPres_Monochrome,
162 EctTypes::E_VolProps_Volume,
163 EctTypes::DT_VolBasedCalcTechnique_VolumeRender,
164 eq,
165 "20190801120000" /* acquisition date */,
166 2.0 /* acquisition duration */);
167
168 OFCHECK(result.good());
169 OFCHECK(ct != OFnullptr);
170 return ct;
171 }
172
configureIOD(EctEnhancedCT * ct)173 static void configureIOD(EctEnhancedCT* ct)
174 {
175 if (!ct)
176 return;
177 }
178
setGenericValues(EctEnhancedCT * ct)179 static void setGenericValues(EctEnhancedCT* ct)
180 {
181 if (!ct)
182 return;
183 OFCHECK(ct->getPatient().setPatientName("Bond^James").good());
184 OFCHECK(ct->getPatient().setPatientID("007").good());
185 OFCHECK(ct->getPatient().setPatientBirthDate("19771007").good());
186 OFCHECK(ct->getStudy().setStudyDate("20190801").good());
187 OFCHECK(ct->getStudy().setStudyTime("120000").good());
188 OFCHECK(ct->getStudy().setStudyID("1").good());
189 OFCHECK(ct->getPatientStudy().setPatientAge("040Y").good());
190 OFCHECK(ct->getSeries().setSeriesDescription("Test Description").good());
191 OFCHECK(ct->getSeries().setSeriesNumber("1").good());
192 OFCHECK(ct->getSeries().setPatientPosition("HFS").good());
193
194 // Those values are usually computed automatically. UIDS are generated and date/times are set to current values.
195 // But in order to compare the "old" dump with the freshly created image attributes, we set some values manually,
196 // so that they are not overwritten with new, automatically created values later.
197 OFCHECK(ct->getStudy().setStudyInstanceUID("1.2.276.0.7230010.3.1.2.8323329.14863.1565940357.864811").good());
198 OFCHECK(ct->getFrameOfReference().setFrameOfReferenceUID("2.25.30853397773651184949181049330553108086").good());
199 OFCHECK(ct->getSeries().setSeriesInstanceUID("1.2.276.0.7230010.3.1.3.8323329.14863.1565940357.864812").good());
200 OFCHECK(ct->getSOPCommon().setSOPInstanceUID("1.2.276.0.7230010.3.1.4.8323329.14863.1565940357.864813").good());
201
202 OFCHECK(ct->getIODMultiFrameFGModule().setContentTime("092557").good());
203 OFCHECK(ct->getIODMultiFrameFGModule().setContentDate("20190816").good());
204 }
205
addSharedFGs(EctEnhancedCT * ct)206 static void addSharedFGs(EctEnhancedCT* ct)
207 {
208 if (!ct)
209 return;
210
211 FGPixelMeasures meas;
212 OFCHECK(meas.setPixelSpacing("0.1\\0.1").good());
213 OFCHECK(meas.setSliceThickness("1.0").good());
214 OFCHECK(meas.setSpacingBetweenSlices("0.05").good());
215
216 FGPlanePosPatient planpo;
217 OFCHECK(planpo.setImagePositionPatient("0.0", "0.0", "0.0").good());
218
219 FGPlaneOrientationPatient planor;
220 OFCHECK(planor.setImageOrientationPatient("1.0", "0.0", "0.0", "0.0", "1.0", "0.0").good());
221
222 FGFrameAnatomy ana;
223 OFCHECK(ana.setLaterality(FGFrameAnatomy::LATERALITY_BOTH).good());
224 OFCHECK(ana.getAnatomy().getAnatomicRegion().set("12738006", "SCT", "Brain").good());
225
226 FGFrameVOILUT voi;
227 OFCHECK(voi.setCenterWidthExplanation(1000, 2000, FGFrameVOILUT::DT_CT_WindowCenterWidthExplanation_Brain).good());
228
229 FGIrradiationEventIdentification irr;
230 OFCHECK(irr.setIrradiationEventUID("2.25.30853892236613436472911970638347155062").good());
231
232 FGCTImageFrameType itype;
233 OFCHECK(itype.setFrameType("ORIGINAL\\PRIMARY\\VOLUME\\MAXIMUM").good());
234 OFCHECK(itype.setPixelPresentation(FGCTImageFrameType::E_PixelPres_Monochrome).good());
235 OFCHECK(itype.setVolumetricProperties(FGCTImageFrameType::E_VolProp_Volume).good());
236 OFCHECK(itype.setVolumeBasedCalculationTechnique(FGCTImageFrameType::DT_VolBasedCalcTechnique_VolumeRender).good());
237
238 FGCTAcquisitionType atype;
239 OFCHECK(atype.setAcquisitionType(FGCTAcquisitionType::DT_AcquisitionType_ConstantAngle).good());
240 OFCHECK(atype.setTubeAngle(0.1).good());
241 OFCHECK(atype.setConstantVolumeFlag(FGCTAcquisitionType::E_ConstVol_Yes).good());
242 OFCHECK(atype.setFluoroscopyFlag(FGCTAcquisitionType::E_Fluoroscopy_No).good());
243
244 FGCTAcquisitionDetails adetails;
245 FGCTAcquisitionDetails::FGCTAcquisitionDetailsItem* item = new FGCTAcquisitionDetails::FGCTAcquisitionDetailsItem();
246 OFCHECK(item->setRotationDirection(FGCTAcquisitionDetails::E_RotationDirection_CW).good());
247 OFCHECK(item->setRevolutionTime(5).good());
248 OFCHECK(item->setSingleCollimationWidth(1).good());
249 OFCHECK(item->setTotalCollimationWidth(10).good());
250 OFCHECK(item->setTableHeight(50).good());
251 OFCHECK(item->setGantryDetectorTilt(5).good());
252 OFCHECK(item->setDataCollectionDiameter(20).good());
253 adetails.getCTAcquisitionDetailsItems().push_back(item);
254
255 FGCTTableDynamics dyn;
256 FGCTTableDynamics::FGCTTableDynamicsItem* dyn_item = new FGCTTableDynamics::FGCTTableDynamicsItem;
257 OFCHECK(dyn_item);
258 if (dyn_item)
259 {
260 OFCHECK(dyn_item->setTableSpeed(1.0).good());
261 OFCHECK(dyn_item->setTableFeedPerRotation(0.1).good());
262 OFCHECK(dyn_item->setSpiralPitchFactor(0.2).good());
263 dyn.getCTTableDynamicsItems().push_back(dyn_item);
264 }
265
266 FGCTPosition pos;
267 OFCHECK(pos.setTablePosition(100.0).good());
268 OFCHECK(pos.setReconstructionTargetCenterPatient(OFVector<Float64>(3, 1.0)).good());
269 OFCHECK(pos.setDataCollectionCenterPatient(OFVector<Float64>(3, 2.0)).good());
270
271 FGCTGeometry geo;
272 FGCTGeometry::FGCTGeometryItem* geo_item = new FGCTGeometry::FGCTGeometryItem;
273 if (geo_item)
274 {
275 OFCHECK(geo_item->setDistanceSourceToDataCollectionCenter(5.0).good());
276 OFCHECK(geo_item->setDistanceSourceToDetector(0.5).good());
277 geo.getCTGeometryItems().push_back(geo_item);
278 }
279
280 FGCTReconstruction rec;
281 OFCHECK(rec.setConvolutionKernel("DUMMY").good());
282 OFCHECK(rec.setConvolutionKernelGroup("DUMMYGROUP").good());
283 OFCHECK(rec.setImageFilter("FILTER").good());
284 OFCHECK(rec.setReconstructionAlgorithm("ALGO").good());
285 OFCHECK(rec.setReconstructionAngle(90.0).good());
286 OFCHECK(rec.setReconstructionDiameter(100.0).good());
287 // Not permitted if Reconstruction Diameter is provided instead
288 // OFCHECK(rec.setReconstructionFieldOfView(100.0, 100.0).good());
289 OFCHECK(rec.setReconstructionPixelSpacing(0.1, 0.1).good());
290
291 FGCTExposure exp;
292 FGCTExposure::FGCTExposureItem* exp_item = new FGCTExposure::FGCTExposureItem;
293 if (exp_item)
294 {
295 OFCHECK(exp_item->setCTDIVol(0.1).good());
296 CodeSequenceMacro* phantom_item = new CodeSequenceMacro("113682", "DCM", "ACR Accreditation Phantom - CT");
297 exp_item->getCTDIPhantomTypeCodeSequence().push_back(phantom_item);
298 OFCHECK(exp_item->setEstimatedDoseSaving(0.2).good());
299 OFCHECK(exp_item->setExposureInMas(0.3).good());
300 OFCHECK(exp_item->setExposureModulationType("WEIRD").good());
301 OFCHECK(exp_item->setExposureTimeInMs(0.4).good());
302 OFCHECK(exp_item->setImageAndFluoroscopyAreaDoseProduct(0.5).good());
303 OFCHECK(exp_item->setWaterEquivalentDiameter(0.6).good());
304 CodeSequenceMacro* water_code = new CodeSequenceMacro("113987", "DCM", "AAPM 220");
305 exp_item->getWaterEquivalentDiameterCalculationMethodCodeSequence().push_back(water_code);
306 OFCHECK(exp_item->setXRayTubeCurrentInMa(0.7).good());
307 exp.getCTExposureItems().push_back(exp_item);
308 }
309
310 FGCTXRayDetails det;
311 FGCTXRayDetails::FGCTXRayDetailsItem* det_item = new FGCTXRayDetails::FGCTXRayDetailsItem;
312 if (det_item)
313 {
314 OFCHECK(det_item->setCalciumScoringMassFactorDevice(OFVector<Float32>(3, 1)).good());
315 OFCHECK(det_item->setCalciumScoringMassFactorPatient(2).good());
316 OFCHECK(det_item->setEnergyWeightingFactor(3).good());
317 OFCHECK(det_item->setFilterMaterial("FILTER_MATERIAL").good());
318 OFCHECK(det_item->setFilterType("FILTER_TYPE").good());
319 OFCHECK(det_item->setFocalSpots(OFVector<Float64>(4, 4.4)).good());
320 OFCHECK(det_item->setKVP(5.0).good());
321 det.getCTXRayDetailsItems().push_back(det_item);
322 }
323
324 FGPixelValueTransformation trans;
325 trans.setFGType(FGPixelValueTransformation::E_PixelValTrans_CT);
326 trans.setRescaleIntercept("0");
327 trans.setRescaleSlope("1");
328 trans.setRescaleType("HU");
329
330 FGCTAdditionalXRaySource asrc;
331 FGCTAdditionalXRaySource::FGCTAdditionalXRaySourceItem* asrc_item
332 = new FGCTAdditionalXRaySource::FGCTAdditionalXRaySourceItem;
333 if (asrc_item)
334 {
335 OFCHECK(asrc_item->setDataCollectionDiameter(1.0).good());
336 OFCHECK(asrc_item->setEnergyWeightingFactor(2.0).good());
337 OFCHECK(asrc_item->setExposureInmAs(3.0).good());
338 OFCHECK(asrc_item->setFilterMaterial("FILTER_MATERIAL").good());
339 OFCHECK(asrc_item->setFilterType("FILTER_TYPE").good());
340 OFCHECK(asrc_item->setFocalSpots(OFVector<Float64>(4, 4.4)).good());
341 OFCHECK(asrc_item->setKVP(5).good());
342 OFCHECK(asrc_item->setXRayTubeCurrentInmA(6).good());
343 asrc.getCTAdditionalXRaySourceItems().push_back(asrc_item);
344 }
345
346 FGTemporalPosition tempos;
347 OFCHECK(tempos.setTemporalPositionTimeOffset(1.0).good());
348
349 OFCHECK(ct->addForAllFrames(meas).good());
350 OFCHECK(ct->addForAllFrames(planpo).good());
351 OFCHECK(ct->addForAllFrames(planor).good());
352 OFCHECK(ct->addForAllFrames(ana).good());
353 OFCHECK(ct->addForAllFrames(voi).good());
354 OFCHECK(ct->addForAllFrames(irr).good());
355 OFCHECK(ct->addForAllFrames(itype).good());
356 OFCHECK(ct->addForAllFrames(atype).good());
357 OFCHECK(ct->addForAllFrames(adetails).good());
358 OFCHECK(ct->addForAllFrames(dyn).good());
359 OFCHECK(ct->addForAllFrames(pos).good());
360 OFCHECK(ct->addForAllFrames(geo).good());
361 OFCHECK(ct->addForAllFrames(rec).good());
362 OFCHECK(ct->addForAllFrames(exp).good());
363 OFCHECK(ct->addForAllFrames(det).good());
364 OFCHECK(ct->addForAllFrames(trans).good());
365 OFCHECK(ct->addForAllFrames(asrc).good());
366 OFCHECK(ct->addForAllFrames(tempos).good());
367 }
368
addFrames(EctEnhancedCT * ct)369 static void addFrames(EctEnhancedCT* ct)
370 {
371 if (!ct)
372 return;
373
374 FGFrameContent* fg = new FGFrameContent();
375 fg->setStackID("1");
376 OFCHECK(fg);
377 if (fg)
378 {
379 EctEnhancedCT::FramesType frames = ct->getFrames();
380 for (Uint16 frameNo = 1; frameNo <= NUM_FRAMES; frameNo++)
381 {
382 OFCHECK(fg->setFrameAcquisitionNumber(frameNo).good());
383 OFCHECK(fg->setFrameReferenceDateTime("20190816092557").good());
384 OFCHECK(fg->setFrameAcquisitionDateTime("20190816092557").good());
385 OFCHECK(fg->setFrameAcquisitionDuration(0.001).good());
386 OFCHECK(fg->setInStackPositionNumber(frameNo).good());
387 OFCHECK(fg->setDimensionIndexValues(1, 0).good());
388 OFCHECK(fg->setDimensionIndexValues(frameNo, 1).good());
389 OFVector<FGBase*> groups;
390 groups.push_back(fg);
391
392 Uint16* data = new Uint16[NUM_PIXELS_PER_FRAME];
393 for (size_t i = 0; i < NUM_PIXELS_PER_FRAME; ++i)
394 {
395 data[i] = frameNo;
396 }
397 OFCHECK(
398 OFget<EctEnhancedCT::Frames<Uint16> >(&frames)->addFrame(data, NUM_PIXELS_PER_FRAME, groups).good());
399 delete[] data;
400 }
401 }
402 delete fg;
403 }
404
addDimensions(EctEnhancedCT * ct)405 static void addDimensions(EctEnhancedCT* ct)
406 {
407 if (!ct)
408 return;
409 IODMultiframeDimensionModule& dims = ct->getDimensions();
410 OFCHECK(dims.addDimensionIndex(
411 DCM_StackID, "2.25.30855560781715986879861690673941231222", DCM_FrameContentSequence, "STACK_DIM")
412 .good());
413 OFCHECK(dims.addDimensionIndex(DCM_InStackPositionNumber,
414 "2.25.30855560781715986879861690673941231222",
415 DCM_FrameContentSequence,
416 "STACK_DIM")
417 .good());
418 OFunique_ptr<IODMultiframeDimensionModule::DimensionOrganizationItem> org(
419 new IODMultiframeDimensionModule::DimensionOrganizationItem);
420 if (org)
421 {
422 org->setDimensionOrganizationUID("2.25.30855560781715986879861690673941231222");
423 dims.getDimensionOrganizationSequence().push_back(org.release());
424 }
425 }
426
write(EctEnhancedCT * ct,DcmDataset & ds)427 static OFString write(EctEnhancedCT* ct, DcmDataset& ds)
428 {
429 OFCondition result = ct->writeDataset(ds);
430 OFCHECK(result.good());
431 if (result.bad())
432 {
433 OFLOG_ERROR(tRoundLogger, "Writing Enhanced CT dataset failed: " << result.text() << OFendl);
434 }
435 // Make dump and return it
436 OFStringStream sstream;
437 ds.print(sstream);
438 OFSTRINGSTREAM_GETOFSTRING(sstream, dump);
439 return dump;
440 }
441
writeAndCheckConcatenation(EctEnhancedCT * ct,OFList<OFFilename> & concats)442 static void writeAndCheckConcatenation(EctEnhancedCT* ct, OFList<OFFilename>& concats)
443 {
444 ConcatenationCreator cc;
445 cc.setCfgFramesPerInstance(NUM_FRAMES_CONCAT);
446 OFCHECK(ct->writeConcatenation(cc).good());
447 size_t numInstances = cc.getNumInstances();
448 OFCHECK(numInstances == NUM_FRAMES);
449 OFCondition result;
450 for (size_t n = 0; n < numInstances; n++)
451 {
452 OFStringStream s;
453 s << "concat_" << n << "_";
454 OFTempFile tf(O_RDWR, "", s.str().c_str(), ".dcm");
455 result = cc.writeNextInstance(tf.getFilename());
456 OFCHECK(result.good());
457 if (result.good())
458 {
459 DcmFileFormat concat;
460 OFCHECK(concat.loadFile(tf.getFilename()).good());
461 checkConcatenationInstance(n, ct, concat.getDataset());
462 concats.push_back(tf.getFilename());
463 tf.stealFile();
464 }
465 }
466 }
467
checkConcatenationInstance(size_t numInstance,EctEnhancedCT * srcInstance,DcmDataset * concatInstance)468 static void checkConcatenationInstance(size_t numInstance, EctEnhancedCT* srcInstance, DcmDataset* concatInstance)
469 {
470 EctEnhancedCT* concat = NULL;
471 OFCHECK(EctEnhancedCT::loadDataset(*concatInstance, concat).good());
472 if (concat)
473 {
474 size_t numFrames;
475 numFrames = concat->getNumberOfFrames();
476 OFCHECK(numFrames == 1);
477 IODMultiFrameFGModule::ConcatenationInfo& ci = concat->getConcatenationInfo();
478 OFString val;
479 OFCHECK(ci.getConcatenationUID(val).good());
480 OFCHECK(DcmUniqueIdentifier::checkStringValue(val, "1").good());
481 Uint32 frameOffsetNo = 0;
482 OFCHECK(ci.getConcatenationFrameOffsetNumber(frameOffsetNo).good());
483 OFCHECK(frameOffsetNo == numInstance);
484 Uint16 inConcatNo = 0;
485 OFCHECK(ci.getInConcatenationNumber(inConcatNo).good());
486 OFCHECK(inConcatNo == numInstance + 1);
487 Uint16 concatTotalNo = 0;
488 OFCHECK(ci.getInConcatenationTotalNumber(concatTotalNo).good());
489 OFCHECK(concatTotalNo == NUM_FRAMES);
490
491 OFString srcUID;
492 OFCHECK(ci.getSOPInstanceUIDOfConcatenationSource(srcUID).good());
493 OFCHECK(srcInstance->getSOPCommon().getSOPInstanceUID(val).good());
494 OFCHECK(srcUID == val);
495
496 OFCHECK(concat->getSOPCommon().getSOPInstanceUID(val).good());
497 OFCHECK(srcUID != val);
498
499 FunctionalGroups::const_iterator srcShared = srcInstance->getFunctionalGroups().getShared()->begin();
500 FunctionalGroups::const_iterator cShared = concat->getFunctionalGroups().getShared()->begin();
501 size_t numShared = 0;
502 do
503 {
504 OFCHECK(srcShared->second->compare(*cShared->second) == 0);
505 srcShared++;
506 cShared++;
507 numShared++;
508 } while ((srcShared != srcInstance->getFunctionalGroups().getShared()->end())
509 && (cShared != concat->getFunctionalGroups().getShared()->end()));
510 OFCHECK((srcShared == srcInstance->getFunctionalGroups().getShared()->end())
511 && (cShared == concat->getFunctionalGroups().getShared()->end()));
512 DcmSequenceOfItems* cPerFrame = NULL;
513 OFCHECK(concatInstance->findAndGetSequence(DCM_PerFrameFunctionalGroupsSequence, cPerFrame).good());
514
515 if (cPerFrame)
516 {
517 OFCHECK(cPerFrame->card() == 1);
518 }
519
520 OFBool perFrame = OFFalse;
521 FGBase* fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTACQUISITIONDETAILS, perFrame);
522 OFCHECK(fg != NULL);
523 OFCHECK(perFrame == OFFalse);
524
525 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTACQUISITIONTYPE, perFrame);
526 OFCHECK(fg != NULL);
527 OFCHECK(perFrame == OFFalse);
528
529 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTADDITIONALXRAYSOURCE, perFrame);
530 OFCHECK(fg != NULL);
531 OFCHECK(perFrame == OFFalse);
532
533 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTEXPOSURE, perFrame);
534 OFCHECK(fg != NULL);
535 OFCHECK(perFrame == OFFalse);
536
537 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTGEOMETRY, perFrame);
538 OFCHECK(fg != NULL);
539 OFCHECK(perFrame == OFFalse);
540
541 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTIMAGEFRAMETYPE, perFrame);
542 OFCHECK(fg != NULL);
543 OFCHECK(perFrame == OFFalse);
544
545 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTPOSITION, perFrame);
546 OFCHECK(fg != NULL);
547 OFCHECK(perFrame == OFFalse);
548
549 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTRECONSTRUCTION, perFrame);
550 OFCHECK(fg != NULL);
551 OFCHECK(perFrame == OFFalse);
552
553 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTTABLEDYNAMICS, perFrame);
554 OFCHECK(fg != NULL);
555 OFCHECK(perFrame == OFFalse);
556
557 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTXRAYDETAILS, perFrame);
558 OFCHECK(fg != NULL);
559 OFCHECK(perFrame == OFFalse);
560
561 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_CTXRAYDETAILS, perFrame);
562 OFCHECK(fg != NULL);
563 OFCHECK(perFrame == OFFalse);
564
565 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_FRAMECONTENT, perFrame);
566 OFCHECK(fg != NULL);
567 OFCHECK(perFrame == OFTrue);
568
569 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_FRAMEANATOMY, perFrame);
570 OFCHECK(fg != NULL);
571 OFCHECK(perFrame == OFFalse);
572
573 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_FRAMEANATOMY, perFrame);
574 OFCHECK(fg != NULL);
575 OFCHECK(perFrame == OFFalse);
576
577 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_FRAMEVOILUTMETA, perFrame);
578 OFCHECK(fg != NULL);
579 OFCHECK(perFrame == OFFalse);
580
581 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_FRAMEANATOMY, perFrame);
582 OFCHECK(fg != NULL);
583 OFCHECK(perFrame == OFFalse);
584
585 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_IRRADIATIONEVENTIDENT, perFrame);
586 OFCHECK(fg != NULL);
587 OFCHECK(perFrame == OFFalse);
588
589 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_PIXELVALUETRANSMETA, perFrame);
590 OFCHECK(fg != NULL);
591 OFCHECK(perFrame == OFFalse);
592
593 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_IRRADIATIONEVENTIDENT, perFrame);
594 OFCHECK(fg != NULL);
595 OFCHECK(perFrame == OFFalse);
596
597 fg = NULL;
598 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_PIXELMEASURES, perFrame);
599 OFCHECK(fg != NULL);
600 OFCHECK(perFrame == OFFalse);
601
602 fg = NULL;
603 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_PLANEPOSPATIENT, perFrame);
604 OFCHECK(fg != NULL);
605 OFCHECK(perFrame == OFFalse);
606
607 fg = NULL;
608 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_PLANEORIENTPATIENT, perFrame);
609 OFCHECK(fg != NULL);
610 OFCHECK(perFrame == OFFalse);
611
612 fg = concat->getFunctionalGroups().get(0, DcmFGTypes::EFG_TEMPORALPOSITION, perFrame);
613 OFCHECK(fg != NULL);
614 OFCHECK(perFrame == OFFalse);
615
616 EctEnhancedCT::FramesType frames = concat->getFrames();
617 Uint16* frame = OFget<EctEnhancedCT::Frames<Uint16> >(&frames)->getFrame(0);
618 OFCHECK(frame != OFnullptr);
619 // Check that all pixels are set to their original source instances frame number (starting from 1)
620 for (size_t pix = 0; pix < NUM_PIXELS_PER_FRAME; pix++)
621 {
622 OFCHECK(frame[pix] == numInstance + 1);
623 }
624 delete concat;
625 }
626 }
627
loadAndCheckConcatenation(const OFList<OFFilename> & concats)628 void loadAndCheckConcatenation(const OFList<OFFilename>& concats)
629 {
630 ConcatenationLoader cl;
631 OFCondition result = cl.scan(concats);
632 OFCHECK(result.good());
633 if (result.good())
634 {
635 DcmDataset merged;
636 EctEnhancedCT* mergedCT = NULL;
637 result = EctEnhancedCT::loadConcatenation(cl, cl.getInfo().begin()->first, mergedCT);
638 if (result.good())
639 {
640 OFStringStream s;
641 DcmDataset d;
642 result = mergedCT->writeDataset(d);
643 OFCHECK(result.good());
644 if (result.good())
645 {
646 // patch in old timestamp to match dump
647 OFCHECK(d.putAndInsertOFStringArray(DCM_ContentDate, "20190816").good());
648 OFCHECK(d.putAndInsertOFStringArray(DCM_ContentTime, "092557").good());
649 d.print(s);
650 checkCreatedObject(s.str().c_str());
651 delete mergedCT;
652 }
653 }
654 }
655 }
656
prepareExpectedDump()657 static void prepareExpectedDump()
658 {
659 EXPECTED_DUMP += "\n";
660 EXPECTED_DUMP += "# Dicom-Data-Set\n";
661 EXPECTED_DUMP += "# Used TransferSyntax: Little Endian Explicit\n";
662 EXPECTED_DUMP += "(0008,0008) CS [ORIGINAL\\PRIMARY\\VOLUME\\MAXIMUM] # 32, 4 ImageType\n";
663 EXPECTED_DUMP += "(0008,0016) UI =EnhancedCTImageStorage # 28, 1 SOPClassUID\n";
664 EXPECTED_DUMP
665 += "(0008,0018) UI [1.2.276.0.7230010.3.1.4.8323329.14863.1565940357.864813] # 56, 1 SOPInstanceUID\n";
666 EXPECTED_DUMP += "(0008,0020) DA [20190801] # 8, 1 StudyDate\n";
667 EXPECTED_DUMP += "(0008,0023) DA [20190816] # 8, 1 ContentDate\n";
668 EXPECTED_DUMP += "(0008,002a) DT [20190801120000] # 14, 1 AcquisitionDateTime\n";
669 EXPECTED_DUMP += "(0008,0030) TM [120000] # 6, 1 StudyTime\n";
670 EXPECTED_DUMP += "(0008,0033) TM [092557] # 6, 1 ContentTime\n";
671 EXPECTED_DUMP += "(0008,0050) SH (no value available) # 0, 0 AccessionNumber\n";
672 EXPECTED_DUMP += "(0008,0060) CS [CT] # 2, 1 Modality\n";
673 EXPECTED_DUMP += "(0008,0070) LO [Open Connections] # 16, 1 Manufacturer\n";
674 EXPECTED_DUMP += "(0008,0090) PN (no value available) # 0, 0 ReferringPhysicianName\n";
675 EXPECTED_DUMP += "(0008,103e) LO [Test Description] # 16, 1 SeriesDescription\n";
676 EXPECTED_DUMP += "(0008,1090) LO [OC CT] # 6, 1 ManufacturerModelName\n";
677 EXPECTED_DUMP += "(0008,9205) CS [MONOCHROME] # 10, 1 PixelPresentation\n";
678 EXPECTED_DUMP += "(0008,9206) CS [VOLUME] # 6, 1 VolumetricProperties\n";
679 EXPECTED_DUMP
680 += "(0008,9207) CS [VOLUME_RENDER] # 14, 1 VolumeBasedCalculationTechnique\n";
681 EXPECTED_DUMP += "(0010,0010) PN [Bond^James] # 10, 1 PatientName\n";
682 EXPECTED_DUMP += "(0010,0020) LO [007] # 4, 1 PatientID\n";
683 EXPECTED_DUMP += "(0010,0030) DA [19771007] # 8, 1 PatientBirthDate\n";
684 EXPECTED_DUMP += "(0010,0040) CS (no value available) # 0, 0 PatientSex\n";
685 EXPECTED_DUMP += "(0010,1010) AS [040Y] # 4, 1 PatientAge\n";
686 EXPECTED_DUMP += "(0018,1000) LO [4711] # 4, 1 DeviceSerialNumber\n";
687 EXPECTED_DUMP += "(0018,1020) LO [0.1] # 4, 1 SoftwareVersions\n";
688 EXPECTED_DUMP += "(0018,5100) CS [HFS] # 4, 1 PatientPosition\n";
689 EXPECTED_DUMP += "(0018,9004) CS [RESEARCH] # 8, 1 ContentQualification\n";
690 EXPECTED_DUMP += "(0018,9073) FD 2 # 8, 1 AcquisitionDuration\n";
691 EXPECTED_DUMP
692 += "(0020,000d) UI [1.2.276.0.7230010.3.1.2.8323329.14863.1565940357.864811] # 56, 1 StudyInstanceUID\n";
693 EXPECTED_DUMP
694 += "(0020,000e) UI [1.2.276.0.7230010.3.1.3.8323329.14863.1565940357.864812] # 56, 1 SeriesInstanceUID\n";
695 EXPECTED_DUMP += "(0020,0010) SH [1] # 2, 1 StudyID\n";
696 EXPECTED_DUMP += "(0020,0011) IS [1] # 2, 1 SeriesNumber\n";
697 EXPECTED_DUMP += "(0020,0013) IS [1] # 2, 1 InstanceNumber\n";
698 EXPECTED_DUMP += "(0020,0052) UI [2.25.30853397773651184949181049330553108086] # 44, 1 FrameOfReferenceUID\n";
699 EXPECTED_DUMP += "(0020,1040) LO (no value available) # 0, 0 PositionReferenceIndicator\n";
700 EXPECTED_DUMP += "(0020,9221) SQ (Sequence with explicit length #=1) # 0, 1 DimensionOrganizationSequence\n";
701 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
702 EXPECTED_DUMP
703 += " (0020,9164) UI [2.25.30855560781715986879861690673941231222] # 44, 1 DimensionOrganizationUID\n";
704 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
705 EXPECTED_DUMP += "(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
706 EXPECTED_DUMP += "(0020,9222) SQ (Sequence with explicit length #=2) # 0, 1 DimensionIndexSequence\n";
707 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=4) # 0, 1 Item\n";
708 EXPECTED_DUMP
709 += " (0020,9164) UI [2.25.30855560781715986879861690673941231222] # 44, 1 DimensionOrganizationUID\n";
710 EXPECTED_DUMP += " (0020,9165) AT (0020,9056) # 4, 1 DimensionIndexPointer\n";
711 EXPECTED_DUMP += " (0020,9167) AT (0020,9111) # 4, 1 FunctionalGroupPointer\n";
712 EXPECTED_DUMP += " (0020,9421) LO [STACK_DIM] # 10, 1 DimensionDescriptionLabel\n";
713 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
714 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=4) # 0, 1 Item\n";
715 EXPECTED_DUMP
716 += " (0020,9164) UI [2.25.30855560781715986879861690673941231222] # 44, 1 DimensionOrganizationUID\n";
717 EXPECTED_DUMP += " (0020,9165) AT (0020,9057) # 4, 1 DimensionIndexPointer\n";
718 EXPECTED_DUMP += " (0020,9167) AT (0020,9111) # 4, 1 FunctionalGroupPointer\n";
719 EXPECTED_DUMP += " (0020,9421) LO [STACK_DIM] # 10, 1 DimensionDescriptionLabel\n";
720 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
721 EXPECTED_DUMP += "(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
722 EXPECTED_DUMP += "(0028,0002) US 1 # 2, 1 SamplesPerPixel\n";
723 EXPECTED_DUMP += "(0028,0004) CS [MONOCHROME2] # 12, 1 PhotometricInterpretation\n";
724 EXPECTED_DUMP += "(0028,0008) IS [2] # 2, 1 NumberOfFrames\n";
725 EXPECTED_DUMP += "(0028,0010) US 2 # 2, 1 Rows\n";
726 EXPECTED_DUMP += "(0028,0011) US 2 # 2, 1 Columns\n";
727 EXPECTED_DUMP += "(0028,0100) US 16 # 2, 1 BitsAllocated\n";
728 EXPECTED_DUMP += "(0028,0101) US 16 # 2, 1 BitsStored\n";
729 EXPECTED_DUMP += "(0028,0102) US 15 # 2, 1 HighBit\n";
730 EXPECTED_DUMP += "(0028,0103) US 0 # 2, 1 PixelRepresentation\n";
731 EXPECTED_DUMP += "(0028,0301) CS [NO] # 2, 1 BurnedInAnnotation\n";
732 EXPECTED_DUMP += "(0028,2110) CS [00] # 2, 1 LossyImageCompression\n";
733 EXPECTED_DUMP += "(0040,0555) SQ (Sequence with explicit length #=0) # 0, 1 AcquisitionContextSequence\n";
734 EXPECTED_DUMP += "(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
735 EXPECTED_DUMP += "(2050,0020) CS [IDENTITY] # 8, 1 PresentationLUTShape\n";
736 EXPECTED_DUMP
737 += "(5200,9229) SQ (Sequence with explicit length #=1) # 0, 1 SharedFunctionalGroupsSequence\n";
738 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=18) # 0, 1 Item\n";
739 EXPECTED_DUMP += " (0018,9301) SQ (Sequence with explicit length #=1) # 0, 1 CTAcquisitionTypeSequence\n";
740 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=4) # 0, 1 Item\n";
741 EXPECTED_DUMP += " (0018,9302) CS [CONSTANT_ANGLE] # 14, 1 AcquisitionType\n";
742 EXPECTED_DUMP += " (0018,9303) FD 0.1 # 8, 1 TubeAngle\n";
743 EXPECTED_DUMP += " (0018,9333) CS [YES] # 4, 1 ConstantVolumeFlag\n";
744 EXPECTED_DUMP += " (0018,9334) CS [NO] # 2, 1 FluoroscopyFlag\n";
745 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
746 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
747 EXPECTED_DUMP
748 += " (0018,9304) SQ (Sequence with explicit length #=1) # 0, 1 CTAcquisitionDetailsSequence\n";
749 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=7) # 0, 1 Item\n";
750 EXPECTED_DUMP
751 += " (0018,0090) DS [20] # 2, 1 DataCollectionDiameter\n";
752 EXPECTED_DUMP += " (0018,1120) DS [5] # 2, 1 GantryDetectorTilt\n";
753 EXPECTED_DUMP += " (0018,1130) DS [50] # 2, 1 TableHeight\n";
754 EXPECTED_DUMP += " (0018,1140) CS [CW] # 2, 1 RotationDirection\n";
755 EXPECTED_DUMP += " (0018,9305) FD 5 # 8, 1 RevolutionTime\n";
756 EXPECTED_DUMP
757 += " (0018,9306) FD 1 # 8, 1 SingleCollimationWidth\n";
758 EXPECTED_DUMP += " (0018,9307) FD 10 # 8, 1 TotalCollimationWidth\n";
759 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
760 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
761 EXPECTED_DUMP += " (0018,9308) SQ (Sequence with explicit length #=1) # 0, 1 CTTableDynamicsSequence\n";
762 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
763 EXPECTED_DUMP += " (0018,9309) FD 1 # 8, 1 TableSpeed\n";
764 EXPECTED_DUMP += " (0018,9310) FD 0.1 # 8, 1 TableFeedPerRotation\n";
765 EXPECTED_DUMP += " (0018,9311) FD 0.2 # 8, 1 SpiralPitchFactor\n";
766 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
767 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
768 EXPECTED_DUMP += " (0018,9312) SQ (Sequence with explicit length #=1) # 0, 1 CTGeometrySequence\n";
769 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=2) # 0, 1 Item\n";
770 EXPECTED_DUMP
771 += " (0018,1110) DS [0.5] # 4, 1 DistanceSourceToDetector\n";
772 EXPECTED_DUMP += " (0018,9335) FD 5 # 8, 1 "
773 "DistanceSourceToDataCollectionCenter\n";
774 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
775 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
776 EXPECTED_DUMP += " (0018,9314) SQ (Sequence with explicit length #=1) # 0, 1 CTReconstructionSequence\n";
777 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=7) # 0, 1 Item\n";
778 EXPECTED_DUMP
779 += " (0018,1100) DS [100] # 4, 1 ReconstructionDiameter\n";
780 EXPECTED_DUMP += " (0018,1210) SH [DUMMY] # 6, 1 ConvolutionKernel\n";
781 EXPECTED_DUMP
782 += " (0018,9315) CS [ALGO] # 4, 1 ReconstructionAlgorithm\n";
783 EXPECTED_DUMP
784 += " (0018,9316) CS [DUMMYGROUP] # 10, 1 ConvolutionKernelGroup\n";
785 EXPECTED_DUMP += " (0018,9319) FD 90 # 8, 1 ReconstructionAngle\n";
786 EXPECTED_DUMP += " (0018,9320) SH [FILTER] # 6, 1 ImageFilter\n";
787 EXPECTED_DUMP
788 += " (0018,9322) FD 0.1\\0.1 # 16, 2 ReconstructionPixelSpacing\n";
789 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
790 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
791 EXPECTED_DUMP += " (0018,9321) SQ (Sequence with explicit length #=1) # 0, 1 CTExposureSequence\n";
792 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=10) # 0, 1 Item\n";
793 EXPECTED_DUMP += " (0018,115e) DS [0.5] # 4, 1 "
794 "ImageAndFluoroscopyAreaDoseProduct\n";
795 EXPECTED_DUMP
796 += " (0018,1271) FD 0.6 # 8, 1 WaterEquivalentDiameter\n";
797 EXPECTED_DUMP += " (0018,1272) SQ (Sequence with explicit length #=1) # 0, 1 "
798 "WaterEquivalentDiameterCalculationMethodCodeSequence\n";
799 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
800 EXPECTED_DUMP += " (0008,0100) SH [113987] # 6, 1 CodeValue\n";
801 EXPECTED_DUMP
802 += " (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator\n";
803 EXPECTED_DUMP += " (0008,0104) LO [AAPM 220] # 8, 1 CodeMeaning\n";
804 EXPECTED_DUMP
805 += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
806 EXPECTED_DUMP
807 += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
808 EXPECTED_DUMP
809 += " (0018,9323) CS [WEIRD] # 6, 1 ExposureModulationType\n";
810 EXPECTED_DUMP += " (0018,9324) FD 0.2 # 8, 1 EstimatedDoseSaving\n";
811 EXPECTED_DUMP += " (0018,9328) FD 0.4 # 8, 1 ExposureTimeInms\n";
812 EXPECTED_DUMP += " (0018,9330) FD 0.7 # 8, 1 XRayTubeCurrentInmA\n";
813 EXPECTED_DUMP += " (0018,9332) FD 0.3 # 8, 1 ExposureInmAs\n";
814 EXPECTED_DUMP += " (0018,9345) FD 0.1 # 8, 1 CTDIvol\n";
815 EXPECTED_DUMP
816 += " (0018,9346) SQ (Sequence with explicit length #=1) # 0, 1 CTDIPhantomTypeCodeSequence\n";
817 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
818 EXPECTED_DUMP += " (0008,0100) SH [113682] # 6, 1 CodeValue\n";
819 EXPECTED_DUMP
820 += " (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator\n";
821 EXPECTED_DUMP += " (0008,0104) LO [ACR Accreditation Phantom - CT] # 30, 1 CodeMeaning\n";
822 EXPECTED_DUMP
823 += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
824 EXPECTED_DUMP
825 += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
826 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
827 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
828 EXPECTED_DUMP += " (0018,9325) SQ (Sequence with explicit length #=1) # 0, 1 CTXRayDetailsSequence\n";
829 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=7) # 0, 1 Item\n";
830 EXPECTED_DUMP += " (0018,0060) DS [5] # 2, 1 KVP\n";
831 EXPECTED_DUMP += " (0018,1160) SH [FILTER_TYPE] # 12, 1 FilterType\n";
832 EXPECTED_DUMP += " (0018,1190) DS [4.4\\4.4\\4.4\\4.4] # 16, 4 FocalSpots\n";
833 EXPECTED_DUMP += " (0018,7050) CS [FILTER_MATERIAL] # 16, 1 FilterMaterial\n";
834 EXPECTED_DUMP
835 += " (0018,9351) FL 2 # 4, 1 CalciumScoringMassFactorPatient\n";
836 EXPECTED_DUMP += " (0018,9352) FL 1\\1\\1 # 12, 3 "
837 "CalciumScoringMassFactorDevice\n";
838 EXPECTED_DUMP += " (0018,9353) FL 3 # 4, 1 EnergyWeightingFactor\n";
839 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
840 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
841 EXPECTED_DUMP += " (0018,9326) SQ (Sequence with explicit length #=1) # 0, 1 CTPositionSequence\n";
842 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
843 EXPECTED_DUMP
844 += " (0018,9313) FD 2\\2\\2 # 24, 3 DataCollectionCenterPatient\n";
845 EXPECTED_DUMP += " (0018,9318) FD 1\\1\\1 # 24, 3 "
846 "ReconstructionTargetCenterPatient\n";
847 EXPECTED_DUMP += " (0018,9327) FD 100 # 8, 1 TablePosition\n";
848 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
849 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
850 EXPECTED_DUMP += " (0018,9329) SQ (Sequence with explicit length #=1) # 0, 1 CTImageFrameTypeSequence\n";
851 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=4) # 0, 1 Item\n";
852 EXPECTED_DUMP += " (0008,9007) CS [ORIGINAL\\PRIMARY\\VOLUME\\MAXIMUM] # 32, 4 FrameType\n";
853 EXPECTED_DUMP += " (0008,9205) CS [MONOCHROME] # 10, 1 PixelPresentation\n";
854 EXPECTED_DUMP += " (0008,9206) CS [VOLUME] # 6, 1 VolumetricProperties\n";
855 EXPECTED_DUMP
856 += " (0008,9207) CS [VOLUME_RENDER] # 14, 1 VolumeBasedCalculationTechnique\n";
857 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
858 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
859 EXPECTED_DUMP
860 += " (0018,9360) SQ (Sequence with explicit length #=1) # 0, 1 CTAdditionalXRaySourceSequence\n";
861 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=8) # 0, 1 Item\n";
862 EXPECTED_DUMP += " (0018,0060) DS [5] # 2, 1 KVP\n";
863 EXPECTED_DUMP
864 += " (0018,0090) DS [1] # 2, 1 DataCollectionDiameter\n";
865 EXPECTED_DUMP += " (0018,1160) SH [FILTER_TYPE] # 12, 1 FilterType\n";
866 EXPECTED_DUMP += " (0018,1190) DS [4.4\\4.4\\4.4\\4.4] # 16, 4 FocalSpots\n";
867 EXPECTED_DUMP += " (0018,7050) CS [FILTER_MATERIAL] # 16, 1 FilterMaterial\n";
868 EXPECTED_DUMP += " (0018,9330) FD 6 # 8, 1 XRayTubeCurrentInmA\n";
869 EXPECTED_DUMP += " (0018,9332) FD 3 # 8, 1 ExposureInmAs\n";
870 EXPECTED_DUMP += " (0018,9353) FL 2 # 4, 1 EnergyWeightingFactor\n";
871 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
872 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
873 EXPECTED_DUMP += " (0018,9477) SQ (Sequence with explicit length #=1) # 0, 1 "
874 "IrradiationEventIdentificationSequence\n";
875 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
876 EXPECTED_DUMP
877 += " (0008,3010) UI [2.25.30853892236613436472911970638347155062] # 44, 1 IrradiationEventUID\n";
878 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
879 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
880 EXPECTED_DUMP += " (0020,9071) SQ (Sequence with explicit length #=1) # 0, 1 FrameAnatomySequence\n";
881 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=2) # 0, 1 Item\n";
882 EXPECTED_DUMP
883 += " (0008,2218) SQ (Sequence with explicit length #=1) # 0, 1 AnatomicRegionSequence\n";
884 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
885 EXPECTED_DUMP += " (0008,0100) SH [12738006] # 8, 1 CodeValue\n";
886 EXPECTED_DUMP
887 += " (0008,0102) SH [SCT] # 4, 1 CodingSchemeDesignator\n";
888 EXPECTED_DUMP += " (0008,0104) LO [Brain] # 6, 1 CodeMeaning\n";
889 EXPECTED_DUMP
890 += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
891 EXPECTED_DUMP
892 += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
893 EXPECTED_DUMP += " (0020,9072) CS [B] # 2, 1 FrameLaterality\n";
894 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
895 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
896 EXPECTED_DUMP += " (0020,9113) SQ (Sequence with explicit length #=1) # 0, 1 PlanePositionSequence\n";
897 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
898 EXPECTED_DUMP
899 += " (0020,0032) DS [0.0\\0.0\\0.0] # 12, 3 ImagePositionPatient\n";
900 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
901 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
902 EXPECTED_DUMP += " (0020,9116) SQ (Sequence with explicit length #=1) # 0, 1 PlaneOrientationSequence\n";
903 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
904 EXPECTED_DUMP
905 += " (0020,0037) DS [1.0\\0.0\\0.0\\0.0\\1.0\\0.0] # 24, 6 ImageOrientationPatient\n";
906 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
907 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
908 EXPECTED_DUMP += " (0020,9310) SQ (Sequence with explicit length #=1) # 0, 1 TemporalPositionSequence\n";
909 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
910 EXPECTED_DUMP
911 += " (0020,930d) FD 1 # 8, 1 TemporalPositionTimeOffset\n";
912 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
913 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
914 EXPECTED_DUMP += " (0028,9110) SQ (Sequence with explicit length #=1) # 0, 1 PixelMeasuresSequence\n";
915 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
916 EXPECTED_DUMP += " (0018,0050) DS [1.0] # 4, 1 SliceThickness\n";
917 EXPECTED_DUMP += " (0018,0088) DS [0.05] # 4, 1 SpacingBetweenSlices\n";
918 EXPECTED_DUMP += " (0028,0030) DS [0.1\\0.1] # 8, 2 PixelSpacing\n";
919 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
920 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
921 EXPECTED_DUMP += " (0028,9132) SQ (Sequence with explicit length #=1) # 0, 1 FrameVOILUTSequence\n";
922 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
923 EXPECTED_DUMP += " (0028,1050) DS [1000] # 4, 1 WindowCenter\n";
924 EXPECTED_DUMP += " (0028,1051) DS [2000] # 4, 1 WindowWidth\n";
925 EXPECTED_DUMP
926 += " (0028,1055) LO [BRAIN] # 6, 1 WindowCenterWidthExplanation\n";
927 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
928 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
929 EXPECTED_DUMP
930 += " (0028,9145) SQ (Sequence with explicit length #=1) # 0, 1 PixelValueTransformationSequence\n";
931 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=3) # 0, 1 Item\n";
932 EXPECTED_DUMP += " (0028,1052) DS [0] # 2, 1 RescaleIntercept\n";
933 EXPECTED_DUMP += " (0028,1053) DS [1] # 2, 1 RescaleSlope\n";
934 EXPECTED_DUMP += " (0028,1054) LO [HU] # 2, 1 RescaleType\n";
935 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
936 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
937 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
938 EXPECTED_DUMP += "(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
939 EXPECTED_DUMP
940 += "(5200,9230) SQ (Sequence with explicit length #=2) # 0, 1 PerFrameFunctionalGroupsSequence\n";
941 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
942 EXPECTED_DUMP += " (0020,9111) SQ (Sequence with explicit length #=1) # 0, 1 FrameContentSequence\n";
943 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=7) # 0, 1 Item\n";
944 EXPECTED_DUMP
945 += " (0018,9074) DT [20190816092557] # 14, 1 FrameAcquisitionDateTime\n";
946 EXPECTED_DUMP
947 += " (0018,9151) DT [20190816092557] # 14, 1 FrameReferenceDateTime\n";
948 EXPECTED_DUMP
949 += " (0018,9220) FD 0.001 # 8, 1 FrameAcquisitionDuration\n";
950 EXPECTED_DUMP += " (0020,9056) SH [1] # 2, 1 StackID\n";
951 EXPECTED_DUMP += " (0020,9057) UL 1 # 4, 1 InStackPositionNumber\n";
952 EXPECTED_DUMP
953 += " (0020,9156) US 1 # 2, 1 FrameAcquisitionNumber\n";
954 EXPECTED_DUMP += " (0020,9157) UL 1\\1 # 8, 2 DimensionIndexValues\n";
955 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
956 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
957 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
958 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=1) # 0, 1 Item\n";
959 EXPECTED_DUMP += " (0020,9111) SQ (Sequence with explicit length #=1) # 0, 1 FrameContentSequence\n";
960 EXPECTED_DUMP += " (fffe,e000) na (Item with explicit length #=7) # 0, 1 Item\n";
961 EXPECTED_DUMP
962 += " (0018,9074) DT [20190816092557] # 14, 1 FrameAcquisitionDateTime\n";
963 EXPECTED_DUMP
964 += " (0018,9151) DT [20190816092557] # 14, 1 FrameReferenceDateTime\n";
965 EXPECTED_DUMP
966 += " (0018,9220) FD 0.001 # 8, 1 FrameAcquisitionDuration\n";
967 EXPECTED_DUMP += " (0020,9056) SH [1] # 2, 1 StackID\n";
968 EXPECTED_DUMP += " (0020,9057) UL 2 # 4, 1 InStackPositionNumber\n";
969 EXPECTED_DUMP
970 += " (0020,9156) US 2 # 2, 1 FrameAcquisitionNumber\n";
971 EXPECTED_DUMP += " (0020,9157) UL 1\\2 # 8, 2 DimensionIndexValues\n";
972 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
973 EXPECTED_DUMP += " (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
974 EXPECTED_DUMP += " (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem\n";
975 EXPECTED_DUMP += "(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem\n";
976 EXPECTED_DUMP += "(7fe0,0010) OW 0001\\0001\\0001\\0001\\0002\\0002\\0002\\0002 # 16, 1 PixelData\n";
977 }
978
checkCreatedObject(const OFString & ds_dump)979 static void checkCreatedObject(const OFString& ds_dump)
980 {
981 OFBool dump_ok = (ds_dump == EXPECTED_DUMP);
982 OFCHECK(dump_ok);
983 if (!dump_ok)
984 {
985 CERR << "Dump produced: " << OFendl << ds_dump << OFendl;
986 CERR << "------------------------------------" << OFendl;
987 CERR << "Dump expected: " << OFendl << EXPECTED_DUMP << OFendl;
988 CERR << "------------------------------------" << OFendl;
989 }
990 }
991