I would like that, too.
For some other time, yet...
Anyway, this is the (almost) minimal code.
Code: Select all
#include <dcmtk/config/osconfig.h>
#include <dcmtk/ofstd/ofstd.h>
#include <dcmtk/dcmdata/dcuid.h>
#include <dcmtk/dcmdata/dcfilefo.h>
#include <dcmtk/dcmsr/dsrdoc.h>
#include <dcmtk/dcmsr/dsrtypes.h>
#include <dcmtk/dcmsr/dsrsoprf.h>
#include <dcmtk/dcmsr/dsrmamcc.h>
#include <dcmtk/dcmsr/dsrscovl.h>
#include <stdlib.h>
#include <strstream>
#include <stdio.h>
#include <string>
#include <iostream>
#include <math.h>
using std::iostream;
using std::ifstream;
using namespace std;
int main(int argc,char **argv) {
char fnm[130];
int hhh, i,j ,k,dix;
size_t im_id[20];
int cad_status;
bool ex_ImageLaterality[20];
DcmFileFormat fileformat;
DcmFileFormat fform[20]; /// these are the dicom image files to be opened
OFString patientsName, patientID, patientsBirthDate, patientsSex;
OFString studyID, studyDate, studyTime;
OFString seriesNumber[20], instanceNumber[20], seriesInstanceUID[20];
OFString imageSOPClassUID[20], actualSOPInstanceUID[20];
OFString imageLaterality[20];
cad_status=1; /// this will mean CAD found smthg
for (dix=0;dix<4;dix++) { /// there are four images
sprintf(fnm,"%d",dix+1); /// filenames of the images are 1, 2, 3, 4...
OFCondition status = fform[dix].loadFile(fnm);
if (status.good())
{
// OFString patientsName;
if (fform[dix].getDataset()->findAndGetOFString(DCM_PatientsName, patientsName).good())
{
fform[dix].getDataset()->findAndGetOFString(DCM_StudyID,studyID);
// fform[dix].getDataset()->findAndGetOFString(DCM_SeriesID,seriesID);
fform[dix].getDataset()->findAndGetOFString(DCM_PatientID,patientID);
fform[dix].getDataset()->findAndGetOFString(DcmTag(0x0010, 0x0030),patientsBirthDate, 0, false);
fform[dix].getDataset()->findAndGetOFString(DCM_PatientsSex,patientsSex);
fform[dix].getDataset()->findAndGetOFString(DCM_StudyDate,studyDate); /// not used!
fform[dix].getDataset()->findAndGetOFString(DCM_StudyTime,studyTime); /// not used!
fform[dix].getDataset()->findAndGetOFString(DCM_SeriesNumber,seriesNumber[dix]);
fform[dix].getDataset()->findAndGetOFString(DCM_InstanceNumber,instanceNumber[dix]);
fform[dix].getDataset()->findAndGetOFString(DCM_SeriesInstanceUID,seriesInstanceUID[dix]);
fform[dix].getDataset()->findAndGetOFString(DCM_SOPClassUID,imageSOPClassUID[dix]);
if (imageSOPClassUID[dix]=="1.2.840.10008.5.1.4.1.1.1.2.1") { /// the image is for processing
fform[dix].getDataset()->findAndGetOFString(DCM_SOPInstanceUID, actualSOPInstanceUID[dix]);
}
else { /// the image is for presentation
fform[dix].getDataset()->findAndGetOFString(DcmTag(0x0008, 0x1155),actualSOPInstanceUID[dix], 0, true);
}
//// image values
if (fform[dix].getDataset()->findAndGetOFString(DcmTag(0x0020, 0x0062),imageLaterality[dix], 0, true)==EC_Normal) {
ex_ImageLaterality[dix]=true;
cout << "img inst UID " << actualSOPInstanceUID[dix] << endl;
}
else ex_ImageLaterality[dix]=false;
}
else cerr << "Error: cannot access Patient's Name!" << endl;
}
else cerr << "Error: cannot read DICOM file (" << status.text() << ")" << endl;
}
///////////////
DSRDocument *doc = new DSRDocument();
if (doc != NULL) {
OFString studyUID_01;
OFBool writeFile = OFTrue;
doc->createNewDocument(DSRTypes::DT_MammographyCadSR);
doc->getStudyInstanceUID(studyUID_01);
doc->setStudyID(studyID);
doc->setStudyDescription("Test Mammography CAD Reporting Template");
doc->setSeriesDescription("My Dicom SR test");
doc->setSeriesNumber(seriesNumber[0]);
doc->setSpecificCharacterSetType(DSRTypes::CS_Latin1);
doc->setPatientID(patientID);
doc->setPatientsName(patientsName);
doc->setPatientsBirthDate(patientsBirthDate);
doc->setPatientsSex(patientsSex);
//////////
//// the next lines set on container's root node (with value, scheme, meaning)
doc->getTree().addContentItem(DSRTypes::RT_isRoot, DSRTypes::VT_Container); /// this is node 1 (document root)
doc->getTree().getCurrentContentItem().setTemplateIdentification("4000","DCMR");
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111036", "DCM", "Mammography CAD Report"));
//// setting document language (EN_US)
{
doc->getTree().addContentItem(DSRTypes::RT_hasConceptMod, DSRTypes::VT_Code, DSRTypes::AM_belowCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("121049", "DCM", "Language of Content Item and Descendants"));
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("eng","ISO639_2","English"));
doc->getTree().addContentItem(DSRTypes::RT_hasConceptMod, DSRTypes::VT_Code, DSRTypes::AM_belowCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("121046", "DCM", "Country of Language"));
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("US","ISO3166_1","UNITED STATES"));
doc->getTree().goUp();
}
//// end setting document language (EN_US)
//// now inserting the images
{
doc->getTree().addContentItem(DSRTypes::RT_contains, DSRTypes::VT_Container); /// this is node 1.2
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111028", "DCM", "Image library"));
for(dix=0;dix<4;dix++) { /// still 4 images to add...
im_id[dix] = doc->getTree().addContentItem(DSRTypes::RT_contains, DSRTypes::VT_Image, DSRTypes::AM_belowCurrent); /// these are nodes 1.2.*
cout << "im_id is " << im_id[dix] << endl;
doc->getTree().getCurrentContentItem().setImageReference(DSRImageReferenceValue(UID_DigitalMammographyXRayImageStorageForProcessing, actualSOPInstanceUID[dix]));
doc->getCurrentRequestedProcedureEvidence().addItem(studyID, seriesInstanceUID[dix], UID_DigitalMammographyXRayImageStorageForProcessing, actualSOPInstanceUID[dix]);
if (ex_ImageLaterality[dix]) { /// this part concerns image laterality (R/L/Unknown)
//cout << "Writing laterality\n";
doc->getTree().addContentItem(DSRTypes::RT_hasAcqContext, DSRTypes::VT_Code, DSRTypes::AM_belowCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111027", "DCM", "Image Laterality"));
if (imageLaterality[dix]=='R') doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("T-04020","SNM3","Right breast"));
else if (imageLaterality[dix]=='L') doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("T-04030","SNM3","Left breast"));
else doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("T-Rex","SNM3","Unknown breast"));
doc->getTree().goUp();
}
doc->getTree().goUp();
}
}
//// end inserting the images
//// inserting CAD findings general report
doc->getTree().addContentItem(DSRTypes::RT_contains, DSRTypes::VT_Code, DSRTypes::AM_afterCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111017", "DCM", "CAD Processing and Findings Summary")); /// this is node 1.3
switch (cad_status) { /// well, if there's really something in that image...
case 0:
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("111241","DCM","All algorithms succeeded; without findings"));
break;
case 1:
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("111242","DCM","All algorithms succeeded; with findings"));
break;
default:
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("111245","DCM","No algorithms succeeded; without findings"));
break;
}
//// inserting some findings
doc->getTree().addContentItem(DSRTypes::RT_inferredFrom, DSRTypes::VT_Container, DSRTypes::AM_belowCurrent); /// this is node 1.3.1
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111034", "DCM", "Individual Impression/Recommendation"));
doc->getTree().addContentItem(DSRTypes::RT_hasConceptMod, DSRTypes::VT_Code, DSRTypes::AM_belowCurrent); /// this is node 1.3.1.1
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111056", "DCM", "Rendering Intent"));
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("111150","DCM","Presentation Required: Rendering device is expected to present"));
doc->getTree().addContentItem(DSRTypes::RT_contains, DSRTypes::VT_Code, DSRTypes::AM_afterCurrent); /// this is node 1.3.1.2
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111059", "DCM", "Single Image Finding"));
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("F-01796","SRT", "1.1","Mammography breast density"));
doc->getTree().addContentItem(DSRTypes::RT_hasConceptMod, DSRTypes::VT_Code, DSRTypes::AM_belowCurrent); /// this is node 1.3.1.2.1
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111056", "DCM", "Rendering Intent"));
doc->getTree().getCurrentContentItem().setCodeValue(DSRCodedEntryValue("111150","DCM","Presentation Required: Rendering device is expected to present"));
{ /// algorithm presentation
doc->getTree().addContentItem(DSRTypes::RT_hasProperties, DSRTypes::VT_Text, DSRTypes::AM_afterCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111001", "DCM", "Algorithm Name"));
doc->getTree().getCurrentContentItem().setStringValue("My_CAD");
doc->getTree().addContentItem(DSRTypes::RT_hasProperties, DSRTypes::VT_Text, DSRTypes::AM_afterCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111003", "DCM", "Algorithm Version"));
doc->getTree().getCurrentContentItem().setStringValue("1.0.0");
doc->getTree().addContentItem(DSRTypes::RT_hasProperties, DSRTypes::VT_Text, DSRTypes::AM_afterCurrent);
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111002", "DCM", "Algorithm Parameters"));
doc->getTree().getCurrentContentItem().setStringValue("none");
}
{ /// finally, adding the point...
doc->getTree().addContentItem(DSRTypes::RT_hasProperties, DSRTypes::VT_SCoord); /// this is node 1.3.1.2.5
cout << "added code " << doc->getTree().addByReferenceRelationship(DSRTypes::RT_selectedFrom, im_id[3]) << endl;
doc->getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue("111010", "DCM", "Center"));
DSRSpatialCoordinatesValue spatialCoord(DSRTypes::GT_Point);
spatialCoord.getGraphicDataList().addItem(2200,3000);
doc->getTree().getCurrentContentItem().setSpatialCoordinates(spatialCoord);
}
DcmFileFormat *newfileformat = new DcmFileFormat();
OFCondition status = doc->write(*newfileformat->getDataset());
status = newfileformat->saveFile("SRF", EXS_LittleEndianExplicit);
if (status.bad())
cerr << "Can't save SR file";
}
else cout << "Something went REALLY wrong, you were not supposed to read this\n";
k = 0;
return k;
}
(of course, if saved as sr_gen.cpp). The output is the file SRF containing a microcalcification point for the 4-th image. At this moment it's really scratchy, but is still a fair startpoint.