/* * * Copyright (C) 2015-2021, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmfg * * Author: Michael Onken * * Purpose: Class for reading, accessing and writing stacks * */ #include "dcmtk/dcmfg/stackinterface.h" #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmfg/fgfracon.h" #include "dcmtk/dcmiod/iodutil.h" FGStackInterface::FGStackInterface() : m_Stacks() { } FGStackInterface::~FGStackInterface() { while (m_Stacks.size() != 0) { OFMap::iterator it = m_Stacks.begin(); FGStack* toDelete = (*it).second; m_Stacks.erase(it); delete toDelete; } } void FGStackInterface::clear() { size_t stackSize = m_Stacks.size(); OFMap::iterator it; for (size_t count = 0; count < stackSize; count++) { it = m_Stacks.begin(); m_Stacks.erase(it); } } OFCondition FGStackInterface::read(FGInterface& fgSource) { clear(); size_t numFrames = fgSource.getNumberOfFrames(); for (size_t count = 0; count < numFrames; count++) { // Get frame content FG if existing FGFrameContent* fracon = OFstatic_cast(FGFrameContent*, fgSource.get(OFstatic_cast(Uint32, count), DcmFGTypes::EFG_FRAMECONTENT)); if (fracon != NULL) { OFString stackID; Uint32 inStackPos = 0; // Check whether stack ID is actually present and get value if ((fracon->getStackID(stackID).good()) && (fracon->getInStackPositionNumber(inStackPos).good())) { // Check whether this is a stack ID we do not know yet OFMap::iterator it = m_Stacks.begin(); while (it != m_Stacks.end()) { // If this is an old stack, add frame to it if ((*it).second->getStackID() == stackID) { // Add frame to stack if it is not in yet if ((*it).second->getInStackPos(OFstatic_cast(Uint32, count)) == 0) { (*it).second->addFrame(OFstatic_cast(Uint32, count), inStackPos); } } it++; } // If this is a new stack, add it to the list if (it == m_Stacks.end()) { FGStack* stack = new FGStack(stackID); if (stack == NULL) { return EC_MemoryExhausted; } stack->addFrame(OFstatic_cast(Uint32, count), inStackPos); if (!m_Stacks.insert(OFMake_pair(stack->getStackID(), stack)).second) { delete stack; DCMFG_ERROR("Could not add stack to internal list (internal error, ignored)"); } } } else { DCMFG_WARN("Reading stacks but Frame " << count << " does not provide Stack ID or In-Stack Position"); } } else { DCMFG_WARN("Reading stacks but Frame " << count << " does not provide a Frame Content functional group"); } } if (m_Stacks.size() == 0) { return FG_EC_NoStacksFound; } return EC_Normal; } OFCondition FGStackInterface::write(FGInterface& fgDestination) { size_t numFrames = fgDestination.getNumberOfFrames(); if (numFrames == 0) { return FG_EC_NotEnoughFrames; } // Check whether maximum in-stack position does not exceed number of frames in image if (!checkConsistency(&fgDestination)) { return FG_EC_InvalidData; } // Walk through stacks, and for each, add/overwrite stack information to // the respective Frame Content functional groups OFCondition result; OFMap::iterator stack = m_Stacks.begin(); while (result.good() && (stack != m_Stacks.end())) { OFString stackID = (*stack).second->getStackID(); // Walk over all referenced frames and add Stack information OFMap::const_iterator stackEntry = (*stack).second->begin(); while (stackEntry != (*stack).second->end()) { Uint32 frameNo = (*stackEntry).first; Uint32 inStackPos = (*stackEntry).second; FGFrameContent* fg = ensureFrameContentFG(frameNo, fgDestination); // Create functional group if not already existing if (fg == NULL) { result = FG_EC_CouldNotAddFG; } if (result.good()) result = fg->setStackID(stackID); if (result.good()) result = fg->setInStackPositionNumber(inStackPos + 1 /* vector counts from 0, position in DICOM starts with 1 */); if (result.bad()) { DCMFG_ERROR("Could not create or add stack with ID " << stackID << " to frame " << frameNo << ": " << result.text()); } stackEntry++; } stack++; } return result; } FGFrameContent* FGStackInterface::ensureFrameContentFG(const Uint32 frameNo, FGInterface& fg) { FGFrameContent* content = OFstatic_cast(FGFrameContent*, fg.get(frameNo, DcmFGTypes::EFG_FRAMECONTENT)); if (!content) { FGFrameContent newContent; OFCondition result = fg.addPerFrame(frameNo, newContent); if (result.bad()) { DCMFG_ERROR("Could not add Frame Content FG for frame " << frameNo); } else { content = OFstatic_cast(FGFrameContent*, fg.get(frameNo, DcmFGTypes::EFG_FRAMECONTENT)); } } return content; } OFBool FGStackInterface::addStack(FGStack* stack) { if (stack == NULL) return OFFalse; if (stack->getStackID().empty()) { DCMFG_ERROR("Stack ID cannot be empty"); return OFFalse; } return m_Stacks.insert(OFMake_pair(stack->getStackID(), stack)).second; } size_t FGStackInterface::numStacks() const { return m_Stacks.size(); } OFBool FGStackInterface::checkConsistency(FGInterface* fgContext) { size_t errors = 0; size_t count = 0; // Run over stacks and check whether each has a Stack ID. // Also check whether stacks can be valid in the context // of the given functional groups (e.g. frame range ok) OFMap::iterator it = m_Stacks.begin(); while (it != m_Stacks.end()) { if ((*it).second->getStackID().empty()) { errors++; DCMFG_ERROR("Stack ID for stack #" << count << " is empty"); } if (fgContext) { errors += checkContext((*it).second, fgContext); } it++; count++; } return (errors == 0); } size_t FGStackInterface::checkContext(FGStack* stack, FGInterface* context) { // Must be non-empty if (!stack || !context) return 1; // Check whether we have any frames size_t numFrames = context->getNumberOfFrames(); if (numFrames == 0) { DCMFG_ERROR("Cannot have stacks without frames"); return 1; } // Walk over frames in stack and check that frame number and // in-stack position is in range with the number of available frames size_t errors = 0; const OFMap& frames = stack->m_FrameNumbers; FGStack::const_iterator it = frames.begin(); for (size_t count = 0; count < numFrames; count++) { // Frame number must be in range if ((*it).first > numFrames) { DCMFG_ERROR("Stack references frame #" << (*it).first << " but only #" << numFrames << " frames exist"); errors++; } if ((*it).second > numFrames) { DCMFG_ERROR("Stack references in-stack position #" << (*it).first << " but only #" << numFrames << " frames exist"); errors++; } } return errors; }