/* * * Copyright (C) 2016-2019, 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: dcmiod * * Author: Michael Onken * * Purpose: Class for managing SOP references as used in different modules * */ #include "dcmtk/dcmiod/iodreferences.h" #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmdata/dcdatset.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcerror.h" #include "dcmtk/dcmdata/dcfilefo.h" #include "dcmtk/dcmdata/dcvrlo.h" #include "dcmtk/dcmdata/dcvrui.h" #include "dcmtk/dcmiod/iodtypes.h" #include "dcmtk/dcmiod/iodutil.h" // ------------------- class IODReference ------------------------------- IODReference::IODReference(const IODReference::MAX_LEVEL level) : m_PatientID() , m_StudyInstanceUID() , m_SeriesInstanceUID() , m_SOPClassUID() , m_SOPInstanceUID() , m_Level(level) { } IODReference::IODReference() : m_PatientID() , m_StudyInstanceUID() , m_SeriesInstanceUID() , m_SOPClassUID() , m_SOPInstanceUID() , m_Level(LEVEL_STUDY) { } IODReference::~IODReference() { // nothing to do } void IODReference::clear() { m_StudyInstanceUID.clear(); m_SeriesInstanceUID.clear(); m_SOPClassUID.clear(); m_SOPInstanceUID.clear(); } OFBool IODReferences::add(IODReference* ref) { if (ref->check().good()) { m_References.push_back(ref); return OFTrue; } return OFFalse; } OFBool IODReference::readFromItem(DcmItem& item) { if (m_Level >= LEVEL_PATIENT) { item.findAndGetOFString(DCM_PatientID, m_PatientID); } if (m_Level >= LEVEL_STUDY) { item.findAndGetOFString(DCM_StudyInstanceUID, m_StudyInstanceUID); } if (m_Level >= LEVEL_SERIES) { item.findAndGetOFString(DCM_SeriesInstanceUID, m_SeriesInstanceUID); } if (m_Level >= LEVEL_INSTANCE) { item.findAndGetOFString(DCM_SOPClassUID, m_SOPClassUID); item.findAndGetOFString(DCM_SOPInstanceUID, m_SOPInstanceUID); } return check().good(); } OFBool IODReference::readFromFile(const OFString& filename) { clear(); DcmFileFormat ff; OFCondition result = ff.loadFile(filename.c_str()); if (result.good()) { DcmDataset* dset = ff.getDataset(); return readFromItem(*dset); } return OFFalse; } OFCondition IODReferences::readTractographyReferencedInstanceSequence(DcmItem& source) { size_t omitted = 0; size_t added = 0; DcmSequenceOfItems* seq = NULL; OFCondition result = source.findAndGetSequence(DCM_ReferencedInstanceSequence, seq); if (result.good()) { DcmItem* item = NULL; item = OFstatic_cast(DcmItem*, seq->nextInContainer(item)); while ((item != NULL) && result.good()) { IODImageReference* ref = new IODImageReference(IODReference::LEVEL_INSTANCE); if (!ref) { return EC_MemoryExhausted; } item->findAndGetOFString(DCM_ReferencedSOPClassUID, ref->m_SOPClassUID); item->findAndGetOFString(DCM_ReferencedSOPInstanceUID, ref->m_SOPInstanceUID); // if present, copy referenced frame numbers DcmElement* elem = NULL; if (item->findAndGetElement(DCM_ReferencedFrameNumber, elem).good()) { unsigned long vm = elem->getNumberOfValues(); for (unsigned long f = 0; f < vm; f++) { Sint32 val = 0; if (elem->getSint32(val, f).good()) { if (val >= 0) ref->m_ReferencedFrameNumber.push_back(OFstatic_cast(Uint32, val)); else { DCMIOD_WARN("Referenced Frame Number must be > 0 but is " << val << ", omitting frame reference"); } } else { DCMIOD_WARN("Cannot get Referenced Frame Number from position #" << f << " omitting frame reference"); } } } result = ref->check(); if (result.good()) { added++; m_References.push_back(ref); } else { DCMIOD_WARN("Could not read Image reference (invalid?): " << (*ref).toString()); omitted++; delete ref; ref = NULL; } item = OFstatic_cast(DcmItem*, seq->nextInContainer(item)); } } if ((omitted > 0) && (added > 0)) { return IOD_EC_ReferencesOmitted; } else if (omitted > 0) { return IOD_EC_InvalidReference; } return EC_Normal; } OFCondition IODReferences::writeTractographyReferencedInstanceSequence(DcmItem& item) { OFVector::iterator it = m_References.begin(); item.findAndDeleteElement(DCM_ReferencedInstanceSequence); DcmItem* seqItem = NULL; size_t numItem = 0; OFCondition result; while ((it != m_References.end() && result.good())) { if (result.good()) { result = item.findOrCreateSequenceItem(DCM_ReferencedInstanceSequence, seqItem, OFstatic_cast(long, numItem)); numItem++; } if (result.good()) { result = seqItem->putAndInsertOFStringArray(DCM_ReferencedSOPClassUID, (*it)->m_SOPClassUID); } if (result.good()) result = seqItem->putAndInsertOFStringArray(DCM_ReferencedSOPInstanceUID, (*it)->m_SOPInstanceUID); if (result.good()) { if ((*it)->getType() == IODReference::IMAGE) { if (result.good()) { IODImageReference* ref = OFstatic_cast(IODImageReference*, *it); if (ref && !ref->m_ReferencedFrameNumber.empty()) { OFStringStream oss; for (size_t f = 0; f < ref->m_ReferencedFrameNumber.size(); f++) { oss << ref->m_ReferencedFrameNumber[f] << "\\"; } OFSTRINGSTREAM_GETOFSTRING(oss, frameRefs) // Insert all references into Referenced Frame Number attribute. // Remove superfluous "\" at the end of the string. result = seqItem->putAndInsertOFStringArray(DCM_ReferencedFrameNumber, frameRefs.substr(0, frameRefs.size() - 1)); } } } } it++; } if (result.bad()) { item.findAndDeleteElement(DCM_ReferencedInstanceSequence); } return result; } OFString IODReference::toString() const { char buf[400]; sprintf(buf, "Study/Series/SOPClass/SOPInstance UIDs: %s/%s/%s/%s", m_StudyInstanceUID.c_str(), m_SeriesInstanceUID.c_str(), m_SOPClassUID.c_str(), m_SOPInstanceUID.c_str()); return buf; } OFCondition IODReference::check() const { OFCondition result; if (m_Level >= LEVEL_PATIENT) { if (!m_PatientID.empty()) { result = DcmLongString::checkStringValue(m_PatientID); } else { result = IOD_EC_InvalidElementValue; } } if (result.good() && (m_Level >= LEVEL_STUDY)) { if (!m_StudyInstanceUID.empty()) { result = DcmUniqueIdentifier::checkStringValue(m_StudyInstanceUID, "1"); } else { result = IOD_EC_InvalidElementValue; } } if (result.good() && (m_Level >= LEVEL_SERIES)) { if (!m_SeriesInstanceUID.empty()) { result = DcmUniqueIdentifier::checkStringValue(m_SeriesInstanceUID, "1"); } else { result = IOD_EC_InvalidElementValue; } } if (result.good() && (m_Level >= LEVEL_INSTANCE)) { if (!m_SOPClassUID.empty()) { result = DcmUniqueIdentifier::checkStringValue(m_SOPClassUID, "1"); } else { result = IOD_EC_InvalidElementValue; } } if (result.good() && (m_Level >= LEVEL_INSTANCE)) { if (!m_SOPInstanceUID.empty()) { result = DcmUniqueIdentifier::checkStringValue(m_SOPInstanceUID, "1"); } else { result = IOD_EC_InvalidElementValue; } } return result; } IODReference* IODReference::clone() const { IODReference* ref = new IODReference(); if (ref) { ref->m_PatientID = m_PatientID; ref->m_StudyInstanceUID = m_StudyInstanceUID; ref->m_SeriesInstanceUID = m_SeriesInstanceUID; ref->m_SOPClassUID = m_SOPClassUID; ref->m_SOPInstanceUID = m_SOPInstanceUID; ref->m_Level = m_Level; } return ref; } // ------------------- class IODImageReference ------------------------------- IODImageReference::IODImageReference(const IODReference::MAX_LEVEL level) : IODReference(level) , m_ReferencedFrameNumber() { } IODImageReference::IODImageReference() : IODReference(LEVEL_INSTANCE) , m_ReferencedFrameNumber() { } IODImageReference::IODImageReference(const OFString& patientID, const OFString& studyUID, const OFString& seriesUID, const OFString& sopInstanceUID, const OFString& sopClassUID, const OFVector& refFrameNumbers) : IODReference(LEVEL_INSTANCE) , m_ReferencedFrameNumber(refFrameNumbers) { m_PatientID = patientID; m_StudyInstanceUID = studyUID; m_SeriesInstanceUID = seriesUID; m_SOPInstanceUID = sopInstanceUID; m_SOPClassUID = sopClassUID; } IODImageReference::IODImageReference(const OFString& patientID, const OFString& studyUID, const OFString& seriesUID, const OFString& sopInstanceUID, const OFString& sopClassUID) : m_ReferencedFrameNumber() { m_PatientID = patientID; m_StudyInstanceUID = studyUID; m_SeriesInstanceUID = seriesUID; m_SOPInstanceUID = sopInstanceUID; m_SOPClassUID = sopClassUID; } IODReference* IODImageReference::clone() const { IODImageReference* ref = new IODImageReference(m_Level); if (ref) { *(OFstatic_cast(IODReference*, ref)) = *this; ref->m_ReferencedFrameNumber = m_ReferencedFrameNumber; } return ref; } void IODImageReference::clear() { IODReference::clear(); m_ReferencedFrameNumber.clear(); } OFBool IODImageReference::readFromFile(const OFString& filename, const OFVector frameNumbers) { clear(); DcmFileFormat ff; OFCondition result = ff.loadFile(filename.c_str()); if (result.good()) { if (readFromItem(*ff.getDataset())) { m_ReferencedFrameNumber = frameNumbers; return OFTrue; } } return OFFalse; } // ------------------ class IODSegmentationReference --------------------------- IODSegmentationReference::IODSegmentationReference(const IODReference::MAX_LEVEL level) : IODReference(level) , m_ReferencedSegmentNumber() { } IODSegmentationReference::IODSegmentationReference() : IODReference(LEVEL_INSTANCE) , m_ReferencedSegmentNumber() { } IODReference* IODSegmentationReference::clone() const { IODSegmentationReference* ref = new IODSegmentationReference(m_Level); if (ref) { *(OFstatic_cast(IODReference*, ref)) = *this; ref->m_ReferencedSegmentNumber = m_ReferencedSegmentNumber; } return ref; } OFBool IODSegmentationReference::readFromFile(const OFString& filename, const OFVector segmentNumbers) { clear(); DcmFileFormat ff; OFCondition result = ff.loadFile(filename.c_str()); if (result.good()) { if (readFromItem(*ff.getDataset())) { m_ReferencedSegmentNumber = segmentNumbers; return OFTrue; } } return OFFalse; } void IODSegmentationReference::clear() { IODReference::clear(); m_ReferencedSegmentNumber.clear(); } IODReferences::IODReferences() : m_References() { // nothing to do } IODReferences::~IODReferences() { DcmIODUtil::freeContainer(m_References); } IODReferences::IODReferences(const IODReferences& rhs) : m_References() { *this = rhs; } IODReferences& IODReferences::operator=(const IODReferences& rhs) { if (&rhs == this) return *this; OFVector::const_iterator ref = rhs.m_References.begin(); while (ref != rhs.m_References.end()) { m_References.push_back((*ref)->clone()); ref++; } return *this; } const OFVector& IODReferences::get() const { return m_References; } size_t IODReferences::size() const { return m_References.size(); } size_t IODReferences::addFromFiles(const OFVector& dcmFiles, const IODReference::MAX_LEVEL level) { if (dcmFiles.empty()) { return 0; } OFCondition result; OFVector::const_iterator it = dcmFiles.begin(); size_t count = 0; while (it != dcmFiles.end()) { IODReference* ref = new IODReference(level); if (ref && ref->readFromFile(*it)) { m_References.push_back(ref); count++; } else { DCMIOD_WARN("Could not add references from file " << (*it) << " (skipping)"); delete ref; } it++; } return count; } void IODReferences::clearData() { DcmIODUtil::freeContainer(m_References); }