1 /*
2 *
3 * Copyright (C) 2015-2019, Open Connections GmbH
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: dcmfg
15 *
16 * Author: Michael Onken
17 *
18 * Purpose: Main interface class for managing Functional Groups
19 *
20 */
21
22 #include "dcmtk/config/osconfig.h"
23
24 #include "dcmtk/dcmfg/fg.h"
25 #include "dcmtk/dcmfg/fgfact.h" // for creating new functional groups
26 #include "dcmtk/dcmfg/fginterface.h"
27 #include "dcmtk/dcmiod/iodutil.h" // for static helpers
28 #include "dcmtk/ofstd/ofmap.h"
29 #include "dcmtk/ofstd/ofmem.h"
30
FGInterface()31 FGInterface::FGInterface()
32 : m_shared()
33 , m_perFrame()
34 , m_checkOnWrite(OFTrue)
35 {
36 }
37
~FGInterface()38 FGInterface::~FGInterface()
39 {
40 clear();
41 }
42
clear()43 void FGInterface::clear()
44 {
45 // Clear per frame functional groups
46 while (m_perFrame.size() > 0)
47 {
48 OFMap<Uint32, FunctionalGroups*>::iterator it = m_perFrame.begin();
49 FunctionalGroups* fg = (*it).second;
50 m_perFrame.erase(it);
51 delete fg;
52 }
53
54 // Clear shared functional groups
55 m_shared.clear();
56 }
57
getNumberOfFrames()58 size_t FGInterface::getNumberOfFrames()
59 {
60 return m_perFrame.size();
61 }
62
addShared(const FGBase & group)63 OFCondition FGInterface::addShared(const FGBase& group)
64 {
65 DcmFGTypes::E_FGSharedType sharedType = group.getSharedType();
66 if (sharedType == DcmFGTypes::EFGS_ONLYPERFRAME)
67 {
68 DCMFG_ERROR("Cannot add group as shared, per DICOM, group type " << DcmFGTypes::FGType2OFString(group.getType())
69 << " is always per-frame");
70 return FG_EC_CouldNotAddFG;
71 }
72
73 // Delete all per frame groups of this type
74 for (size_t count = 0; count < m_perFrame.size(); count++)
75 {
76 deletePerFrame(OFstatic_cast(Uint32, count), group.getType());
77 }
78
79 // Create copy for insertion
80 FGBase* copy = group.clone();
81 if (!copy)
82 {
83 return EC_MemoryExhausted;
84 }
85
86 // Insert shared one, replace old one if existing
87 OFCondition result = insertShared(copy, OFTrue /* replace */);
88 if (result.bad())
89 {
90 DCMFG_ERROR("Could not add shared group of type: " << DcmFGTypes::FGType2OFString(group.getType()));
91 delete copy;
92 }
93
94 return result;
95 }
96
addPerFrame(const Uint32 frameNo,const FGBase & group)97 OFCondition FGInterface::addPerFrame(const Uint32 frameNo, const FGBase& group)
98 {
99 OFCondition result = EC_Normal;
100 DcmFGTypes::E_FGSharedType sharedType = group.getSharedType();
101 if (sharedType == DcmFGTypes::EFGS_ONLYSHARED)
102 {
103 DCMFG_ERROR("Cannot add group as per-frame, group type " << DcmFGTypes::FGType2OFString(group.getType())
104 << " is always shared");
105 return FG_EC_CouldNotAddFG;
106 }
107
108 // Check whether there is already a shared group of this type.
109 // If the content is equal to the given group, we re-use the shared one
110 FGBase* shared = getShared(group.getType());
111 // If there is a shared group
112 if (shared)
113 {
114 // If shared has identical values as given group, nothing has to be done.
115 // Else if shared group with such type exists, but content differs,
116 // we must the make the existing shared FG "per-frame", i.e. distribute
117 // it to all frames, and add the given group for the given frame.
118 if ((*shared).compare(group) != 0)
119 {
120 // We need to unshare this group, i.e. distribute it to frames
121 DCMFG_DEBUG("Converting shared group of type " << DcmFGTypes::FGType2OFString(
122 group.getType()) << " to per-frame, triggered by deviating per-frame insertion");
123 result = convertSharedToPerFrame(group.getType());
124 }
125 else
126 {
127 DCMFG_DEBUG("Re-using shared group instead of adding per-frame for frame "
128 << frameNo << ", type " << DcmFGTypes::FGType2OFString(group.getType()));
129 return EC_Normal;
130 }
131 }
132
133 if (result.good())
134 {
135 FGBase* copy = group.clone();
136 if (!copy)
137 {
138 return EC_MemoryExhausted;
139 }
140 result = insertPerFrame(frameNo, copy);
141 if (result.bad())
142 delete copy;
143 }
144
145 return result;
146 }
147
148 // Get specific functional group for a frame,
149 // no matter whether it is stored per frame or shared
get(const Uint32 frameNo,const DcmFGTypes::E_FGType fgType)150 FGBase* FGInterface::get(const Uint32 frameNo, const DcmFGTypes::E_FGType fgType)
151 {
152 OFBool helpShared; // throw-away variable
153 return get(frameNo, fgType, helpShared);
154 }
155
getPerFrame(const Uint32 frameNo) const156 const FunctionalGroups* FGInterface::getPerFrame(const Uint32 frameNo) const
157 {
158 if (frameNo > m_perFrame.size())
159 {
160 return NULL;
161 }
162 else
163 {
164 return (*(m_perFrame.find(frameNo))).second;
165 }
166 }
167
getShared() const168 const FunctionalGroups* FGInterface::getShared() const
169 {
170 return &m_shared;
171 }
172
173 // Read enhanced multi-frame information from DICOM item, usually DcmDataset
read(DcmItem & dataset)174 OFCondition FGInterface::read(DcmItem& dataset)
175 {
176 OFCondition result = EC_Normal;
177
178 // clear any old values
179 clear();
180
181 /* read shared functional groups */
182 if (result.good())
183 {
184 result = readSharedFG(dataset);
185 }
186
187 /* read per frame functional groups */
188 if (result.good())
189 {
190 result = readPerFrameFG(dataset);
191 }
192
193 return result;
194 }
195
readSharedFG(DcmItem & dataset)196 OFCondition FGInterface::readSharedFG(DcmItem& dataset)
197 {
198 /* read shared functional groups */
199 DcmSequenceOfItems* shared = NULL;
200 OFCondition result = dataset.findAndGetSequence(DCM_SharedFunctionalGroupsSequence, shared);
201 if (result.bad())
202 {
203 DCMFG_ERROR("Could not find Shared Functional Group Sequence");
204 return FG_EC_NoSharedFG;
205 }
206
207 if (shared->card() > 1)
208 {
209 DCMFG_WARN("More than one item in Shared Functional Group Sequence, only considering the first one");
210 }
211 else if (shared->card() == 0)
212 {
213 DCMFG_WARN("No Item in Shared Functional Group Sequence but exactly one expected");
214 return FG_EC_NoSharedFG;
215 }
216
217 // get the only item of shared functional group sequence
218 DcmItem* sharedFGs = shared->getItem(0);
219 // read all functional groups from shared fg sequence item
220 result = readSingleFG(*sharedFGs, m_shared);
221
222 return result;
223 }
224
readPerFrameFG(DcmItem & dataset)225 OFCondition FGInterface::readPerFrameFG(DcmItem& dataset)
226 {
227 /* read per-frame functional groups */
228 DcmSequenceOfItems* perFrame = NULL;
229 OFCondition result = dataset.findAndGetSequence(DCM_PerFrameFunctionalGroupsSequence, perFrame);
230 if (result.bad())
231 {
232 DCMFG_ERROR("Could not find Per-Frame Functional Group Sequence");
233 return FG_EC_NoPerFrameFG;
234 }
235
236 /* 1-n items required */
237 size_t numFrames = perFrame->card();
238 if (numFrames == 0)
239 {
240 DCMFG_WARN("No Item in Shared Functional Group Sequence but exactly one or more expected");
241 return FG_EC_NoPerFrameFG;
242 }
243
244 /* Read functional groups for each item (one per frame) */
245 DcmItem* oneFrameItem = OFstatic_cast(DcmItem*, perFrame->nextInContainer(NULL));
246 Uint32 count = 0;
247 while (oneFrameItem != NULL)
248 {
249 OFunique_ptr<FunctionalGroups> perFrameGroups(new FunctionalGroups());
250 if (!oneFrameItem)
251 {
252 DCMFG_ERROR("Could not get functional group item for frame #" << count << " (internal error)");
253 }
254 else if (!perFrameGroups.get())
255 {
256 DCMFG_ERROR("Could not create functional groups for frame #" << count << ": Memory exhausted?");
257 }
258 else
259 {
260 result = readSingleFG(*oneFrameItem, *perFrameGroups);
261 if (result.good())
262 {
263 if (!m_perFrame.insert(OFMake_pair(count, perFrameGroups.release())).second)
264 {
265 DCMFG_ERROR("Could not store functional groups for frame #" << count << " (internal error)");
266 }
267 }
268 else
269 {
270 DCMFG_ERROR("Could not read functional groups for frame #" << count << ": " << result.text());
271 }
272 }
273 oneFrameItem = OFstatic_cast(DcmItem*, perFrame->nextInContainer(oneFrameItem));
274 count++;
275 }
276 return EC_Normal; // for now we always return EC_Normal...
277 }
278
readSingleFG(DcmItem & fgItem,FunctionalGroups & groups)279 OFCondition FGInterface::readSingleFG(DcmItem& fgItem, FunctionalGroups& groups)
280 {
281 OFCondition result;
282 size_t card = fgItem.card();
283 OFString fgname;
284 for (size_t count = 0; count < card; count++)
285 {
286 DcmElement* elem = fgItem.getElement(OFstatic_cast(unsigned long, count));
287 // TODO: non-sequence elements are not explicitly forbidden here(?), we could store them and re-store them later
288 if (elem->getVR() != EVR_SQ)
289 {
290 DCMFG_WARN("Found non-sequence element in functional group sequence item (ignored): " << elem->getTag());
291 }
292 else
293 {
294 FGBase* fg = FGFactory::instance().create(elem->getTag());
295 if (fg != NULL)
296 {
297 OFStringStream stream;
298 stream << DcmFGTypes::tagKey2FGString(elem->getTag()) << " " << elem->getTag();
299 OFSTRINGSTREAM_GETSTR(stream, tmpstr)
300 fgname = tmpstr;
301 OFSTRINGSTREAM_FREESTR(tmpstr)
302 result = fg->read(fgItem);
303 if (result.bad())
304 {
305 DCMFG_WARN("Cannot read functional group: " << fgname << " (ignored)");
306 }
307 // we also accept groups while reading which could instantiated but not could not be read
308 result = groups.insert(fg, OFTrue);
309 if (result.good())
310 {
311 DCMFG_DEBUG("Inserted functional group: " << fgname);
312 }
313 else
314 {
315 DCMFG_ERROR("Could not insert functional group: " << fgname << " (internal error)");
316 delete fg;
317 }
318 }
319 else
320 {
321 DCMFG_WARN("Cannot understand functional group for sequence tag: " << elem->getTag());
322 }
323 }
324 }
325 return EC_Normal; // for now we always return EC_Normal...
326 }
327
328 // Write enhanced multi-frame information to DICOM item, usually DcmDataset
write(DcmItem & dataset)329 OFCondition FGInterface::write(DcmItem& dataset)
330 {
331 // Check data integrity of functional group macros */
332 if (m_checkOnWrite)
333 {
334 if (!check())
335 return FG_EC_CouldNotWriteFG;
336 }
337
338 // Write shared functional Groups
339 OFCondition result = writeSharedFG(dataset);
340
341 // Write per frame functional groups
342 if (result.good())
343 result = writePerFrameFG(dataset);
344
345 return result;
346 }
347
getShared(const DcmFGTypes::E_FGType fgType)348 FGBase* FGInterface::getShared(const DcmFGTypes::E_FGType fgType)
349 {
350 return m_shared.find(fgType);
351 }
352
insertShared(FGBase * group,const OFBool replaceExisting)353 OFCondition FGInterface::insertShared(FGBase* group, const OFBool replaceExisting)
354 {
355 return m_shared.insert(group, replaceExisting);
356 }
357
getPerFrame(const Uint32 frameNo,const DcmFGTypes::E_FGType fgType)358 FGBase* FGInterface::getPerFrame(const Uint32 frameNo, const DcmFGTypes::E_FGType fgType)
359 {
360 FGBase* group = NULL;
361 OFMap<Uint32, FunctionalGroups*>::iterator it = m_perFrame.find(frameNo);
362 if (it != m_perFrame.end())
363 {
364 FunctionalGroups* perFrameGroups = (*it).second;
365 group = perFrameGroups->find(fgType);
366 }
367
368 return group;
369 }
370
deleteShared(const DcmFGTypes::E_FGType fgType)371 OFBool FGInterface::deleteShared(const DcmFGTypes::E_FGType fgType)
372 {
373 FGBase* group = m_shared.find(fgType);
374 if (group)
375 {
376 delete m_shared.remove(fgType);
377 return OFTrue;
378 }
379 return OFFalse;
380 }
381
deletePerFrame(const Uint32 frameNo,const DcmFGTypes::E_FGType fgType)382 OFBool FGInterface::deletePerFrame(const Uint32 frameNo, const DcmFGTypes::E_FGType fgType)
383 {
384 OFMap<Uint32, FunctionalGroups*>::iterator it = m_perFrame.find(frameNo);
385 if (it != m_perFrame.end())
386 {
387 if ((*it).second)
388 {
389 FGBase* remove = (*it).second->remove(fgType);
390 if (remove)
391 {
392 DCMFG_DEBUG("Deleting FG for frame " << frameNo << ", type: " << DcmFGTypes::FGType2OFString(fgType));
393 delete remove;
394 remove = NULL;
395 return OFTrue;
396 }
397 }
398 }
399 return OFFalse;
400 }
401
deletePerFrame(const DcmFGTypes::E_FGType fgType)402 size_t FGInterface::deletePerFrame(const DcmFGTypes::E_FGType fgType)
403 {
404 size_t numDeleted = 0;
405 const size_t numFrames = m_perFrame.size();
406 for (size_t frameNo = 0; frameNo < numFrames; frameNo++)
407 {
408 if (deletePerFrame(OFstatic_cast(Uint32, frameNo), fgType))
409 {
410 numDeleted++;
411 }
412 }
413 return numDeleted;
414 }
415
deleteFrame(const Uint32 frameNo)416 size_t FGInterface::deleteFrame(const Uint32 frameNo)
417 {
418 OFMap<Uint32, FunctionalGroups*>::iterator it = m_perFrame.find(frameNo);
419 if (it != m_perFrame.end())
420 {
421 if ((*it).second)
422 {
423 FunctionalGroups::iterator fg = (*it).second->begin();
424 while (fg != (*it).second->end())
425 {
426 delete (*fg).second;
427 fg++;
428 }
429 }
430 m_perFrame.erase(it);
431 }
432 return OFFalse;
433 }
434
setCheckOnWrite(const OFBool doCheck)435 void FGInterface::setCheckOnWrite(const OFBool doCheck)
436 {
437 m_checkOnWrite = doCheck;
438 }
439
getCheckOnWrite()440 OFBool FGInterface::getCheckOnWrite()
441 {
442 return m_checkOnWrite;
443 }
444
getOrCreatePerFrameGroups(const Uint32 frameNo)445 FunctionalGroups* FGInterface::getOrCreatePerFrameGroups(const Uint32 frameNo)
446 {
447 OFMap<Uint32, FunctionalGroups*>::iterator it = m_perFrame.find(frameNo);
448 if (it != m_perFrame.end())
449 return (*it).second;
450
451 FunctionalGroups* fg = new FunctionalGroups();
452 if (fg != NULL)
453 {
454 if (!(m_perFrame.insert(OFMake_pair(frameNo, fg))).second)
455 {
456 DCMFG_ERROR("Could not insert Per-frame Functional Groups for frame " << frameNo << ": "
457 << "Internal error");
458 delete fg;
459 fg = NULL;
460 }
461 }
462 else
463 {
464 DCMFG_ERROR("Could not create Per-frame Functional Groups for frame " << frameNo << ": "
465 << "Memory exhausted");
466 }
467
468 return fg;
469 }
470
writePerFrameFG(DcmItem & dataset)471 OFCondition FGInterface::writePerFrameFG(DcmItem& dataset)
472 {
473 DCMFG_DEBUG("Writing per-frame functional groups");
474 OFCondition result
475 = dataset.insertEmptyElement(DCM_PerFrameFunctionalGroupsSequence, OFTrue); // start with empty sequence
476 if (result.bad())
477 {
478 DCMFG_ERROR("Could not create Per-frame Functional Groups Sequence");
479 return result;
480 }
481
482 /* Iterate over frames */
483 OFMap<Uint32, FunctionalGroups*>::iterator it = m_perFrame.begin();
484 size_t numFrames = m_perFrame.size();
485 for (size_t count = 0; (count < numFrames) && result.good(); count++)
486 {
487 DcmItem* perFrameItem = NULL;
488 result = dataset.findOrCreateSequenceItem(
489 DCM_PerFrameFunctionalGroupsSequence, perFrameItem, OFstatic_cast(long, count));
490 if (result.good())
491 {
492 /* Iterate over groups for each frame */
493 FunctionalGroups::iterator groupIt = (*it).second->begin();
494 while (result.good() && (groupIt != (*it).second->end()))
495 {
496 DCMFG_DEBUG("Writing per-frame group: " << DcmFGTypes::FGType2OFString((*groupIt).second->getType())
497 << " for frame #" << count);
498 result = (*groupIt).second->write(*perFrameItem);
499 groupIt++;
500 }
501 }
502 else
503 {
504 DCMFG_ERROR("Cannot create item in Per-frame Functional Groups Sequence");
505 }
506 it++;
507 }
508 return result;
509 }
510
writeSharedFG(DcmItem & dataset)511 OFCondition FGInterface::writeSharedFG(DcmItem& dataset)
512 {
513 DCMFG_DEBUG("Writing shared functional groups");
514 OFCondition result
515 = dataset.insertEmptyElement(DCM_SharedFunctionalGroupsSequence, OFTrue); // start with empty sequence
516 DcmItem* sharedFGItem = NULL;
517 if (result.good())
518 {
519 result = dataset.findOrCreateSequenceItem(DCM_SharedFunctionalGroupsSequence, sharedFGItem, 0);
520 }
521 if (result.bad())
522 {
523 DCMFG_ERROR("Could not create Shared Functional Groups Sequence with single item");
524 return result;
525 }
526
527 FunctionalGroups::iterator it = m_shared.begin();
528 FunctionalGroups::iterator end = m_shared.end();
529 while ((it != end) && result.good())
530 {
531 DCMFG_DEBUG("Writing shared group: " << DcmFGTypes::FGType2OFString((*it).second->getType()));
532 result = (*it).second->write(*sharedFGItem);
533 it++;
534 }
535 return result;
536 }
537
insertPerFrame(const Uint32 frameNo,FGBase * group,const OFBool replaceExisting)538 OFCondition FGInterface::insertPerFrame(const Uint32 frameNo, FGBase* group, const OFBool replaceExisting)
539 {
540 if (group == NULL)
541 return EC_IllegalParameter;
542
543 OFCondition result = EC_Normal;
544 FGBase* existing = getPerFrame(frameNo, group->getType());
545 if (existing)
546 {
547 if (replaceExisting)
548 {
549 DCMFG_DEBUG("Replacing per-frame FG for frame: " << frameNo << ", type: "
550 << DcmFGTypes::FGType2OFString(group->getType()));
551 deletePerFrame(frameNo, group->getType());
552 }
553 else
554 {
555 result = FG_EC_DoubledFG;
556 }
557 }
558
559 // Insert per-frame functional group
560 if (result.good())
561 {
562 FunctionalGroups* perFrameGroups = getOrCreatePerFrameGroups(frameNo);
563 if (perFrameGroups != NULL)
564 {
565 result = perFrameGroups->insert(group, replaceExisting);
566 }
567 else
568 {
569 result = FG_EC_CouldNotInsertFG;
570 }
571 }
572 return result;
573 }
574
convertSharedToPerFrame(const DcmFGTypes::E_FGType fgType)575 OFCondition FGInterface::convertSharedToPerFrame(const DcmFGTypes::E_FGType fgType)
576 {
577 FGBase* shared = m_shared.remove(fgType);
578 if (!shared)
579 {
580 return FG_EC_NoSuchGroup;
581 }
582
583 OFCondition result;
584 size_t numFrames = m_perFrame.size();
585 // Walk over all existing frames and copy "old" shared group to them
586 size_t count = 0;
587 for (count = 0; result.good() && (count < numFrames); count++)
588 {
589 FGBase* clone = shared->clone();
590 if (!clone)
591 {
592 result = EC_MemoryExhausted;
593 }
594 else
595 {
596 result = insertPerFrame(OFstatic_cast(Uint32, count), clone, OFTrue /* replace existing */);
597 if (result.bad())
598 {
599 delete clone;
600 }
601 }
602 }
603 return result;
604 }
605
get(const Uint32 frameNo,const DcmFGTypes::E_FGType fgType,OFBool & isPerFrame)606 FGBase* FGInterface::get(const Uint32 frameNo, const DcmFGTypes::E_FGType fgType, OFBool& isPerFrame)
607 {
608 FGBase* group = m_shared.find(fgType);
609 if (!group)
610 {
611 group = getPerFrame(frameNo, fgType);
612 isPerFrame = OFTrue;
613 }
614 else
615 {
616 isPerFrame = OFFalse;
617 }
618
619 return group;
620 }
621
check()622 OFBool FGInterface::check()
623 {
624 size_t numFrames = m_perFrame.size();
625 DCMFG_DEBUG("Checking functional group structure for " << numFrames << " frames");
626 size_t numErrors = 0;
627 for (size_t frameCount = 0; frameCount < numFrames; frameCount++)
628 {
629 DCMFG_TRACE("Checking frame " << frameCount << "...");
630 // Every frame requires the FrameContent functional group, check "en passant"
631 OFBool foundFrameContent = OFFalse;
632 OFMap<Uint32, FunctionalGroups*>::iterator frameFG = m_perFrame.begin();
633 OFMap<Uint32, FunctionalGroups*>::iterator end = m_perFrame.end();
634 while (frameFG != end)
635 {
636 FunctionalGroups::iterator group = (*frameFG).second->begin();
637 FunctionalGroups::iterator groupEnd = (*frameFG).second->end();
638 while (group != groupEnd)
639 {
640 // Check that per-frame group is not a shared group at the same time
641 DcmFGTypes::E_FGType groupType = group->second->getType();
642 if ((groupType != DcmFGTypes::EFG_UNDEFINED) && (groupType != DcmFGTypes::EFG_UNKNOWN))
643 {
644 if (m_shared.find(groupType) != NULL)
645 {
646 DCMFG_ERROR("Functional group of type " << DcmFGTypes::FGType2OFString(groupType)
647 << " is shared AND per-frame for frame " << frameCount);
648 numErrors++;
649 }
650 if (groupType == DcmFGTypes::EFG_FRAMECONTENT)
651 foundFrameContent = OFTrue;
652 }
653 // Check if "per-frame" is allowed for this group;
654 if (group->second->getSharedType() == DcmFGTypes::EFGS_ONLYSHARED)
655 {
656 DCMFG_ERROR("Functional group of type " << DcmFGTypes::FGType2OFString(groupType)
657 << " can never be per-frame, but found for frame "
658 << frameCount);
659 numErrors++;
660 }
661 group++;
662 }
663 frameFG++;
664 }
665 if (!foundFrameContent)
666 {
667 DCMFG_ERROR("Frame Content Functional group missing for frame #" << frameCount);
668 numErrors++;
669 }
670 }
671
672 // Check whether shared groups contain FGs that are only permitted per-frame
673 FunctionalGroups::iterator it = m_shared.begin();
674 FunctionalGroups::iterator end = m_shared.end();
675 while (it != end)
676 {
677 if ((*it).second->getSharedType() == DcmFGTypes::EFGS_ONLYPERFRAME)
678 {
679 DCMFG_ERROR("Functional group of type " << DcmFGTypes::FGType2OFString((*it).second->getType())
680 << " used as shared functional group but must be per-frame");
681 numErrors++;
682 }
683 it++;
684 }
685
686 if (numErrors > 0)
687 return OFFalse;
688
689 return OFTrue;
690 }
691