dcmsr: bibliothèque de rapports structurés et applications utilitaires
Ce module contient des classes permettant de lire, écrire, créer, modifier, consulter, imprimer et restituer des documents DICOM de rapport structuré (SR). La liste des classes SOP prises en charge est fournie dans DSRTypes::E_DocumentType.
Les principales classes d'interface sont :
- DSRDocument
- DSRDocumentTree
- DSRDocumentSubTree
- DSRDocumentTreeNode
- DSRContentItem
- DSRCodedEntryValue
Voici quelques classes supplémentaires utiles pour l'implémentation de templates SR :
Actuellement, les templates SR suivants sont implémentés (voir les notes pour plus de détails) :
- TID300_Measurement
- TID1001_ObservationContext
- TID1204_LanguageOfContentItemAndDescendants
- TID1411_VolumetricROIMeasurements
- TID1419_ROIMeasurements_Measurement
- TID1500_MeasurementReport
- TID1501_MeasurementGroup
- TID1600_ImageLibrary
- TID4019_AlgorithmIdentification
Outils
Ce module contient les outils en ligne de commande suivants :
- dsr2html: restituer un fichier et un jeu de données DICOM SR en HTML/XHTML
- dsr2xml: convertir un fichier et un jeu de données DICOM SR en XML
- dsrdump: afficher un fichier et un jeu de données DICOM SR
- xml2dsr: convertir un document XML en fichier DICOM SR
Exemples
L'exemple suivant montre comment charger un rapport structuré DICOM et restituer son contenu au format HTML :
DcmFileFormat fileformat;
OFCondition status = fileformat.loadFile("test.dcm");
if (status.good())
{
DSRDocument document;
status = document.read(*fileformat.getDataset());
if (status.good())
{
status = document.renderHTML(cout);
if (status.bad())
cerr << "Error: cannot render SR document (" << status.text() << ")" << endl;
} else
cerr << "Error: cannot read SR document (" << status.text() << ")" << endl;
} else
cerr << "Error: cannot load DICOM file (" << status.text() << ")" << endl;
L'exemple suivant montre comment créer un rapport structuré DICOM et l'enregistrer dans un fichier (pour plus de détails, voir le fichier source mkreport) :
DSRDocument document;
document.setPatientName("Doe^John");
/ ... /
DSRDocumentTree &tree = document.getTree();
tree.addContentItem(DSRTypes::RT_isRoot, DSRTypes::VT_Container);
tree.getCurrentContentItem().setConceptName(DSRCodedEntryValue(/ some code /));
tree.addChildContentItem(DSRTypes::RT_hasObsContext, DSRTypes::VT_Code, CODE_DCM_ObserverType);
tree.getCurrentContentItem().setCodeValue(CODE_DCM_Person);
/ ... /
DcmFileFormat fileformat;
OFCondition status = document.write(*fileformat.getDataset())
if (status.good())
{
status = fileformat.saveFile("test.dcm", EXS_LittleEndianExplicit);
if (status.bad())
cerr << "Error: cannot save DICOM file (" << status.text() << ")" << endl;
} else
cerr << "Error: cannot write SR document (" << status.text() << ")" << endl;
On peut également accéder directement à de nombreuses propriétés de l'arborescence du document et les modifier, comme le montre l'exemple suivant :
DSRDocument document(DSRTypes::DT_KeyObjectSelectionDocument);
/ ... /
DSRDocumentTree &tree = document.getTree();
tree.addContentItem(DSRTypes::RT_isRoot, DSRTypes::VT_Container);
DSRCodedEntryValue *codePtr = tree.getCurrentContentItem().getConceptNamePtr();
if (codePtr != NULL)
codePtr->setCode("113000", "DCM", "Of Interest");
/ ... /
tree.addContentItem(DSRTypes::RT_contains, DSRTypes::VT_Image);
DSRImageReferenceValue *imagePtr = tree.getCurrentContentItem().getImageReferencePtr();
if (imagePtr != NULL)
{
imagePtr->setValue(DSRImageReferenceValue(UID_UltrasoundMultiframeImageStorage, / image UID /));
imagePtr->setPresentationState(DSRCompositeReferenceValue(UID_GrayscaleSoftcopyPresentationStateStorage, / GSPS UID /));
imagePtr->getFrameList().addItem(2);
imagePtr->getFrameList().addItem(5);
}
/ ... /
En outre, il existe des méthodes spécifiques DSRDocumentTree::addContentItem() et DSRDocumentTree::addChildContentItem() qui attendent un pointeur vers une instance de DSRDocumentTreeNode nouvellement créée.
Il existe plusieurs façons de parcourir une arborescence de document et d'y rechercher des éléments de contenu répondant à certains critères. En voici deux :
DSRDocument document;
/ ... /
DSRDocumentTree &tree = document.getTree();
/ #1: search for content items with a certain concept name /
if (tree.gotoNamedNode(CODE_DCM_ProcedureReported))
{
do {
const DSRDocumentTreeNode *node = tree.getNode();
/ and check for expected value type /
if ((node != NULL) && (node->getValueType() == DSRTypes::VT_Code))
{
/ do something useful with the CODE content item /
}
} while (tree.gotoNextNamedNode(CODE_DCM_ProcedureReported, OFFalse /searchIntoSub/));
}
/ #2: search for content items using a more complex filter /
DSRDocumentTreeNodeAndFilter filter;
filter.addFilter(new DSRDocumentTreeNodeValueTypeFilter(DSRTypes::VT_Code));
filter.addFilter(new DSRDocumentTreeNodeRelationshipTypeFilter(DSRTypes::RT_hasConceptMod));
filter.addFilter(new DSRDocumentTreeNodeHasChildrenFilter());
if (tree.gotoMatchingNode(filter))
{
/ found first "has concept mod CODE" content item that has children /
}
Le dernier exemple montre comment gérer les templates SR, par exemple lors de la création d'un rapport structuré DICOM basé sur le TID 1500 (Measurement Report) :
TID1500_MeasurementReport report(CMR_CID7021::ImagingMeasurementReport);
report.setLanguage(CID5000_Language::English);
report.getObservationContext().addPersonObserver("Doe^Jane", "Some Organization");
/ ... /
CMR_TID1411_in_TID1500 &volumetric = report.getVolumetricROIMeasurements();
volumetric.setActivitySession("1");
volumetric.setTrackingIdentifier("aorta reference region");
/ ... /
CMR_TID1419_in_TID1411_in_TID1500 &measurement = volumetric.getMeasurement();
measurement.createNewMeasurement(CMR_CID7469::Volume, CMR_TID1419_in_TID1411_in_TID1500::MeasurementValue("15", CMR_CID7181::CubicMillimeter));
measurement.setDerivation(CMR_CID7464::StandardDeviation);
/ ... /
DSRDocument document;
document.setPatientName("Last Name^First Name");
/ ... /
if (document.setTreeFromRootTemplate(report).good())
document.print(cout, DSRTypes::PF_printTemplateIdentification);
else
cerr << "Error: cannot set template content as document tree" << endl;