dcmsr: uma biblioteca de relatórios estruturados e aplicativos utilitários
Este módulo contém classes para ler, escrever, criar, modificar, acessar, imprimir e renderizar documentos DICOM de relatório estruturado (SR). A lista de classes SOP com suporte é fornecida em DSRTypes::E_DocumentType.
As principais classes de interface são:
- DSRDocument
- DSRDocumentTree
- DSRDocumentSubTree
- DSRDocumentTreeNode
- DSRContentItem
- DSRCodedEntryValue
Aqui estão mais algumas classes úteis para implementar templates SR:
Atualmente, os seguintes templates SR estão implementados (consulte as notas para mais detalhes):
- TID300_Measurement
- TID1001_ObservationContext
- TID1204_LanguageOfContentItemAndDescendants
- TID1411_VolumetricROIMeasurements
- TID1419_ROIMeasurements_Measurement
- TID1500_MeasurementReport
- TID1501_MeasurementGroup
- TID1600_ImageLibrary
- TID4019_AlgorithmIdentification
Ferramentas
Este módulo contém as seguintes ferramentas de linha de comando:
- dsr2html: renderiza arquivo e conjunto de dados DICOM SR para HTML/XHTML
- dsr2xml: converte arquivo e conjunto de dados DICOM SR para XML
- dsrdump: exibe arquivo e conjunto de dados DICOM SR
- xml2dsr: converte documento XML para arquivo DICOM SR
Exemplos
O exemplo a seguir mostra como carregar um relatório estruturado DICOM e renderizar seu conteúdo em formato 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;
O exemplo a seguir mostra como criar um relatório estruturado DICOM e salvá-lo em um arquivo (mais detalhes podem ser encontrados no arquivo-fonte 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;
Como alternativa, muitas propriedades da árvore do documento podem ser acessadas e modificadas diretamente, como mostra o exemplo a seguir:
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);
}
/ ... /
Além disso, existem métodos específicos DSRDocumentTree::addContentItem() e DSRDocumentTree::addChildContentItem() que esperam um ponteiro para uma instância recém-criada de DSRDocumentTreeNode.
Percorrer uma árvore de documento e buscar itens de conteúdo que atendam a determinados critérios pode ser feito de várias maneiras. Aqui estão duas delas:
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 /
}
O último exemplo mostra como lidar com templates SR, por exemplo, ao criar um relatório estruturado DICOM baseado no 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;