diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/core/mlprovider/PrepareDatastoreTask.java b/dicoogle/src/main/java/pt/ua/dicoogle/core/mlprovider/PrepareDatastoreTask.java index ce570e8c5..b6ee0e81d 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/core/mlprovider/PrepareDatastoreTask.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/core/mlprovider/PrepareDatastoreTask.java @@ -18,18 +18,28 @@ */ package pt.ua.dicoogle.core.mlprovider; +import org.dcm4che2.data.BasicDicomObject; +import org.dcm4che2.data.Tag; +import org.dcm4che2.data.TransferSyntax; +import org.dcm4che2.data.VR; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pt.ua.dicoogle.plugins.PluginController; import pt.ua.dicoogle.sdk.datastructs.SearchResult; -import pt.ua.dicoogle.sdk.mlprovider.MLDataset; -import pt.ua.dicoogle.sdk.mlprovider.MLDicomDataset; -import pt.ua.dicoogle.sdk.mlprovider.MLImageDataset; +import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; +import pt.ua.dicoogle.sdk.mlprovider.*; import pt.ua.dicoogle.server.web.dicom.ROIExtractor; import pt.ua.dicoogle.server.web.utils.cache.WSICache; -import java.util.HashMap; -import java.util.UUID; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -65,10 +75,12 @@ public MLDataset call() throws Exception { switch (request.getDataType()){ case DICOM: return new MLDicomDataset(request.getDimLevel(), request.getUids()); - case IMAGE: // Not operational - throw new UnsupportedOperationException("Datastore requests for image objects is not supported"); - /* - HashMap extraFields = new HashMap(); + case IMAGE: + //throw new UnsupportedOperationException("Datastore requests for image objects is not supported"); + + HashMap extraFields = new HashMap<>(); + + String path = this.ensureAndCreatePath(); extraFields.put("SOPInstanceUID", "SOPInstanceUID"); extraFields.put("SharedFunctionalGroupsSequence_PixelMeasuresSequence_PixelSpacing", "SharedFunctionalGroupsSequence_PixelMeasuresSequence_PixelSpacing"); @@ -80,6 +92,8 @@ public MLDataset call() throws Exception { extraFields.put("ImageType", "ImageType"); MLImageDataset mlDataset = new MLImageDataset(); + HashMap dataset = new HashMap<>(); + Set classes = new HashSet<>(); this.request.getDataset().entrySet().forEach((entry -> { try { @@ -88,18 +102,59 @@ public MLDataset call() throws Exception { Iterable results = controller .query(controller.getQueryProvidersName(true).get(0), "SOPInstanceUID:" + entry.getKey(), extraFields).get(); + int c = 0; for (SearchResult image : results) { - //List rois = (List) roiExtractor.extractROI(); + + BasicDicomObject dcm = new BasicDicomObject(); + dcm.putString(Tag.TransferSyntaxUID, VR.CS, "1.2.840.10008.1.2.4.50"); + + for(BulkAnnotation annotation: entry.getValue()){ + BufferedImage roi = roiExtractor.extractROI(image.get("SOPInstanceUID").toString(), annotation); + String roiFileName = annotation.getLabel().getName() + c++; + classes.add(annotation.getLabel().getName()); + File output = new File(path + File.separator + roiFileName + ".jpeg"); + ImageIO.write(roi, "jpeg", output); + dataset.put(new ImageEntry(dcm, output.toURI()), annotation.getLabel()); + } } - } catch (InterruptedException | ExecutionException e) { + } catch (IOException | InterruptedException | ExecutionException e) { logger.error("Error preparing datastore task", e); } })); + + mlDataset.setMultiClass(classes.size() > 2); + mlDataset.setDataset(dataset); + return mlDataset; - */ default: return null; } } + + private String ensureAndCreatePath(){ + Path datasetsFolder = Paths.get("datasets"); + // Check if the folder exists + if (!Files.exists(datasetsFolder)) { + try { + Files.createDirectories(datasetsFolder); + System.out.println("Datasets folder didn't exist, creating one."); + } catch (Exception e) { + System.err.println("Failed to create datasets folder: " + e.getMessage()); + } + } + + Path datasetFolder = Paths.get("datasets" + File.separator + System.currentTimeMillis()); + + if (!Files.exists(datasetFolder)) { + try { + Files.createDirectories(datasetFolder); + } catch (Exception e) { + System.err.println("Failed to create dataset folder: " + e.getMessage()); + } + } + + return datasetFolder.toString(); + } + } diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java index 386261078..1bfc73ff5 100755 --- a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java @@ -863,16 +863,18 @@ public Task datastore(final DatastoreRequest datasetRequest) { String uuid = UUID.randomUUID().toString(); Task prepareTask = new Task<>("MLPrepareDatastoreTask" + uuid, new PrepareDatastoreTask(this, datasetRequest)); + MLProviderInterface mlInterface = getMachineLearningProviderByName(datasetRequest.getProvider(), true); + if (mlInterface == null) { + logger.error("MLProvider with name {} not found", datasetRequest.getProvider()); + prepareTask.cancel(true); + return prepareTask; + } + prepareTask.onCompletion(() -> { - MLProviderInterface mlInterface = getMachineLearningProviderByName(datasetRequest.getProvider(), true); - if (mlInterface == null) { - logger.error("MLProvider with name {} not found", prepareTask.getName()); - } else { - try { - mlInterface.dataStore(prepareTask.get()); - } catch (InterruptedException | ExecutionException e) { - logger.error("Task {} failed execution", prepareTask.getName(), e); - } + try { + mlInterface.dataStore(prepareTask.get()); + } catch (InterruptedException | ExecutionException e) { + logger.error("Task {} failed execution", prepareTask.getName(), e); } }); logger.debug("Fired prepare dataset task with uuid {}", uuid); diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/BulkAnnotation.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/BulkAnnotation.java index b6f108ff0..3ab7792f2 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/BulkAnnotation.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/BulkAnnotation.java @@ -18,6 +18,8 @@ */ package pt.ua.dicoogle.sdk.datastructs.dim; +import pt.ua.dicoogle.sdk.mlprovider.MLlabel; + import java.util.Arrays; import java.util.List; @@ -69,7 +71,7 @@ public enum CoordinateType { private AnnotationType annotationType; - private String label; + private MLlabel label; private List points; @@ -99,11 +101,11 @@ public void setAnnotationType(AnnotationType annotationType) { this.annotationType = annotationType; } - public String getLabel() { + public MLlabel getLabel() { return label; } - public void setLabel(String label) { + public void setLabel(MLlabel label) { this.label = label; } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java index f70883720..daab4948a 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java @@ -50,9 +50,9 @@ private FileType(String s, String mimeType) { private double y; - private int width; + private double width; - private int height; + private double height; private String sopInstanceUID; @@ -67,6 +67,14 @@ public ImageROI(String sopInstanceUID, int x, int y, int width, int height) { this.height = height; } + public ImageROI(String sopInstanceUID, double x, double y, double width, double height, URI roi) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.uriROI = roi; + } + public double getX() { return x; } @@ -91,7 +99,7 @@ public void setSopInstanceUID(String sopInstanceUID) { this.sopInstanceUID = sopInstanceUID; } - public int getWidth() { + public double getWidth() { return width; } @@ -99,7 +107,7 @@ public void setWidth(int width) { this.width = width; } - public int getHeight() { + public double getHeight() { return height; } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/ImageEntry.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/ImageEntry.java new file mode 100644 index 000000000..816aa5637 --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/ImageEntry.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/ + * + * This file is part of Dicoogle/dicoogle-sdk. + * + * Dicoogle/dicoogle-sdk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dicoogle/dicoogle-sdk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dicoogle. If not, see . + */ +package pt.ua.dicoogle.sdk.mlprovider; + +import org.dcm4che2.data.DicomObject; + +import java.net.URI; +import java.util.Objects; + +/** + * Used to map the metadata of regions of interest. + */ +public class ImageEntry { + + private DicomObject object; + + private URI file; + + public ImageEntry(DicomObject object, URI file) { + this.object = object; + this.file = file; + } + + public DicomObject getObject() { + return object; + } + + public void setObject(DicomObject object) { + this.object = object; + } + + public URI getFile() { + return file; + } + + public void setFile(URI file) { + this.file = file; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImageEntry that = (ImageEntry) o; + return object.equals(that.object) && file.equals(that.file); + } + + @Override + public int hashCode() { + return Objects.hash(file); + } +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLCSVDataset.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLCSVDataset.java index 634dcf71a..50b657542 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLCSVDataset.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLCSVDataset.java @@ -24,4 +24,7 @@ public class MLCSVDataset extends MLDataset { private InputStream csvFile; + public MLCSVDataset(){ + super("", MLDataType.CSV); + } } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDataset.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDataset.java index 53104b9e7..3958f476f 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDataset.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDataset.java @@ -22,4 +22,26 @@ public abstract class MLDataset { protected String name; + protected MLDataType dataType; + + public MLDataset(String name, MLDataType dataType) { + this.name = name; + this.dataType = dataType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public MLDataType getDataType() { + return dataType; + } + + public void setDataType(MLDataType dataType) { + this.dataType = dataType; + } } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDicomDataset.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDicomDataset.java index 3b511957e..8b7d69b88 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDicomDataset.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLDicomDataset.java @@ -32,11 +32,13 @@ public class MLDicomDataset extends MLDataset { private List dimUIDs; public MLDicomDataset(DimLevel level){ + super("", MLDataType.DICOM); this.level = level; dimUIDs = new ArrayList<>(); } public MLDicomDataset(DimLevel level, List dimUIDs){ + super("", MLDataType.DICOM); this.level = level; this.dimUIDs = dimUIDs; } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLImageDataset.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLImageDataset.java index 555182b38..272f61561 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLImageDataset.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLImageDataset.java @@ -18,9 +18,7 @@ */ package pt.ua.dicoogle.sdk.mlprovider; -import java.net.URI; import java.util.HashMap; -import java.util.List; /** * An ML dataset of image objects. @@ -28,15 +26,24 @@ */ public class MLImageDataset extends MLDataset { - private HashMap> dataset; + private HashMap dataset; private boolean multiClass; - public HashMap> getDataset() { + public MLImageDataset() { + super("name", MLDataType.IMAGE); + } + + public MLImageDataset(HashMap dataset) { + super("", MLDataType.IMAGE); + this.dataset = dataset; + } + + public HashMap getDataset() { return dataset; } - public void setDataset(HashMap> dataset) { + public void setDataset(HashMap dataset) { this.dataset = dataset; } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLlabel.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLlabel.java index 1ba80ea6f..a20afa4dc 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLlabel.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/mlprovider/MLlabel.java @@ -38,7 +38,7 @@ public enum CodingSchemeDesignator{ /** * DICOM Segment Label (0062, 0005) is a user defined label. */ - private String label; + private String name; /** * DICOM Segment Description (0062, 0007) is a user defined description. @@ -67,8 +67,7 @@ public enum CodingSchemeDesignator{ */ private CodingSchemeDesignator codingSchemeDesignator; - public MLlabel(String label) { - this.label = label; + public MLlabel(){ this.description = "unknown"; this.codingSchemeDesignator = CodingSchemeDesignator.DCM; this.codeValue = "333333"; @@ -76,12 +75,17 @@ public MLlabel(String label) { this.color = "#000000"; } - public String getLabel() { - return label; + public MLlabel(String name) { + this(); + this.name = name; } - public void setLabel(String label) { - this.label = label; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; } public String getDescription() { @@ -129,16 +133,16 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MLlabel mLlabel = (MLlabel) o; - return label.equals(mLlabel.label); + return name.equals(mLlabel.name); } @Override public int hashCode() { - return Objects.hash(label); + return Objects.hash(name); } @Override public int compareTo(MLlabel o) { - return o.getLabel().compareTo(this.getLabel()); + return o.getName().compareTo(this.getName()); } }