package edu.stanford.rsl.tutorial.mammography.inbreast; import ij.IJ; import ij.ImageJ; import ij.ImagePlus; import ij.gui.Roi; import ij.plugin.frame.RoiManager; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import javax.xml.xpath.XPathExpressionException; import edu.stanford.rsl.conrad.data.numeric.Grid2D; import edu.stanford.rsl.conrad.utils.ImageUtil; import edu.stanford.rsl.tutorial.mammography.Mammogram; import edu.stanford.rsl.tutorial.mammography.Mammogram.BreastDensityACR; import edu.stanford.rsl.tutorial.mammography.Mammogram.Findings; import edu.stanford.rsl.tutorial.mammography.Mammogram.MammographyView; import edu.stanford.rsl.tutorial.mammography.Mammogram.SideOfBody; public class ReadMammograms { private SideOfBody readOnlySides = SideOfBody.Both; private MammographyView readOnlyViews = MammographyView.Both; private Findings readOnlyKind = Findings.Mass; private ArrayList<Integer> queriedPatiendIds = new ArrayList<Integer>(); private ArrayList<Integer> patientID = new ArrayList<Integer>(); private ArrayList<SideOfBody> side = new ArrayList<SideOfBody>(); private ArrayList<MammographyView> view = new ArrayList<MammographyView>(); private ArrayList<BreastDensityACR> density = new ArrayList<BreastDensityACR>(); private ArrayList<Integer> birads = new ArrayList<Integer>(); private ArrayList<ArrayList<Findings>> findings = new ArrayList<ArrayList<Findings>>(); private ArrayList<ArrayList<Roi>> roi = new ArrayList<ArrayList<Roi>>(); private File[] listOfFiles = null; private ArrayList<Mammogram> mammograms = new ArrayList<Mammogram>(); private int mammoCounter = 0; //set path to Dicoms, XML and CSV file private String imgDir = null; private String xmlDir = null; private String csvFile = null; private boolean initialized = false; // flag for single access operations public static void main(String[] args) { String imgDir = "G:/INbreast/DataExtracted/AllDICOMs/"; String xmlDir = "G:/INbreast/DataExtracted/ALLXML/"; String csvFile = "G:/INbreast/DataExtracted/INbreast.csv"; ReadMammograms reader = new ReadMammograms(imgDir, xmlDir, csvFile); reader.setOptions(SideOfBody.Both, MammographyView.Both, Findings.All); reader.readImagesSuccessively(); new ImageJ(); String selectedImage = imgDir+"22671003_f571fd4e63c718e3_MG_L_ML_ANON.dcm"; reader.showMammogram(selectedImage, true); // Mammogram m = reader.readNextMammogram(); // while (m != null) { // // here you can do cool stuff with the images // RoiManager manager = new RoiManager(); // m = reader.readNextMammogram(); // ImagePlus imp = ImageUtil.wrapGrid(m.getImage(), "Mammogram"); // ArrayList<Roi> rois = m.getRois(); // for(int i = 0; i < rois.size(); i++){ // manager.add(imp, rois.get(i), i+1); // } // imp.show(); // manager.runCommand("Show All"); // // manager.close(); // imp.close(); // } System.out.println("Done."); } /** * prepares access to whole mammo database */ public void initMammoReading(){ this.readCSVdata(); initialized = true; } /** * prepares access to whole mammo database with prior option selection */ public void initMammoReadingHelper(){ this.setOptions(SideOfBody.Both, MammographyView.Both, Findings.All); this.readImagesSuccessively(); initialized = true; } /** * setOptions sets witch parameters will be accepted * * @param sob SideOfBody * @param vws MammographyView * @param finding Findings */ public void setOptions(SideOfBody sob, MammographyView vws, Findings finding) { this.readOnlySides = sob; this.readOnlyViews = vws; this.readOnlyKind = finding; } /** * deprecated * all CSVdata will be read * all mammograms who satisfy setOptions will be read * a new mammogram constructor will be set for every suitable mammogram */ public void readImages() { readCSVdata(); readMammograms(); } /** * all CSVdata will be read * all mammograms who satisfy setOptions will be read, to start the constructor * readNextMammogram has to be used */ public void readImagesSuccessively() { readCSVdata(); prepReadMammograms(); } public ArrayList<Mammogram> getMammograms() { return mammograms; } public void setMammograms(ArrayList<Mammogram> mammograms) { this.mammograms = mammograms; } /* * deprecated */ private void readMammograms() { //the path of imageFolder is given to listOfFiles, all DICOM images are now listed File folder = new File(imgDir); listOfFiles = folder.listFiles(); //pid = PatiendtID ArrayList<Integer> pid = new ArrayList<Integer>(); for (int i = 0; i < listOfFiles.length; i++) { //fn = filename, consist of 6 Elements, separated by a "_" String fn = listOfFiles[i].getName(); if (fn.contains(".dcm")) { String[] identifiers = fn.split("_"); pid.add(Integer.parseInt(identifiers[0])); } } //calls method getQueriedPatientIds= only patients/images //satisfying wanted SideOfBody,MammographyView and Findings are returned queriedPatiendIds = getQueriedPatientIds(pid); System.out.println("Reading " + String.valueOf(queriedPatiendIds.size()) + " mammograms."); //all filenames will be read and checked if DICOM for (int i = 0; i < listOfFiles.length; i++) { String fn = listOfFiles[i].getName(); if (fn.contains(".dcm")) { //separate patientID from filename, by splitting String[] identifiers = listOfFiles[i].getName().split("_"); int patientId = Integer.parseInt(identifiers[0]); // check if the patientID from the i-th file matches one of the queriedPatiendIds // returns positiv integer if found, -1 if not found int inList = getPatiendIdx(patientId, queriedPatiendIds); if (inList >= 0) { // get actual index from the list of all collected patientID int refIdx = getPatiendIdx(patientId, this.patientID); //load the actual image as Grid2D ImagePlus imgAsImp = IJ.openImage(listOfFiles[i].getAbsolutePath()); Grid2D img = ImageUtil.wrapImageProcessor(imgAsImp.getProcessor()); //collect all information of the Object of refIdx and start a constructor Mammogram mammo = new Mammogram(img, side.get(refIdx), view.get(refIdx), density.get(refIdx), findings.get(refIdx), roi.get(refIdx), birads.get(refIdx)); //adding this mammogram to ArrayList this.mammograms.add(mammo); } } } System.out.println("ReadMammograms - done."); } private void prepReadMammograms() { //the path of imageFolder is given to listOfFiles, all DICOM images are now listed File folder = new File(imgDir); listOfFiles = folder.listFiles(); prepReadMammograms(listOfFiles); } private void prepReadMammograms(File[] customListOfFiles){ this.mammoCounter = 0; listOfFiles = customListOfFiles; //pid = PatiendtID ArrayList<Integer> pid = new ArrayList<Integer>(); for (int i = 0; i < listOfFiles.length; i++) { //fn = filename, consist of 6 Elements, separated by a "_" String fn = listOfFiles[i].getName(); if (fn.contains(".dcm")) { String[] identifiers = fn.split("_"); pid.add(Integer.parseInt(identifiers[0])); } } //calls method getQueriedPatientIds= only patients/images //satisfying wanted SideOfBody,MammographyView and Findings are returned queriedPatiendIds = getQueriedPatientIds(pid); } public Mammogram readNextMammogram() { // with every checked mammogram, mammoCounter increases while (mammoCounter < listOfFiles.length) { String fn = listOfFiles[mammoCounter].getName(); // check if file is a DICOM, if not, mammoCounter++ if (fn.contains(".dcm")) { // get the name of the file in identifiers[0] , rest is also stored but will not be used String[] identifiers = listOfFiles[mammoCounter].getName().split("_"); int patientId = Integer.parseInt(identifiers[0]); // check if this patientID of matches one of the queriedPatiendIds int inList = getPatiendIdx(patientId, queriedPatiendIds); // if patientId matches one of the queriedIds an positive integer will be returned // else -1 will be returned and mammoCounter++ will be executed if (inList >= 0) { // get actual index of all stored patients int refIdx = getPatiendIdx(patientId, this.patientID); ImagePlus imgAsImp = IJ.openImage(listOfFiles[mammoCounter].getAbsolutePath()); Grid2D img = ImageUtil.wrapImageProcessor(imgAsImp.getProcessor()); //start the constructor Mammogram mammo = new Mammogram(img, side.get(refIdx), view.get(refIdx), density.get(refIdx), findings.get(refIdx), roi.get(refIdx), birads.get(refIdx)); mammoCounter++; return mammo; } else { mammoCounter++; } } else { mammoCounter++; } } return null; } /** * * @param listOfPatients = all collected patients * * @return ArrayList<Integer> containing patientIDs of images satisfying wanted SideOfBody, MammographyView and Findings * */ private ArrayList<Integer> getQueriedPatientIds(ArrayList<Integer> listOfPatients) { //list is the return value, will be filled later ArrayList<Integer> list = new ArrayList<Integer>(); // the i-th element of patientID (all 410 patients) will be compared to // the id-th element of listOfPatients () for (int i = 0; i < patientID.size(); i++) { boolean isInList = false; for (int id = 0; id < listOfPatients.size(); id++) { if (patientID.get(i).equals(listOfPatients.get(id))) { isInList = true; continue; } } if (!isInList) { continue; } // read out, witch SideOfBody do we currently want to look up String[] sides = readOnlySides.getValue(); boolean isCorrectSide = false; for (int s = 0; s < sides.length; s++) { // compare element i with the elements of SideOfBody if (sides[s].equals(side.get(i).getValue()[0])) { isCorrectSide = true; } } if (!isCorrectSide) { continue; } // read out, witch MammographyViews do we currently want to look up String[] views = readOnlyViews.getValue(); boolean isCorrectView = false; for (int s = 0; s < views.length; s++) { // compare element i with the elements of the MammographyView if (views[s].equals(view.get(i).getValue()[0])) { isCorrectView = true; } } if (!isCorrectView) { continue; } // read out, witch Finding do we currently want to look up String[] kind = readOnlyKind.getValue(); ArrayList<Findings> fndgInMammoI = findings.get(i); boolean isCorrectFinding = false; for (int s = 0; s < kind.length; s++) { //compare element i with the elements of Findings for (Findings f : fndgInMammoI) { if (kind[s].equals(f.getValue()[0])) { isCorrectFinding = true; } } } if (!isCorrectFinding) { continue; } // if SideOfBody, MammographyView and Findings were matching out specification // the matching patientID will now be stored in "list" list.add(patientID.get(i)); } return list; } /** * * @param id = patientID which could be in the idList * @param idList = all patientIDs that have been previously collected * @return index i from idList if found , returns -1 if not found */ private int getPatiendIdx(int id, ArrayList<Integer> idList) { for (int i = 0; i < idList.size(); i++) { if (idList.get(i).equals(id)) return i; } return -1; } /** * the provided CSVdate will be read */ private void readCSVdata() { System.out.println("Parsing CSV file."); BufferedReader br = null; String line = ""; String cvsSplitBy = ";"; try { //read the csvFile br = new BufferedReader(new FileReader(csvFile)); int count = -1; while ((line = br.readLine()) != null) { count++; //first line of csvFile: Patient; Patient age; Laterality; View; Acquisition date; File Name; ACR; Bi-Rads //ignore first line, it has only labels no, patient information if (count == 0) continue; char lastCh = line.charAt(line.length() - 1); while (lastCh == ' ') { line = line.substring(0, line.length() - 1); lastCh = line.charAt(line.length() - 1); } if (line.charAt(line.length() - 1) == ';') { line = line.substring(0, line.length() - 1); } // use separator to split line // entry containes all information of removed, removed, SideOfBody, MammographyView, Date, FileName, ACR , Bi-Rads String[] entry = line.split(cvsSplitBy); // parse Side Of Body if (entry[2].equals(SideOfBody.Left.getValue()[0])) { this.side.add(SideOfBody.Left); } else if (entry[2].equals(SideOfBody.Right.getValue()[0])) { this.side.add(SideOfBody.Right); } else { this.side.add(SideOfBody.Unknown); } // parse View if (entry[3].equals(MammographyView.MLO.getValue()[0])) { this.view.add(MammographyView.MLO); } else if (entry[3].equals(MammographyView.CC.getValue()[0])) { this.view.add(MammographyView.CC); } else { this.view.add(MammographyView.Unknown); } // parse Patient ID int patient = Integer.parseInt(entry[5]); this.patientID.add(patient); // parse ACR int acr = -1; if (entry.length > 6 && !entry[6].contains(" ")) { //extract acr score and compare to the values denoted in Mammogram.java acr = Integer.parseInt(entry[6]); } if (acr == BreastDensityACR.Fatty.getACRValue()) { this.density.add(BreastDensityACR.Fatty); } else if (acr == BreastDensityACR.FattyGlandular1.getACRValue()) { this.density.add(BreastDensityACR.FattyGlandular1); } else if (acr == BreastDensityACR.FattyGlandular2.getACRValue()) { this.density.add(BreastDensityACR.FattyGlandular2); } else if (acr == BreastDensityACR.Glandular.getACRValue()) { this.density.add(BreastDensityACR.Glandular); } else { this.density.add(BreastDensityACR.Unknown); } // parse Birads int biradsScore = 0; if (entry.length > 7) { biradsScore = Integer.parseInt(entry[7].substring(0, 1)); } this.birads.add(biradsScore); //finding with their corresponding rois are stored ArrayList<Findings> fndgs = new ArrayList<Findings>(); ArrayList<Roi> rois = new ArrayList<Roi>(); String[] rawOutput = null; // rawOutput contains: // [0]total number of findings // [1]class of nth finding // [2]number of XY coordinates // [3...] XY coordinates pairs as an array // [1]to[3..]repeat themself for every finding // health breast have a biradsScore of 1 if (biradsScore != 1) { XMLparser parser = new XMLparser(); try{ parser.readXml(xmlDir + entry[5] + ".xml"); // entry[5] contains the name of the .xml file // value is of ArrayList<String> fndgs.addAll(parser.getFindings()); // Roi.Polygon => X/Y coordinates, width & height denotes // the total XY variance rois.addAll(parser.getRois()); this.findings.add(fndgs); this.roi.add(rois); } catch (XPathExpressionException XPathExpExc){ System.err.println("Error when parsing XML, maybe no internet connection. Findings and RoIs could not be added to Mammogram object."); } catch (FileNotFoundException e) { System.err.println("XML file not found, maybe wrong filename. Findings and RoIs could not be added to Mammogram object."); } } else { //findings is ArrayList of ArrayList of String ArrayList<Findings> healthy = new ArrayList<Findings>(); healthy.add(Findings.Healthy); this.findings.add(healthy); this.roi.add(new ArrayList<Roi>()); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); // } catch (XPathExpressionException e) { // // TODO Auto-generated catch block // e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("ReadCSVdata - Done."); } /** * /path to scvFile and imgFolder are setted * @param imgFolder * @param csvF */ public ReadMammograms(String imgFolder, String xmlDir, String csvF) { this.imgDir = imgFolder; this.xmlDir = xmlDir; this.csvFile = csvF; } /** * prepares access to whole database * @param name : string containing 8-digit number of mammo case (INbreast only) * @param showRois : set true to show RoIs */ public void showMammogram(String name,boolean showRois){ if (!initialized) { initMammoReadingHelper(); } prepReadMammograms(new File[] { new File(name) }); RoiManager manager = new RoiManager(); Mammogram m = this.readNextMammogram(); ImagePlus imp = ImageUtil.wrapGrid(m.getImage(), "Mammogram"); ArrayList<Roi> rois = m.getRois(); for(int i = 0; i < rois.size(); i++){ manager.add(imp, rois.get(i), i+1); } imp.show(); manager.runCommand("Show All"); // manager.close(); // imp.close(); } }