Created
August 12, 2017 19:58
-
-
Save fedorov/41e42c1e701d74b2391792241809fe62 to your computer and use it in GitHub Desktop.
Sample program reading an SR document based on TID 1500 and printing details on the measurements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Sample program reading an SR document based on TID 1500 and printing details on the measurements | |
| * | |
| * Author: J. Riesmeier, Oldenburg, Germany | |
| * | |
| */ | |
| #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ | |
| #include "dcmtk/dcmsr/dsrdoc.h" | |
| #include "dcmtk/dcmsr/dsrcodtn.h" | |
| #include "dcmtk/dcmsr/dsrimgtn.h" | |
| #include "dcmtk/dcmsr/dsrnumtn.h" | |
| #include "dcmtk/dcmsr/dsrtextn.h" | |
| #include "dcmtk/dcmsr/dsruidtn.h" | |
| #include "dcmtk/dcmsr/codes/dcm.h" | |
| #include "dcmtk/dcmsr/codes/ncit.h" | |
| #include "dcmtk/dcmsr/codes/srt.h" | |
| #include "dcmtk/dcmsr/codes/umls.h" | |
| #include <dcmtk/dcmsr/dsruidtn.h> | |
| #include "dcmtk/dcmdata/dcfilefo.h" | |
| class TID1500_Reader | |
| : DSRDocumentTree | |
| { | |
| public: | |
| TID1500_Reader(const DSRDocumentTree &tree) | |
| : DSRDocumentTree(tree) | |
| { | |
| /* check for expected template identification */ | |
| if (!compareTemplateIdentification("1500", "DCMR")) | |
| CERR << "warning: template identification \"TID 1500 (DCMR)\" not found" << OFendl; | |
| } | |
| OFCondition getMeasurements() | |
| { | |
| OFCondition status = SR_EC_InvalidDocumentTree; | |
| /* iterate over the document tree and print the measurements */ | |
| if (gotoNamedChildNode(CODE_DCM_ImagingMeasurements)) | |
| { | |
| if (gotoNamedChildNode(CODE_DCM_MeasurementGroup)) | |
| { | |
| do { | |
| COUT << "Measurement Group:" << OFendl; | |
| /* remember cursor to current content item (as a starting point) */ | |
| const DSRDocumentTreeNodeCursor groupCursor(getCursor()); | |
| /* print some general information */ | |
| printContentItem(CODE_NCIt_ActivitySession, groupCursor); | |
| printContentItem(CODE_UMLS_TimePoint, groupCursor); | |
| printContentItem(CODE_SRT_MeasurementMethod, groupCursor); | |
| printContentItem(CODE_DCM_ReferencedSegment, groupCursor); | |
| printContentItem(CODE_DCM_SourceSeriesForSegmentation, groupCursor); | |
| printContentItem(CODE_DCM_TrackingIdentifier, groupCursor); | |
| printContentItem(CODE_DCM_TrackingUniqueIdentifier, groupCursor); | |
| printContentItem(CODE_DCM_Finding, groupCursor); | |
| printContentItem(CODE_SRT_FindingSite, groupCursor); | |
| /* print details on measurement value(s) */ | |
| DSRDocumentTreeNodeCursor cursor(groupCursor); | |
| if (cursor.gotoChild()) | |
| { | |
| size_t counter = 0; | |
| COUT << "- Measurements:" << OFendl; | |
| /* iterate over all direct child nodes */ | |
| do { | |
| const DSRDocumentTreeNode *node = cursor.getNode(); | |
| /* and check for numeric measurement value content items */ | |
| if ((node != NULL) && (node->getValueType() == VT_Num)) | |
| { | |
| COUT << " #" << (++counter) << " "; | |
| printMeasurement(*OFstatic_cast(const DSRNumTreeNode *, node), cursor); | |
| } | |
| } while (cursor.gotoNext()); | |
| } | |
| } while (gotoNextNamedNode(CODE_DCM_MeasurementGroup)); | |
| status = EC_Normal; | |
| } | |
| } | |
| return status; | |
| } | |
| void printContentItem(const DSRCodedEntryValue &conceptName, | |
| DSRDocumentTreeNodeCursor cursor) | |
| { | |
| /* try to go to the given content item */ | |
| if (gotoNamedChildNode(conceptName, cursor)) | |
| { | |
| const DSRDocumentTreeNode *node = cursor.getNode(); | |
| if (node != NULL) | |
| { | |
| COUT << "- " << conceptName.getCodeMeaning() << ": "; | |
| /* use appropriate value for output */ | |
| switch (node->getValueType()) | |
| { | |
| case VT_Text: | |
| COUT << OFstatic_cast(const DSRTextTreeNode *, node)->getValue(); | |
| break; | |
| case VT_UIDRef: | |
| COUT << OFstatic_cast(const DSRUIDRefTreeNode *, node)->getValue(); | |
| break; | |
| case VT_Code: | |
| COUT << OFstatic_cast(const DSRCodeTreeNode *, node)->getValue().getCodeMeaning(); | |
| break; | |
| case VT_Image: | |
| COUT << OFstatic_cast(const DSRImageTreeNode *, node)->getValue().getSOPInstanceUID(); | |
| break; | |
| default: | |
| COUT << "n/a"; | |
| } | |
| COUT << OFendl; | |
| } | |
| } | |
| } | |
| void printMeasurement(const DSRNumTreeNode &numNode, | |
| DSRDocumentTreeNodeCursor cursor) | |
| { | |
| COUT << numNode.getConceptName().getCodeMeaning() << ": " << numNode.getValue().getNumericValue() << " " << numNode.getValue().getMeasurementUnit().getCodeMeaning() << OFendl; | |
| /* check for any modifiers */ | |
| if (cursor.gotoChild()) | |
| { | |
| /* iterate over all direct child nodes */ | |
| do { | |
| const DSRDocumentTreeNode *node = cursor.getNode(); | |
| if (node != NULL) | |
| { | |
| /* and check for "has concept mod CODE" content items */ | |
| if ((node->getRelationshipType() == RT_hasConceptMod) && (node->getValueType() == VT_Code)) | |
| { | |
| /* of course, we could also filter on concept name (if needed) */ | |
| COUT << " - " << node->getConceptName().getCodeMeaning() << ": " << OFstatic_cast(const DSRCodeTreeNode *, node)->getCodeMeaning() << OFendl; | |
| } | |
| /* as well as "inferred from NUM" content items */ | |
| else if ((node->getRelationshipType() == RT_inferredFrom) && (node->getValueType() == VT_Num)) | |
| { | |
| COUT << " - " << node->getConceptName().getCodeMeaning() << ": " << OFstatic_cast(const DSRNumTreeNode *, node)->getNumericValue() | |
| << " " << OFstatic_cast(const DSRNumTreeNode *, node)->getValue().getMeasurementUnit().getCodeMeaning() << OFendl; | |
| } | |
| } | |
| } while (cursor.gotoNext()); | |
| } | |
| } | |
| using DSRDocumentTree::gotoNamedChildNode; | |
| protected: | |
| size_t gotoNamedChildNode(const DSRCodedEntryValue &conceptName, | |
| DSRDocumentTreeNodeCursor &cursor) | |
| { | |
| size_t nodeID = 0; | |
| /* goto the first child node */ | |
| if (conceptName.isValid() && cursor.gotoChild()) | |
| { | |
| const DSRDocumentTreeNode *node; | |
| /* iterate over all nodes on this level */ | |
| do { | |
| node = cursor.getNode(); | |
| /* and check for the desired concept name */ | |
| if ((node != NULL) && (node->getConceptName() == conceptName)) | |
| nodeID = node->getNodeID(); | |
| } while ((nodeID == 0) && cursor.gotoNext()); | |
| } | |
| return nodeID; | |
| } | |
| }; | |
| int main(int argc, char *argv[]) | |
| { | |
| int result = 0; | |
| /* check number of command line parameters */ | |
| if (argc >= 2) | |
| { | |
| const char *filename = argv[1]; | |
| DcmFileFormat fileformat; | |
| /* first, load the DICOM file */ | |
| COUT << "Filename: " << filename << OFendl; | |
| OFCondition status = fileformat.loadFile(filename); | |
| if (status.good()) | |
| { | |
| DSRDocument document; | |
| DcmDataset &dataset = *fileformat.getDataset(); | |
| /* then, read the SR document from the DICOM dataset */ | |
| status = document.read(dataset); | |
| if (status.good()) | |
| { | |
| TID1500_Reader reader(document.getTree()); | |
| /* and, finally, get the measurements from the SR document */ | |
| status = reader.getMeasurements(); | |
| if (status.bad()) | |
| { | |
| CERR << "error: cannot get Measurements from DICOM SR document: " << status.text() << OFendl; | |
| result = 4; | |
| } | |
| } else { | |
| CERR << "error: cannot read DICOM SR document: " << status.text() << OFendl; | |
| result = 3; | |
| } | |
| } else { | |
| CERR << "error: cannot load DICOM file: " << status.text() << OFendl; | |
| result = 2; | |
| } | |
| } else { | |
| CERR << "error: no input filename specified" << OFendl; | |
| result = 1; | |
| } | |
| return result; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment