Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Machine learning provider interface #631

Open
wants to merge 53 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
68a8a69
Implemented skeleton of MLProvider Interface and Servlets
Rui-Jesus Jan 7, 2022
7a0317c
Started implementation of build dataset endpoint
Rui-Jesus Feb 14, 2022
27146b6
Added missing contributes
Rui-Jesus Feb 14, 2022
c8ee2a8
Added new ImageWorkerInterface
Rui-Jesus Feb 20, 2022
091aee5
Implemented new ImageWorker interface
Rui-Jesus May 1, 2022
2a2eebc
Removed image worker interface.
Rui-Jesus Nov 30, 2022
8ca7bf8
Added new ImageWorkerInterface
Rui-Jesus Feb 20, 2022
8c391d4
Implemented new ImageWorker interface
Rui-Jesus May 1, 2022
b435067
Added ImageWorkerInterface to PlatformInterface
Rui-Jesus Jun 20, 2022
4dec4ec
Changes to ImageROI and ImageWorker interfaces
Rui-Jesus Jul 1, 2022
52d1643
Removed unecessary dependency
Rui-Jesus Jul 2, 2022
bb21bf4
Removed Image worker interface. Introduced instead new module to extr…
Rui-Jesus Nov 18, 2022
0d87c2c
Baseline ROI extractor servlet. Fully removed ImageWorkerInterface.
Rui-Jesus Nov 18, 2022
268d7d3
Fixed compilation issues.
Rui-Jesus Nov 23, 2022
6633ada
Finished implementation of ROI extractor. Added cache to make it faster.
Rui-Jesus Nov 26, 2022
863dbd8
Moved some code into roi extractor.
Rui-Jesus Nov 27, 2022
969beb0
Removed unused import.
Rui-Jesus Nov 30, 2022
9d09bd6
Started implementation of makePrediction endpoint.
Rui-Jesus Nov 30, 2022
5afed6a
Finished baseline makePrediction endpoint.
Rui-Jesus Dec 2, 2022
eaf0de6
Adjustments to makePrediction endpoint.
Rui-Jesus Dec 2, 2022
bf4b495
Fixed a bug in roi extractor.
Rui-Jesus Dec 2, 2022
62f523a
Added missing dependency. Removed duplicated dependencies.
Rui-Jesus Dec 11, 2022
d707dc1
Fixed potential issue with dicoogle not initializing when a dicom pro…
Rui-Jesus Dec 13, 2022
bba57d2
Fixed bug in annotation coordinate rescaling
Rui-Jesus Dec 14, 2022
292e679
ROI extractor now supports other shapes such as polygons and ellipses
Rui-Jesus Dec 29, 2022
b11cfcb
Fixed issues with ellipse ROIs. Some code improvements.
Rui-Jesus Jan 6, 2023
e9ae2d6
Removed problematic import
Rui-Jesus Jan 6, 2023
902fcb0
Fixed some compilation issues
Rui-Jesus May 9, 2023
7d431e7
Implemented list models endpoint. Changed make prediction endpoint to…
Rui-Jesus May 30, 2023
3a7b451
Added new train model signature do provider interface. Renamed create…
Rui-Jesus Jun 2, 2023
94ed3d9
Improvements to datastore endpoint. New Train endpoint.
Rui-Jesus Jun 7, 2023
14d167c
New endpoints implemented. Code improvements.
Rui-Jesus Jun 14, 2023
90b2631
Re-organization of code to remove dcm4che3 dependency from sdk.
Rui-Jesus Jun 15, 2023
2f9c929
Removed unecessary interfaces from MLProvider. Infer endpoint can now…
Rui-Jesus Jun 20, 2023
a8671dc
Changed type of DICOM SEG. Improved make prediction servlet
Rui-Jesus Jun 28, 2023
4e300a8
Applied code review
Rui-Jesus Jul 10, 2023
ce01b7c
New isAvailable method for mlprovider plugins. MLlabel.java extended …
Rui-Jesus Jul 17, 2023
eaa34c3
Code cleanup. More documentation added.
Rui-Jesus Jul 19, 2023
ad5f970
Code cleanup. More documentation added.
Rui-Jesus Jul 19, 2023
22cd7fb
Updated API docs with new endpoints
Rui-Jesus Jul 19, 2023
6648cb0
Applied code review suggestions
Rui-Jesus Jul 20, 2023
da55375
Added license headers
Rui-Jesus Jul 25, 2023
44c63f9
Apply suggestions from code review
Rui-Jesus Jul 31, 2023
667b971
Added documentation. Fixed issue with previous commit
Rui-Jesus Jul 31, 2023
8a217cb
Updated version
Rui-Jesus Jul 31, 2023
d5bf8c6
Some corrections to MLInference.java
Rui-Jesus Nov 23, 2023
0cd1d9b
Added capacity to define model parameters.
Rui-Jesus Dec 11, 2023
dcb1667
Started implementation of labeled datastore endpoint. Improvements to…
Rui-Jesus Feb 23, 2024
56a8fd2
Added new model parameter to allow the specification of a preferred m…
Rui-Jesus Feb 26, 2024
ee80127
Reworked bulk annotation to be compliant with the standard. Applied c…
Rui-Jesus Mar 2, 2024
9ade42e
Implemented new getAvailableMethods and cache endpoints.
Rui-Jesus May 25, 2024
2115a69
Added missing licenses.
Rui-Jesus May 25, 2024
14470f6
Added missing servlets and API documentation.
Rui-Jesus Jun 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions dicoogle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<parent>
<groupId>pt.ua.ieeta</groupId>
<artifactId>dicoogle-all</artifactId>
<version>3.4.1-SNAPSHOT</version>
<version>3.5.0-1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -376,12 +376,6 @@
<version>2.9.5</version>
</dependency>

<dependency>
<groupId>dcm4che</groupId>
<artifactId>dcm4che-imageio</artifactId>
<version>${dcm4che.version}</version>
</dependency>

<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
Expand Down Expand Up @@ -465,5 +459,20 @@
<artifactId>raven-log4j2</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>dcm4che</groupId>
<artifactId>dcm4che-imageio</artifactId>
<version>${dcm4che.version}</version>
</dependency>
<dependency>
<groupId>org.dcm4che</groupId>
<artifactId>dcm4che-imageio</artifactId>
<version>3.3.8</version>
</dependency>
<dependency>
<groupId>org.dcm4che</groupId>
<artifactId>dcm4che-core</artifactId>
<version>3.3.8</version>
</dependency>
</dependencies>
</project>
10 changes: 10 additions & 0 deletions dicoogle/src/main/java/pt/ua/dicoogle/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package pt.ua.dicoogle;

import org.dcm4che2.data.TransferSyntax;
import org.dcm4che3.imageio.plugins.dcm.DicomImageReaderSpi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
Expand All @@ -31,6 +32,8 @@
import pt.ua.dicoogle.sdk.settings.server.ServerSettings;
import pt.ua.dicoogle.server.web.auth.Authentication;

import javax.imageio.ImageIO;
import javax.imageio.spi.IIORegistry;
import javax.swing.*;
import java.awt.*;
import java.io.File;
Expand Down Expand Up @@ -172,6 +175,13 @@ private static void LaunchDicoogle() {
// Start the initial Services of Dicoogle
pt.ua.dicoogle.server.ControlServices.getInstance();

// Register Image Reader for DICOM Objects
if(settings.getArchiveSettings().isSupportWSI()){
IIORegistry.getDefaultInstance().registerServiceProvider(new DicomImageReaderSpi());
ImageIO.setUseCache(false);
System.setProperty("dcm4che.useImageIOServiceRegistry", "true");
}

// Launch Async Index
// It monitors a folder, and when a file is touched an event
// triggers and index is updated.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
*
* This file is part of Dicoogle/dicoogle.
*
* Dicoogle/dicoogle 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 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 <http://www.gnu.org/licenses/>.
*/
package pt.ua.dicoogle.core.mlprovider;

import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation;
import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel;
import pt.ua.dicoogle.sdk.mlprovider.MLDataType;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Java object to represent datastore requests.
* Datastore requests can be for image ROIs or DICOM objects.
* In case of DICOM objects, the DIM level and DIM UID must be provided.
* @author Rui Jesus
*/
public class DatastoreRequest {
Rui-Jesus marked this conversation as resolved.
Show resolved Hide resolved

/**
* The name of the ML Provider plugin to update the dataset to.
*/
private String provider;

/**
* The type of dataset to upload
*/
private MLDataType dataType;

private DimLevel dimLevel;

private ArrayList<String> uids;

/**
* The dataset to upload.
* Each key should be a SOPInstanceUID and optionally the value should be a list of annotations.
*/
private Map<String, List<BulkAnnotation>> dataset;

public String getProvider() {
return provider;
}

public void setProvider(String provider) {
this.provider = provider;
}

public MLDataType getDataType() {
return dataType;
}

public void setDataType(MLDataType dataType) {
this.dataType = dataType;
}

public DimLevel getDimLevel() {
return dimLevel;
}

public void setDimLevel(DimLevel dimLevel) {
this.dimLevel = dimLevel;
}

public ArrayList<String> getUids() {
return uids;
}

public void setUids(ArrayList<String> uids) {
this.uids = uids;
}

public Map<String, List<BulkAnnotation>> getDataset() {
return dataset;
}

public void setDataset(Map<String, List<BulkAnnotation>> dataset) {
this.dataset = dataset;
}

/**
* Check if this request has enough information to be processed.
* For example, if it is of DICOM type, it must specify the dim level and the dim uid.
* @return true if the request can processed, false otherwise.
*/
public boolean validate(){
Rui-Jesus marked this conversation as resolved.
Show resolved Hide resolved
if(provider == null || provider.isEmpty())
return false;

switch (dataType){
case DICOM:
if(this.dimLevel == null || this.uids == null || this.uids.isEmpty())
return false;
case IMAGE:
return this.dataset != null && !this.dataset.isEmpty();
default:
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/**
* Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
*
* This file is part of Dicoogle/dicoogle.
*
* Dicoogle/dicoogle 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 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 <http://www.gnu.org/licenses/>.
*/
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.datastructs.dim.BulkAnnotation;
import pt.ua.dicoogle.sdk.datastructs.dim.Point2D;
import pt.ua.dicoogle.sdk.mlprovider.*;
import pt.ua.dicoogle.server.web.dicom.ROIExtractor;
import pt.ua.dicoogle.server.web.utils.cache.WSICache;

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;

/**
* This task processes Datastore Requests, and builds the appropriate ML Dataset.
*/
public class PrepareDatastoreTask implements Callable<MLDataset> {

private static final Logger logger = LoggerFactory.getLogger(PrepareDatastoreTask.class);
private final DatastoreRequest request;
private final PluginController controller;
private String dataset;

private final ROIExtractor roiExtractor;

private final WSICache wsiCache;

public PrepareDatastoreTask(PluginController controller, DatastoreRequest request) {
this.controller = controller;
this.request = request;
this.dataset = UUID.randomUUID().toString();

this.wsiCache = WSICache.getInstance();
this.roiExtractor = new ROIExtractor();
}

@Override
public MLDataset call() throws Exception {

if(!request.validate())
return null;

switch (request.getDataType()){
case DICOM:
return new MLDicomDataset(request.getDimLevel(), request.getUids());
case IMAGE:
//throw new UnsupportedOperationException("Datastore requests for image objects is not supported");

HashMap<String, String> extraFields = new HashMap<>();

String path = this.ensureAndCreatePath();

extraFields.put("SOPInstanceUID", "SOPInstanceUID");
extraFields.put("SharedFunctionalGroupsSequence_PixelMeasuresSequence_PixelSpacing", "SharedFunctionalGroupsSequence_PixelMeasuresSequence_PixelSpacing");
extraFields.put("Rows", "Rows");
extraFields.put("Columns", "Columns");
extraFields.put("NumberOfFrames", "NumberOfFrames");
extraFields.put("TotalPixelMatrixColumns", "TotalPixelMatrixColumns");
extraFields.put("TotalPixelMatrixRows", "TotalPixelMatrixRows");
extraFields.put("ImageType", "ImageType");

MLImageDataset mlDataset = new MLImageDataset();
HashMap<ImageEntry, MLlabel> dataset = new HashMap<>();
Set<String> classes = new HashSet<>();

this.request.getDataset().entrySet().forEach((entry -> {
try {

// Query this image using the first available query provider
Iterable<SearchResult> results = controller
.query(controller.getQueryProvidersName(true).get(0), "SOPInstanceUID:" + entry.getKey(), extraFields).get();

int c = 0;
for (SearchResult image : results) {

BasicDicomObject dcm = new BasicDicomObject();
dcm.putString(Tag.TransferSyntaxUID, VR.CS, "1.2.840.10008.1.2.4.50");

for(BulkAnnotation annotation: entry.getValue()){
for(List<Point2D> points: annotation.getAnnotations()){
BufferedImage roi = roiExtractor.extractROI(image.get("SOPInstanceUID").toString(), annotation.getAnnotationType(), points);
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 (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();
}

}
Loading
Loading