From b8810636fc01d89244032347101331beee1160e0 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Sun, 20 Feb 2022 07:35:45 +0000 Subject: [PATCH 01/11] Added new ImageWorkerInterface --- .../java/pt/ua/dicoogle/sdk/PluginBase.java | 7 ++ .../java/pt/ua/dicoogle/sdk/PluginSet.java | 11 ++++ .../sdk/datastructs/dim/BulkAnnotation.java | 65 +++++++++++++++++++ .../dicoogle/sdk/datastructs/dim/Point2D.java | 38 +++++++++++ .../ua/dicoogle/sdk/imageworker/ImageROI.java | 13 ++++ .../sdk/imageworker/ImageWorkerInterface.java | 11 ++++ 6 files changed, 145 insertions(+) create mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/BulkAnnotation.java create mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java create mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java create mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java index a3132387e..5b1582258 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java @@ -18,6 +18,7 @@ */ package pt.ua.dicoogle.sdk; +import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.ConfigurationHolder; import java.util.ArrayList; @@ -39,6 +40,7 @@ public abstract class PluginBase implements PluginSet, PlatformCommunicatorInter protected List queryPlugins = new ArrayList<>(); protected List jettyPlugins = new ArrayList<>(); protected List storagePlugins = new ArrayList<>(); + protected List imageWorkerPlugins = new ArrayList<>(); protected List services = new ArrayList<>(); protected ConfigurationHolder settings = null; @@ -82,6 +84,11 @@ public Collection getStoragePlugins() { return storagePlugins; } + @Override + public Collection getImageWorkerPlugins() { + return imageWorkerPlugins; + } + @Override public void setPlatformProxy(DicooglePlatformInterface core) { this.platform = core; diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java index d58f268dd..6ce7a1678 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java @@ -25,6 +25,7 @@ import org.restlet.resource.ServerResource; +import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.ConfigurationHolder; /** @@ -97,6 +98,16 @@ public default Collection getJettyPlugins() { return Collections.EMPTY_LIST; } + /** + * Obtains a collection of ImageWorker plugins, so as to integrate Image Worker plugins such as ROI extraction plugins in Dicoogle. + * This collection must be immutable. + * @return a collection of ImageWorker plugins to the core application + * @see ImageWorkerInterface + */ + public default Collection getImageWorkerPlugins() { + return Collections.EMPTY_LIST; + } + /** * Defines the plugin's settings. This method will be called once after the plugin set was instantiated * with plugin-scoped settings. Dicoogle users can modify these settings by accessing the XML file with 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 new file mode 100644 index 000000000..af7b15934 --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/BulkAnnotation.java @@ -0,0 +1,65 @@ +package pt.ua.dicoogle.sdk.datastructs.dim; + +import java.util.List; + +public class BulkAnnotation { + + public enum PixelOrigin { + FRAME, // Coordinates of this annotation are related to the frame (image section) + VOLUME // Coordinates of this annotation are related to the Frame Matrix (whole image) + } + + public enum AnnotationType { + RECTANGLE, ELLIPSE, POLYGON, POLYLINE, POINT + } + + private PixelOrigin pixelOrigin; + + private AnnotationType annotationType; + + private String label; + + private List points; + + private double confidence; + + public PixelOrigin getPixelOrigin() { + return pixelOrigin; + } + + public void setPixelOrigin(PixelOrigin pixelOrigin) { + this.pixelOrigin = pixelOrigin; + } + + public AnnotationType getAnnotationType() { + return annotationType; + } + + public void setAnnotationType(AnnotationType annotationType) { + this.annotationType = annotationType; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public double getConfidence() { + return confidence; + } + + public void setConfidence(double confidence) { + this.confidence = confidence; + } + + public List getPoints() { + return points; + } + + public void setPoints(List points) { + this.points = points; + } +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java new file mode 100644 index 000000000..63ff33017 --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java @@ -0,0 +1,38 @@ +package pt.ua.dicoogle.sdk.datastructs.dim; + +public class Point2D { + + private int x; + + private int y; + + public Point2D() { + x = 0; + y = 0; + } + + public Point2D(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public double distance(Point2D otherPoint) { + return Math.sqrt(Math.pow(this.x - otherPoint.getX(), 2) + Math.pow(this.y - otherPoint.getY(), 2)); + } +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java new file mode 100644 index 000000000..8f5222d3f --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java @@ -0,0 +1,13 @@ +package pt.ua.dicoogle.sdk.imageworker; + +import java.net.URI; + +public class ImageROI { + + private int width; + + private int height; + + private URI roi; + +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java new file mode 100644 index 000000000..c25c46832 --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java @@ -0,0 +1,11 @@ +package pt.ua.dicoogle.sdk.imageworker; + +import org.dcm4che2.io.DicomInputStream; +import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; + +public abstract class ImageWorkerInterface { + + public abstract ImageROI extractROI(DicomInputStream is, BulkAnnotation annotation); + + public abstract Iterable extractROIs(DicomInputStream is, Iterable annotations); +} From 566ff49dceb9d8ae50d6190e6e3d79ef0359e8d3 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Sun, 1 May 2022 16:02:51 +0100 Subject: [PATCH 02/11] Implemented new ImageWorker interface --- .../ua/dicoogle/plugins/PluginController.java | 25 ++++++++++++++++ sdk/pom.xml | 6 ++++ .../ua/dicoogle/sdk/imageworker/ImageROI.java | 29 +++++++++++++++++++ .../sdk/imageworker/ImageWorkerInterface.java | 10 +++---- 4 files changed, 65 insertions(+), 5 deletions(-) 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 cb7ae5c0a..b8aa92b4b 100755 --- a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java @@ -30,6 +30,7 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; +import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.ConfigurationHolder; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -344,6 +345,19 @@ public Collection getServletPlugins() { return this.getServletPlugins(true); } + public Collection getImageWorkerPlugins(boolean onlyEnabled) { + List plugins = new ArrayList<>(); + for (PluginSet pSet : pluginSets) { + for (ImageWorkerInterface i : pSet.getImageWorkerPlugins()) { + if (!i.isEnabled() && onlyEnabled) { + continue; + } + plugins.add(i); + } + } + return plugins; + } + public Collection getPluginSetNames() { Collection l = new ArrayList<>(); for (PluginSet s : this.pluginSets) { @@ -512,6 +526,17 @@ public StorageInterface getStorageByName(String name, boolean onlyEnabled) { return null; } + public ImageWorkerInterface getImageWorkerInterfaceByName(String name, boolean onlyEnabled) { + Collection plugins = getImageWorkerPlugins(onlyEnabled); + for (ImageWorkerInterface p : plugins) { + if (p.getName().equalsIgnoreCase(name)) { + // logger.info("Retrived Query Provider: "+name); + return p; + } + } + logger.debug("No image worker matching name {} for onlyEnabled = {}", name, onlyEnabled); + return null; + } public JointQueryTask queryAll(JointQueryTask holder, final String query, final Object... parameters) { List providers = this.getQueryProvidersName(true); diff --git a/sdk/pom.xml b/sdk/pom.xml index cb3efcb4b..79468db5b 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -181,6 +181,12 @@ ${dcm4che.version} + + dcm4che-core + org.dcm4che + 3.3.7 + + dcm4che dcm4che-net diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java index 8f5222d3f..4ac85de70 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java @@ -10,4 +10,33 @@ public class ImageROI { private URI roi; + public ImageROI(int width, int height, URI roi) { + this.width = width; + this.height = height; + this.roi = roi; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public URI getRoi() { + return roi; + } + + public void setRoi(URI roi) { + this.roi = roi; + } } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java index c25c46832..b0e4b62eb 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java @@ -1,11 +1,11 @@ package pt.ua.dicoogle.sdk.imageworker; -import org.dcm4che2.io.DicomInputStream; +import pt.ua.dicoogle.sdk.DicooglePlugin; +import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; -public abstract class ImageWorkerInterface { +public interface ImageWorkerInterface extends DicooglePlugin { + public abstract ImageROI extractROI(SearchResult sr, BulkAnnotation annotation); - public abstract ImageROI extractROI(DicomInputStream is, BulkAnnotation annotation); - - public abstract Iterable extractROIs(DicomInputStream is, Iterable annotations); + public abstract Iterable extractROIs(SearchResult sr, Iterable annotations); } From 26e0b9461855c8a87e554a7dc5715cc752df0336 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Mon, 20 Jun 2022 14:39:06 +0100 Subject: [PATCH 03/11] Added ImageWorkerInterface to PlatformInterface --- .../dicoogle/plugins/DicooglePlatformProxy.java | 11 +++++++++++ .../core/plugins/PlatformInterfaceMock.java | 11 +++++++++++ .../sdk/core/DicooglePlatformInterface.java | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java index 6878fa77b..255edd047 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java @@ -32,6 +32,7 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; +import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.server.ServerSettingsReader; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -162,6 +163,16 @@ public List indexBlocking(URI path) { return pluginController.indexBlocking(path); } + @Override + public ImageWorkerInterface getImageWorkerByName(String name, boolean onlyEnabled) { + return pluginController.getImageWorkerInterfaceByName(name, onlyEnabled); + } + + @Override + public Collection getImageWorkers(boolean onlyEnabled) { + return pluginController.getImageWorkerPlugins(onlyEnabled); + } + @Override public ServerSettingsReader getSettings() { return ServerSettingsManager.getSettings(); diff --git a/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java b/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java index 5b2974dcf..efd0a9480 100644 --- a/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java +++ b/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java @@ -26,6 +26,7 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; +import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.server.ServerSettingsReader; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -125,6 +126,16 @@ public List indexBlocking(URI path) { return null; } + @Override + public ImageWorkerInterface getImageWorkerByName(String name, boolean onlyEnabled) { + return null; + } + + @Override + public Collection getImageWorkers(boolean onlyEnabled) { + return null; + } + @Override public ServerSettingsReader getSettings() { return null; diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java index ac3fac299..a253e74d4 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java @@ -29,6 +29,7 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; +import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.server.ServerSettingsReader; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -213,6 +214,21 @@ public JointQueryTask query(JointQueryTask holder, List querySources, Di */ public List indexBlocking(URI path); + /** Gets the image worker with the given name. + * + * @param name the unique name of the worker + * @param onlyEnabled whether only enabled plugins should be retrieved (all are retrieved if {@code false}) + * @return the image worker with the given name or null if it doesn't exist + */ + public ImageWorkerInterface getImageWorkerByName(String name, boolean onlyEnabled); + + /** Gets all the image workers available. + * + * @param onlyEnabled whether only enabled plugins should be retrieved (all are retrieved if {@code false}) + * @return a collection with all image workers available + */ + public Collection getImageWorkers(boolean onlyEnabled); + /** Obtain access to the server's settings. * @return an object for read-only access to the settings */ From df564a80b5e07eca455d1743ae1871fd033f7d12 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Fri, 1 Jul 2022 18:17:52 +0100 Subject: [PATCH 04/11] Changes to ImageROI and ImageWorker interfaces --- .../ua/dicoogle/sdk/imageworker/ImageROI.java | 113 ++++++++++++++++-- .../sdk/imageworker/ImageWorkerInterface.java | 19 ++- 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java index 4ac85de70..337953fbf 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java @@ -1,19 +1,97 @@ package pt.ua.dicoogle.sdk.imageworker; +import java.io.File; import java.net.URI; +import java.util.Objects; +/** + * This object defines an Image ROI. + * It has a physical location defined by an URI. + * It is possible to write the image contents to a file defined by the URI. + * The ROI has an x and y position that identify its origin in the source image. + * The SOPInstanceUID defines where this ROI was extracted from. + */ public class ImageROI { + public enum FileType { + JPEG (".jpg", "image/jpeg"), + PNG (".png", "image/png"); + + private final String extension; + private final String mimeType; + + private FileType(String s, String mimeType) { + this.extension = s; + this.mimeType = mimeType; + } + + public String getExtension() { return extension; } + + public String getMimeType() {return mimeType; } + } + + private double x; + + private double y; + private int width; private int height; - private URI roi; + private String sopInstanceUID; + + private URI uriROI; - public ImageROI(int width, int height, URI roi) { + private FileType fileType; + + private ImageROI(URI uriROI){ + File f = new File(uriROI); + if(!f.exists()) + throw new IllegalArgumentException(String.format("URI %s does not exist", uriROI.getPath())); + this.x = 0; + this.y = 0; + } + + public ImageROI(String sopInstanceUID, int width, int height, URI uriROI, FileType fileType) { + this(uriROI); this.width = width; this.height = height; - this.roi = roi; + this.uriROI = uriROI; + this.fileType = fileType; + } + + public ImageROI(String sopInstanceUID, int x, int y, int width, int height, URI uriROI, FileType fileType) { + this(uriROI); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.uriROI = uriROI; + this.fileType = fileType; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public String getSopInstanceUID() { + return sopInstanceUID; + } + + public void setSopInstanceUID(String sopInstanceUID) { + this.sopInstanceUID = sopInstanceUID; } public int getWidth() { @@ -32,11 +110,32 @@ public void setHeight(int height) { this.height = height; } - public URI getRoi() { - return roi; + public URI getUriROI() { + return uriROI; + } + + public void setUriROI(URI uriROI) { + this.uriROI = uriROI; + } + + public FileType getFileType() { + return fileType; + } + + public void setFileType(FileType fileType) { + this.fileType = fileType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImageROI imageROI = (ImageROI) o; + return Double.compare(imageROI.x, x) == 0 && Double.compare(imageROI.y, y) == 0 && width == imageROI.width && height == imageROI.height && Objects.equals(sopInstanceUID, imageROI.sopInstanceUID) && Objects.equals(uriROI, imageROI.uriROI); } - public void setRoi(URI roi) { - this.roi = roi; + @Override + public int hashCode() { + return Objects.hash(x, y, width, height, sopInstanceUID, uriROI); } } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java index b0e4b62eb..5b8d6a8fc 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java @@ -4,8 +4,25 @@ import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; +import java.awt.image.BufferedImage; + public interface ImageWorkerInterface extends DicooglePlugin { - public abstract ImageROI extractROI(SearchResult sr, BulkAnnotation annotation); + /** + * Given a search result and one annotation, extract its ROI as a BufferedImage. + * This method does not write the ROI to disk. + * @param sr + * @param annotation + * @return + */ + public abstract BufferedImage extractROI(SearchResult sr, BulkAnnotation annotation); + + /** + * Given a search result and a list of annotations, extract the ROIs the annotations define on the image. + * This method will automatically write all ROIs to disk. + * @param sr + * @param annotations + * @return a list of image ROIs defined by the list of annotations + */ public abstract Iterable extractROIs(SearchResult sr, Iterable annotations); } From f0e92a8455dab46da90e2bfde10b777a15c15539 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Sat, 2 Jul 2022 12:35:33 +0100 Subject: [PATCH 05/11] Removed unecessary dependency --- sdk/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sdk/pom.xml b/sdk/pom.xml index 79468db5b..cb3efcb4b 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -181,12 +181,6 @@ ${dcm4che.version} - - dcm4che-core - org.dcm4che - 3.3.7 - - dcm4che dcm4che-net From b746c62d530cb8a600b67fca82f17f5c6a27e850 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Fri, 18 Nov 2022 07:25:42 +0000 Subject: [PATCH 06/11] Removed Image worker interface. Introduced instead new module to extract WSI ROIs. --- dicoogle/pom.xml | 6 + .../server/web/dicom/ROIExtractor.java | 2 + .../dim}/ImageROI.java | 282 +++++++++--------- .../sdk/datastructs/wsi/WSIFrame.java | 2 + .../sdk/datastructs/wsi/WSISopDescriptor.java | 2 + .../sdk/imageworker/ImageWorkerInterface.java | 28 -- 6 files changed, 153 insertions(+), 169 deletions(-) create mode 100644 dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java rename sdk/src/main/java/pt/ua/dicoogle/sdk/{imageworker => datastructs/dim}/ImageROI.java (96%) create mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java create mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java delete mode 100644 sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java diff --git a/dicoogle/pom.xml b/dicoogle/pom.xml index c1095a761..1699815d1 100644 --- a/dicoogle/pom.xml +++ b/dicoogle/pom.xml @@ -466,5 +466,11 @@ raven-log4j2 5.0.2 + + org.dcm4che + dcm4che-imageio + 3.3.7 + compile + diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java new file mode 100644 index 000000000..c9f53dc53 --- /dev/null +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java @@ -0,0 +1,2 @@ +package pt.ua.dicoogle.server.web.dicom;public class ROIExtractor { +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java similarity index 96% rename from sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java rename to sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java index 337953fbf..be42a8bd3 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageROI.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/ImageROI.java @@ -1,141 +1,141 @@ -package pt.ua.dicoogle.sdk.imageworker; - -import java.io.File; -import java.net.URI; -import java.util.Objects; - -/** - * This object defines an Image ROI. - * It has a physical location defined by an URI. - * It is possible to write the image contents to a file defined by the URI. - * The ROI has an x and y position that identify its origin in the source image. - * The SOPInstanceUID defines where this ROI was extracted from. - */ -public class ImageROI { - - public enum FileType { - JPEG (".jpg", "image/jpeg"), - PNG (".png", "image/png"); - - private final String extension; - private final String mimeType; - - private FileType(String s, String mimeType) { - this.extension = s; - this.mimeType = mimeType; - } - - public String getExtension() { return extension; } - - public String getMimeType() {return mimeType; } - } - - private double x; - - private double y; - - private int width; - - private int height; - - private String sopInstanceUID; - - private URI uriROI; - - private FileType fileType; - - private ImageROI(URI uriROI){ - File f = new File(uriROI); - if(!f.exists()) - throw new IllegalArgumentException(String.format("URI %s does not exist", uriROI.getPath())); - this.x = 0; - this.y = 0; - } - - public ImageROI(String sopInstanceUID, int width, int height, URI uriROI, FileType fileType) { - this(uriROI); - this.width = width; - this.height = height; - this.uriROI = uriROI; - this.fileType = fileType; - } - - public ImageROI(String sopInstanceUID, int x, int y, int width, int height, URI uriROI, FileType fileType) { - this(uriROI); - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.uriROI = uriROI; - this.fileType = fileType; - } - - public double getX() { - return x; - } - - public void setX(double x) { - this.x = x; - } - - public double getY() { - return y; - } - - public void setY(double y) { - this.y = y; - } - - public String getSopInstanceUID() { - return sopInstanceUID; - } - - public void setSopInstanceUID(String sopInstanceUID) { - this.sopInstanceUID = sopInstanceUID; - } - - public int getWidth() { - return width; - } - - public void setWidth(int width) { - this.width = width; - } - - public int getHeight() { - return height; - } - - public void setHeight(int height) { - this.height = height; - } - - public URI getUriROI() { - return uriROI; - } - - public void setUriROI(URI uriROI) { - this.uriROI = uriROI; - } - - public FileType getFileType() { - return fileType; - } - - public void setFileType(FileType fileType) { - this.fileType = fileType; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ImageROI imageROI = (ImageROI) o; - return Double.compare(imageROI.x, x) == 0 && Double.compare(imageROI.y, y) == 0 && width == imageROI.width && height == imageROI.height && Objects.equals(sopInstanceUID, imageROI.sopInstanceUID) && Objects.equals(uriROI, imageROI.uriROI); - } - - @Override - public int hashCode() { - return Objects.hash(x, y, width, height, sopInstanceUID, uriROI); - } -} +package pt.ua.dicoogle.sdk.imageworker; + +import java.io.File; +import java.net.URI; +import java.util.Objects; + +/** + * This object defines an Image ROI. + * It has a physical location defined by an URI. + * It is possible to write the image contents to a file defined by the URI. + * The ROI has an x and y position that identify its origin in the source image. + * The SOPInstanceUID defines where this ROI was extracted from. + */ +public class ImageROI { + + public enum FileType { + JPEG (".jpg", "image/jpeg"), + PNG (".png", "image/png"); + + private final String extension; + private final String mimeType; + + private FileType(String s, String mimeType) { + this.extension = s; + this.mimeType = mimeType; + } + + public String getExtension() { return extension; } + + public String getMimeType() {return mimeType; } + } + + private double x; + + private double y; + + private int width; + + private int height; + + private String sopInstanceUID; + + private URI uriROI; + + private FileType fileType; + + private ImageROI(URI uriROI){ + File f = new File(uriROI); + if(!f.exists()) + throw new IllegalArgumentException(String.format("URI %s does not exist", uriROI.getPath())); + this.x = 0; + this.y = 0; + } + + public ImageROI(String sopInstanceUID, int width, int height, URI uriROI, FileType fileType) { + this(uriROI); + this.width = width; + this.height = height; + this.uriROI = uriROI; + this.fileType = fileType; + } + + public ImageROI(String sopInstanceUID, int x, int y, int width, int height, URI uriROI, FileType fileType) { + this(uriROI); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.uriROI = uriROI; + this.fileType = fileType; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public String getSopInstanceUID() { + return sopInstanceUID; + } + + public void setSopInstanceUID(String sopInstanceUID) { + this.sopInstanceUID = sopInstanceUID; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public URI getUriROI() { + return uriROI; + } + + public void setUriROI(URI uriROI) { + this.uriROI = uriROI; + } + + public FileType getFileType() { + return fileType; + } + + public void setFileType(FileType fileType) { + this.fileType = fileType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImageROI imageROI = (ImageROI) o; + return Double.compare(imageROI.x, x) == 0 && Double.compare(imageROI.y, y) == 0 && width == imageROI.width && height == imageROI.height && Objects.equals(sopInstanceUID, imageROI.sopInstanceUID) && Objects.equals(uriROI, imageROI.uriROI); + } + + @Override + public int hashCode() { + return Objects.hash(x, y, width, height, sopInstanceUID, uriROI); + } +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java new file mode 100644 index 000000000..24b36e27c --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java @@ -0,0 +1,2 @@ +package pt.ua.dicoogle.sdk.datastructs.wsi;public class WSIFrame { +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java new file mode 100644 index 000000000..65848f12c --- /dev/null +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java @@ -0,0 +1,2 @@ +package pt.ua.dicoogle.sdk.datastructs.wsi;public class WSISopDescriptor { +} diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java deleted file mode 100644 index 5b8d6a8fc..000000000 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/imageworker/ImageWorkerInterface.java +++ /dev/null @@ -1,28 +0,0 @@ -package pt.ua.dicoogle.sdk.imageworker; - -import pt.ua.dicoogle.sdk.DicooglePlugin; -import pt.ua.dicoogle.sdk.datastructs.SearchResult; -import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; - -import java.awt.image.BufferedImage; - -public interface ImageWorkerInterface extends DicooglePlugin { - - /** - * Given a search result and one annotation, extract its ROI as a BufferedImage. - * This method does not write the ROI to disk. - * @param sr - * @param annotation - * @return - */ - public abstract BufferedImage extractROI(SearchResult sr, BulkAnnotation annotation); - - /** - * Given a search result and a list of annotations, extract the ROIs the annotations define on the image. - * This method will automatically write all ROIs to disk. - * @param sr - * @param annotations - * @return a list of image ROIs defined by the list of annotations - */ - public abstract Iterable extractROIs(SearchResult sr, Iterable annotations); -} From 2362a8154a9fc40545601205200d9d9e4dc4ad71 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Fri, 18 Nov 2022 10:35:07 +0000 Subject: [PATCH 07/11] Baseline ROI extractor servlet. Fully removed ImageWorkerInterface. --- dicoogle/pom.xml | 1 - .../ua/dicoogle/plugins/PluginController.java | 26 -- .../ua/dicoogle/server/web/DicoogleWeb.java | 14 +- .../server/web/dicom/ROIExtractor.java | 235 +++++++++++++++++- .../server/web/servlets/ROIServlet.java | 134 ++++++++++ sdk/pom.xml | 6 + .../sdk/datastructs/wsi/WSIFrame.java | 71 +++++- .../sdk/datastructs/wsi/WSISopDescriptor.java | 74 +++++- 8 files changed, 522 insertions(+), 39 deletions(-) create mode 100644 dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java diff --git a/dicoogle/pom.xml b/dicoogle/pom.xml index 1699815d1..bbe7e8f2c 100644 --- a/dicoogle/pom.xml +++ b/dicoogle/pom.xml @@ -470,7 +470,6 @@ org.dcm4che dcm4che-imageio 3.3.7 - compile 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 b8aa92b4b..232f1cdd0 100755 --- a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/PluginController.java @@ -30,7 +30,6 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; -import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.ConfigurationHolder; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -345,19 +344,6 @@ public Collection getServletPlugins() { return this.getServletPlugins(true); } - public Collection getImageWorkerPlugins(boolean onlyEnabled) { - List plugins = new ArrayList<>(); - for (PluginSet pSet : pluginSets) { - for (ImageWorkerInterface i : pSet.getImageWorkerPlugins()) { - if (!i.isEnabled() && onlyEnabled) { - continue; - } - plugins.add(i); - } - } - return plugins; - } - public Collection getPluginSetNames() { Collection l = new ArrayList<>(); for (PluginSet s : this.pluginSets) { @@ -526,18 +512,6 @@ public StorageInterface getStorageByName(String name, boolean onlyEnabled) { return null; } - public ImageWorkerInterface getImageWorkerInterfaceByName(String name, boolean onlyEnabled) { - Collection plugins = getImageWorkerPlugins(onlyEnabled); - for (ImageWorkerInterface p : plugins) { - if (p.getName().equalsIgnoreCase(name)) { - // logger.info("Retrived Query Provider: "+name); - return p; - } - } - logger.debug("No image worker matching name {} for onlyEnabled = {}", name, onlyEnabled); - return null; - } - public JointQueryTask queryAll(JointQueryTask holder, final String query, final Object... parameters) { List providers = this.getQueryProvidersName(true); return query(holder, providers, query, parameters); diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java index 3fa734d9b..4e4b41189 100755 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java @@ -24,14 +24,7 @@ import pt.ua.dicoogle.plugins.webui.WebUIPlugin; import pt.ua.dicoogle.sdk.utils.TagsStruct; import pt.ua.dicoogle.server.web.rest.VersionResource; -import pt.ua.dicoogle.server.web.servlets.RestletHttpServlet; -import pt.ua.dicoogle.server.web.servlets.ExportToCSVServlet; -import pt.ua.dicoogle.server.web.servlets.SettingsServlet; -import pt.ua.dicoogle.server.web.servlets.TagsServlet; -import pt.ua.dicoogle.server.web.servlets.ExportCSVToFILEServlet; -import pt.ua.dicoogle.server.web.servlets.SearchHolderServlet; -import pt.ua.dicoogle.server.web.servlets.IndexerServlet; -import pt.ua.dicoogle.server.web.servlets.ImageServlet; +import pt.ua.dicoogle.server.web.servlets.*; import pt.ua.dicoogle.server.web.servlets.plugins.PluginsServlet; import pt.ua.dicoogle.server.web.servlets.management.*; import pt.ua.dicoogle.server.web.servlets.search.*; @@ -140,6 +133,9 @@ public DicoogleWeb(int port) throws Exception { final ServletContextHandler dic2png = createServletHandler(new ImageServlet(cache), "/dic2png"); cache.start(); // start the caching system + // setup the ROI extractor + final ServletContextHandler roiExtractor = createServletHandler(new ROIServlet(cache), "/roi"); + // setup the DICOM to PNG image servlet final ServletContextHandler dictags = createServletHandler(new TagsServlet(), "/dictags"); @@ -178,7 +174,7 @@ public DicoogleWeb(int port) throws Exception { PluginRestletApplication.attachRestPlugin(new VersionResource()); // list the all the handlers mounted above - Handler[] handlers = new Handler[] {pluginHandler, legacyHandler, dic2png, dictags, + Handler[] handlers = new Handler[] {pluginHandler, legacyHandler, dic2png, roiExtractor, dictags, createServletHandler(new IndexerServlet(), "/indexer"), // DEPRECATED createServletHandler(new SettingsServlet(), "/settings"), csvServletHolder, createServletHandler(new LoginServlet(), "/login"), diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java index c9f53dc53..5a73082e2 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java @@ -1,2 +1,235 @@ -package pt.ua.dicoogle.server.web.dicom;public class ROIExtractor { +package pt.ua.dicoogle.server.web.dicom; + +import org.dcm4che3.data.Attributes; +import org.dcm4che3.imageio.plugins.dcm.DicomImageReadParam; +import org.dcm4che3.imageio.plugins.dcm.DicomImageReader; +import org.dcm4che3.imageio.plugins.dcm.DicomMetaData; +import org.dcm4che3.io.BulkDataDescriptor; +import org.dcm4che3.io.DicomInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pt.ua.dicoogle.sdk.StorageInputStream; +import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; +import pt.ua.dicoogle.sdk.datastructs.dim.Point2D; +import pt.ua.dicoogle.sdk.datastructs.wsi.WSIFrame; +import pt.ua.dicoogle.sdk.datastructs.wsi.WSISopDescriptor; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.GZIPInputStream; + +public class ROIExtractor { + + private static final Logger logger = LoggerFactory.getLogger(ROIExtractor.class); + private static final String EXTENSION_GZIP = ".gz"; + private static final int BUFFER_SIZE = 8192; + + public BufferedImage extractROI(StorageInputStream sis, BulkAnnotation bulkAnnotation) { + + ImageReader imageReader = getImageReader(); + + DicomMetaData dicomMetaData; + try { + dicomMetaData = getDicomMetadata(sis); + } catch (IOException e) { + logger.error("Error reading DICOM file", e); + return null; + } + + DicomImageReadParam param; + try{ + imageReader.setInput(dicomMetaData); + param = (DicomImageReadParam) imageReader.getDefaultReadParam(); + } catch (Exception e){ + logger.error("Error setting image reader", e); + return null; + } + + WSISopDescriptor descriptor = new WSISopDescriptor(); + descriptor.extractData(dicomMetaData.getAttributes()); + + try { + return getROIFromAnnotation(bulkAnnotation, descriptor, imageReader, param); + } catch (IllegalArgumentException e) { + logger.error("Error writing ROI", e); + } + + return null; + } + + + + private static ImageReader getImageReader() { + Iterator iter = ImageIO.getImageReadersByFormatName("DICOM"); + while (iter.hasNext()) { + ImageReader reader = iter.next(); + if (reader instanceof DicomImageReader) + return reader; + } + return null; + } + + /** + * Given an annotation and the details of the image, this method returns the frames that intersect with the annotation. + * This method is meant for WSI images that are split into frames. + * @param descriptor the WSI descriptor + * @param annotation The annotation to intersect + * @return a 2D matrix of frames that intersect this annotation. + */ + private List> getFrameMatrixFromAnnotation(WSISopDescriptor descriptor, BulkAnnotation annotation) { + + //Number of tiles along the x direction, number of columns in the frame matrix + int nx_tiles = (int) Math.ceil(descriptor.getTotalPixelMatrixColumns() / descriptor.getTileWidth()); + + //Number of tiles along the y direction, number of rows in the frame matrix + int ny_tiles = (int) Math.ceil(descriptor.getTotalPixelMatrixRows() / descriptor.getTileHeight()); + + List> matrix = new ArrayList<>(); + + switch (annotation.getAnnotationType()) { + case RECTANGLE: + // Calculate the starting position of the annotation in frame coordinates + int x_c = (int) Math.floor(annotation.getPoints().get(0).getX() / descriptor.getTileWidth()); + int y_c = (int) Math.floor(annotation.getPoints().get(0).getY() / descriptor.getTileHeight()); + + //Annotation is completely out of bounds, no intersection possible + if(x_c > nx_tiles || y_c > ny_tiles) + return matrix; + + // Calculate the ending position of the annotation in frame coordinates + int x_e = (int) Math.floor(annotation.getPoints().get(3).getX() / descriptor.getTileWidth()); + int y_e = (int) Math.floor(annotation.getPoints().get(3).getY() / descriptor.getTileHeight()); + + //Annotation might be out of bonds, adjust that + if(x_e > (nx_tiles - 1)) + x_e = nx_tiles - 1; + + if(y_e > (ny_tiles - 1)) + y_e = ny_tiles - 1; + + for (int i = y_c; i <= y_e ; i++) { + matrix.add(new ArrayList<>()); + for (int j = x_c; j <= x_e; j++) { + WSIFrame frame = new WSIFrame(descriptor.getTileWidth(), descriptor.getTileHeight(), j, i, i * nx_tiles + j); + matrix.get(i).add(frame); + } + } + break; + } + + return matrix; + } + + /** + * Given an annotation and an image, return the section of the image the annotation intersects. + * @param annotation + * @param descriptor + * @param imageReader + * @param param + * @return + * @throws IllegalArgumentException + */ + private BufferedImage getROIFromAnnotation(BulkAnnotation annotation, WSISopDescriptor descriptor, ImageReader imageReader, DicomImageReadParam param) throws IllegalArgumentException { + if(annotation.getAnnotationType() != BulkAnnotation.AnnotationType.RECTANGLE){ + throw new IllegalArgumentException("Trying to build a ROI without a rectangle annotation"); + } + + Point2D annotationPoint1 = annotation.getPoints().get(0); + Point2D annotationPoint2 = annotation.getPoints().get(3); + + int clipX = Math.max(annotationPoint2.getX() - descriptor.getTotalPixelMatrixColumns(), 0); + int clipY = Math.max(annotationPoint2.getY() - descriptor.getTotalPixelMatrixRows(), 0); + + int annotationWidth = (int) annotation.getPoints().get(0).distance(annotation.getPoints().get(1)) - clipX; + int annotationHeight = (int) annotation.getPoints().get(0).distance(annotation.getPoints().get(2)) - clipY; + + List> frameMatrix = getFrameMatrixFromAnnotation(descriptor, annotation); + BufferedImage combined = new BufferedImage(annotationWidth, annotationHeight, BufferedImage.TYPE_INT_RGB); + Graphics g = combined.getGraphics(); + + for (List matrix : frameMatrix) { + for (WSIFrame frame : matrix) { + BufferedImage bb; + try { + bb = imageReader.read(frame.getFrameIndex(), param); + } catch (IOException e) { + logger.error("Error building ROI, skipping entry", e); + continue; + } + + //Calculate intersections between ROI and frame + Point2D framePoint1 = new Point2D(frame.getX() * descriptor.getTileWidth(), frame.getY() * descriptor.getTileHeight()); + Point2D framePoint2 = new Point2D(framePoint1.getX() + bb.getWidth(), framePoint1.getY() + bb.getHeight()); + Point2D intersectionPoint1 = new Point2D(Math.max(annotationPoint1.getX(), framePoint1.getX()), Math.max(annotationPoint1.getY(), framePoint1.getY())); + Point2D intersectionPoint2 = new Point2D(Math.min(annotationPoint2.getX(), framePoint2.getX()), Math.min(annotationPoint2.getY(), framePoint2.getY())); + + int startX = intersectionPoint1.getX() - annotationPoint1.getX(); + int startY = intersectionPoint1.getY() - annotationPoint1.getY(); + + int endX = intersectionPoint2.getX() - annotationPoint1.getX(); + int endY = intersectionPoint2.getY() - annotationPoint1.getY(); + + int frameStartX = intersectionPoint1.getX() - framePoint1.getX(); + int frameStartY = intersectionPoint1.getY() - framePoint1.getY(); + + int frameEndX = intersectionPoint2.getX() - framePoint1.getX(); + int frameEndY = intersectionPoint2.getY() - framePoint1.getY(); + + int deltaX = frameEndX - frameStartX; + int deltaY = frameEndY - frameStartY; + + //This means that the frame is smaller than the intersection area + //It can happen when we are on the edge of the image and the tiles do not have the dimensions stated in the DICOM file + if (deltaX > bb.getWidth()) { + endX = frameEndX - bb.getWidth(); + frameEndX = bb.getWidth(); + } + + if (deltaY > bb.getHeight()) { + endY = frameEndY - bb.getHeight(); + frameEndY = bb.getHeight(); + } + + g.drawImage(bb, startX, startY, + endX, endY, frameStartX, frameStartY, frameEndX, frameEndY, null); + + } + } + + g.dispose(); + return combined; + } + + private DicomMetaData getDicomMetadata(StorageInputStream sis) throws IOException{ + Attributes fmi; + Attributes dataset; + DicomInputStream dis; + + if(sis == null){ + logger.info("Storage == null"); + throw new InvalidParameterException("Could not find the desired URI"); + } + + String filePath = sis.getURI().getPath(); + if (filePath.endsWith(EXTENSION_GZIP)){ + InputStream inStream = new GZIPInputStream(new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE)); + dis = new DicomInputStream(inStream); + } + else { + dis = new DicomInputStream(new File(filePath)); + } + dis.setIncludeBulkData(DicomInputStream.IncludeBulkData.URI); + dis.setBulkDataDescriptor(BulkDataDescriptor.PIXELDATA); + fmi = dis.readFileMetaInformation(); + dataset = dis.readDataset(-1, -1); + return new DicomMetaData(fmi, dataset); + } + } diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java new file mode 100644 index 000000000..a24ab09e3 --- /dev/null +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java @@ -0,0 +1,134 @@ +package pt.ua.dicoogle.server.web.servlets; + +import org.restlet.data.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pt.ua.dicoogle.core.settings.ServerSettingsManager; +import pt.ua.dicoogle.plugins.PluginController; +import pt.ua.dicoogle.sdk.QueryInterface; +import pt.ua.dicoogle.sdk.StorageInputStream; +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.utils.QueryException; +import pt.ua.dicoogle.server.web.dicom.ROIExtractor; +import pt.ua.dicoogle.server.web.utils.LocalImageCache; + +import javax.imageio.ImageIO; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; + +public class ROIServlet extends HttpServlet { + + + private static final Logger logger = LoggerFactory.getLogger(ROIServlet.class); + private static final long serialVersionUID = 1L; + + private final LocalImageCache cache; + private final String queryProvider; + private final ROIExtractor roiExtractor; + private HashMap extraFields; + + /** + * Creates an image servlet. + * + * @param cache the local image caching system, can be null and if so no caching mechanism will be used. + */ + public ROIServlet(LocalImageCache cache) { + this.cache = cache; + this.roiExtractor = new ROIExtractor(); + List dicomProviders = ServerSettingsManager.getSettings().getArchiveSettings().getDIMProviders(); + queryProvider = dicomProviders.iterator().next(); + + extraFields = new HashMap<>(); + 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"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String sopInstanceUID = request.getParameter("uid"); + String x = request.getParameter("x"); + String y = request.getParameter("y"); + String width = request.getParameter("width"); + String height = request.getParameter("height"); + + if(sopInstanceUID == null || sopInstanceUID.isEmpty()){ + response.sendError(Status.CLIENT_ERROR_BAD_REQUEST.getCode(), "SOPInstanceUID provided was invalid"); + return; + } + + if(x == null || x.isEmpty() || y == null || y.isEmpty() || width == null || width.isEmpty() || height == null || height.isEmpty()){ + response.sendError(Status.CLIENT_ERROR_BAD_REQUEST.getCode(), "ROI provided was invalid"); + return; + } + + BulkAnnotation annotation; + try{ + int nX = Integer.parseInt(x); + int nY = Integer.parseInt(y); + int nWidth = Integer.parseInt(width); + int nHeight = Integer.parseInt(height); + annotation = new BulkAnnotation(); + Point2D tl = new Point2D(nX, nY); + Point2D tr = new Point2D(nX + nWidth, nY); + Point2D bl = new Point2D(nX, nY + nHeight); + Point2D br = new Point2D(nX + nWidth, nY + nHeight); + List points = new ArrayList<>(); + points.add(tl); points.add(tr); points.add(bl); points.add(br); + annotation.setPoints(points); + annotation.setAnnotationType(BulkAnnotation.AnnotationType.RECTANGLE); + } catch (NumberFormatException e){ + response.sendError(Status.CLIENT_ERROR_BAD_REQUEST.getCode(), "ROI provided was invalid"); + return; + } + + QueryInterface queryInterface = PluginController.getInstance().getQueryProviderByName(queryProvider, false); + + String query = String.format("SOPInstanceUID:%s", sopInstanceUID); + Iterable results; + try { + results = queryInterface.query(query, extraFields); + } catch (QueryException e) { + logger.error("Error requesting ROI for image {}", sopInstanceUID, e); + response.sendError(Status.SERVER_ERROR_INTERNAL.getCode(), "Error building response"); + return; + } + + if(!results.iterator().hasNext()){ + response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), String.format("No instances exist with SOPInstanceUID: %s", sopInstanceUID)); + return; + } + + //We're only expecting one result + SearchResult result = results.iterator().next(); + + StorageInputStream sis = PluginController.getInstance().resolveURI(result.getURI()).iterator().next(); + + BufferedImage bi = roiExtractor.extractROI(sis, annotation); + + if(bi != null){ + response.setContentType("image/jpeg"); + OutputStream out = response.getOutputStream(); + ImageIO.write(bi, "jpg", out); + out.close(); + return; + } + + response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), "Could not build ROI with the provided UID"); + } + +} diff --git a/sdk/pom.xml b/sdk/pom.xml index cb3efcb4b..8c65403ed 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -235,6 +235,12 @@ slf4j-api ${slf4j.version} + + org.dcm4che + dcm4che-core + 3.3.7 + compile + diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java index 24b36e27c..af17b6721 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSIFrame.java @@ -1,2 +1,71 @@ -package pt.ua.dicoogle.sdk.datastructs.wsi;public class WSIFrame { +package pt.ua.dicoogle.sdk.datastructs.wsi; + +public class WSIFrame { + + private int width; + private int height; + private int x; + private int y; + private int frameIndex; + + /** + * Construct a WSI frame. + * @param width the width of the frame + * @param height the height of the frame + * @param x the x coordinate of this frame in the frame matrix + * @param y the y coordinate of this frame in the frame matrix + * @param frameIndex the index of the frame that uniquely identifies this frame in the WSI + */ + public WSIFrame(int width, int height, int x, int y, int frameIndex) { + this.width = width; + this.height = height; + this.x = x; + this.y = y; + this.frameIndex = frameIndex; + } + + public WSIFrame(int width, int height) { + this.width = width; + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getFrameIndex() { + return frameIndex; + } + + public void setFrameIndex(int frameIndex) { + this.frameIndex = frameIndex; + } } diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java index 65848f12c..c39464551 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/wsi/WSISopDescriptor.java @@ -1,2 +1,74 @@ -package pt.ua.dicoogle.sdk.datastructs.wsi;public class WSISopDescriptor { +package pt.ua.dicoogle.sdk.datastructs.wsi; + +import org.dcm4che2.data.Tag; +import org.dcm4che3.data.Attributes; +/** + * Utility class to describe a specific resolution level of a WSI pyramid + */ +public class WSISopDescriptor { + + private int tileWidth; + private int tileHeight; + private int totalPixelMatrixColumns; + private int totalPixelMatrixRows; + + public WSISopDescriptor(int tileWidth, int tileHeight, int totalPixelMatrixColumns, int totalPixelMatrixRows) { + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.totalPixelMatrixColumns = totalPixelMatrixColumns; + this.totalPixelMatrixRows = totalPixelMatrixRows; + } + + public WSISopDescriptor() { + } + + public int getTileWidth() { + return tileWidth; + } + + public void setTileWidth(int tileWidth) { + this.tileWidth = tileWidth; + } + + public int getTileHeight() { + return tileHeight; + } + + public void setTileHeight(int tileHeight) { + this.tileHeight = tileHeight; + } + + public int getTotalPixelMatrixColumns() { + return totalPixelMatrixColumns; + } + + public void setTotalPixelMatrixColumns(int totalPixelMatrixColumns) { + this.totalPixelMatrixColumns = totalPixelMatrixColumns; + } + + public int getTotalPixelMatrixRows() { + return totalPixelMatrixRows; + } + + public void setTotalPixelMatrixRows(int totalPixelMatrixRows) { + this.totalPixelMatrixRows = totalPixelMatrixRows; + } + + /** + * Given a search result, extract if possible the information that describes the resolution level contained within. + * @param attrs + */ + public void extractData(Attributes attrs){ + String strRows = attrs.getString(Tag.Rows); + String strColumns = attrs.getString(Tag.Columns); + String strTotalPixelMatrixColumns = attrs.getString(Tag.TotalPixelMatrixColumns); + String strTotalPixelMatrixRows = attrs.getString(Tag.TotalPixelMatrixRows); + + this.totalPixelMatrixColumns = Integer.parseInt(strTotalPixelMatrixColumns); + this.totalPixelMatrixRows = Integer.parseInt(strTotalPixelMatrixRows); + this.tileHeight = Integer.parseInt(strRows.split("\\.")[0]); + this.tileWidth = Integer.parseInt(strColumns.split("\\.")[0]); + } + } + From 80f530bb3b57c9119c1290b163eb4d5155885a44 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Wed, 23 Nov 2022 04:50:05 +0000 Subject: [PATCH 08/11] Fixed compilation issues. --- .../dicoogle/plugins/DicooglePlatformProxy.java | 11 ----------- .../pt/ua/dicoogle/server/web/DicoogleWeb.java | 2 +- .../dicoogle/server/web/servlets/ROIServlet.java | 7 ++----- .../core/plugins/PlatformInterfaceMock.java | 11 ----------- .../main/java/pt/ua/dicoogle/sdk/PluginBase.java | 7 ------- .../main/java/pt/ua/dicoogle/sdk/PluginSet.java | 11 ----------- .../sdk/core/DicooglePlatformInterface.java | 16 ---------------- 7 files changed, 3 insertions(+), 62 deletions(-) diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java index 255edd047..6878fa77b 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/plugins/DicooglePlatformProxy.java @@ -32,7 +32,6 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; -import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.server.ServerSettingsReader; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -163,16 +162,6 @@ public List indexBlocking(URI path) { return pluginController.indexBlocking(path); } - @Override - public ImageWorkerInterface getImageWorkerByName(String name, boolean onlyEnabled) { - return pluginController.getImageWorkerInterfaceByName(name, onlyEnabled); - } - - @Override - public Collection getImageWorkers(boolean onlyEnabled) { - return pluginController.getImageWorkerPlugins(onlyEnabled); - } - @Override public ServerSettingsReader getSettings() { return ServerSettingsManager.getSettings(); diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java index 4e4b41189..3dba3d7db 100755 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/DicoogleWeb.java @@ -134,7 +134,7 @@ public DicoogleWeb(int port) throws Exception { cache.start(); // start the caching system // setup the ROI extractor - final ServletContextHandler roiExtractor = createServletHandler(new ROIServlet(cache), "/roi"); + final ServletContextHandler roiExtractor = createServletHandler(new ROIServlet(), "/roi"); // setup the DICOM to PNG image servlet final ServletContextHandler dictags = createServletHandler(new TagsServlet(), "/dictags"); diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java index a24ab09e3..78d25ad72 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java @@ -30,18 +30,15 @@ public class ROIServlet extends HttpServlet { private static final Logger logger = LoggerFactory.getLogger(ROIServlet.class); private static final long serialVersionUID = 1L; - private final LocalImageCache cache; private final String queryProvider; private final ROIExtractor roiExtractor; private HashMap extraFields; /** - * Creates an image servlet. + * Creates ROI servlet servlet. * - * @param cache the local image caching system, can be null and if so no caching mechanism will be used. */ - public ROIServlet(LocalImageCache cache) { - this.cache = cache; + public ROIServlet() { this.roiExtractor = new ROIExtractor(); List dicomProviders = ServerSettingsManager.getSettings().getArchiveSettings().getDIMProviders(); queryProvider = dicomProviders.iterator().next(); diff --git a/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java b/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java index efd0a9480..5b2974dcf 100644 --- a/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java +++ b/dicoogle/src/test/java/pt/ua/dicoogle/core/plugins/PlatformInterfaceMock.java @@ -26,7 +26,6 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; -import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.server.ServerSettingsReader; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -126,16 +125,6 @@ public List indexBlocking(URI path) { return null; } - @Override - public ImageWorkerInterface getImageWorkerByName(String name, boolean onlyEnabled) { - return null; - } - - @Override - public Collection getImageWorkers(boolean onlyEnabled) { - return null; - } - @Override public ServerSettingsReader getSettings() { return null; diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java index 5b1582258..a3132387e 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginBase.java @@ -18,7 +18,6 @@ */ package pt.ua.dicoogle.sdk; -import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.ConfigurationHolder; import java.util.ArrayList; @@ -40,7 +39,6 @@ public abstract class PluginBase implements PluginSet, PlatformCommunicatorInter protected List queryPlugins = new ArrayList<>(); protected List jettyPlugins = new ArrayList<>(); protected List storagePlugins = new ArrayList<>(); - protected List imageWorkerPlugins = new ArrayList<>(); protected List services = new ArrayList<>(); protected ConfigurationHolder settings = null; @@ -84,11 +82,6 @@ public Collection getStoragePlugins() { return storagePlugins; } - @Override - public Collection getImageWorkerPlugins() { - return imageWorkerPlugins; - } - @Override public void setPlatformProxy(DicooglePlatformInterface core) { this.platform = core; diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java index 6ce7a1678..d58f268dd 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/PluginSet.java @@ -25,7 +25,6 @@ import org.restlet.resource.ServerResource; -import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.ConfigurationHolder; /** @@ -98,16 +97,6 @@ public default Collection getJettyPlugins() { return Collections.EMPTY_LIST; } - /** - * Obtains a collection of ImageWorker plugins, so as to integrate Image Worker plugins such as ROI extraction plugins in Dicoogle. - * This collection must be immutable. - * @return a collection of ImageWorker plugins to the core application - * @see ImageWorkerInterface - */ - public default Collection getImageWorkerPlugins() { - return Collections.EMPTY_LIST; - } - /** * Defines the plugin's settings. This method will be called once after the plugin set was instantiated * with plugin-scoped settings. Dicoogle users can modify these settings by accessing the XML file with diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java index a253e74d4..ac3fac299 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/core/DicooglePlatformInterface.java @@ -29,7 +29,6 @@ import pt.ua.dicoogle.sdk.datastructs.Report; import pt.ua.dicoogle.sdk.datastructs.SearchResult; import pt.ua.dicoogle.sdk.datastructs.dim.DimLevel; -import pt.ua.dicoogle.sdk.imageworker.ImageWorkerInterface; import pt.ua.dicoogle.sdk.settings.server.ServerSettingsReader; import pt.ua.dicoogle.sdk.task.JointQueryTask; import pt.ua.dicoogle.sdk.task.Task; @@ -214,21 +213,6 @@ public JointQueryTask query(JointQueryTask holder, List querySources, Di */ public List indexBlocking(URI path); - /** Gets the image worker with the given name. - * - * @param name the unique name of the worker - * @param onlyEnabled whether only enabled plugins should be retrieved (all are retrieved if {@code false}) - * @return the image worker with the given name or null if it doesn't exist - */ - public ImageWorkerInterface getImageWorkerByName(String name, boolean onlyEnabled); - - /** Gets all the image workers available. - * - * @param onlyEnabled whether only enabled plugins should be retrieved (all are retrieved if {@code false}) - * @return a collection with all image workers available - */ - public Collection getImageWorkers(boolean onlyEnabled); - /** Obtain access to the server's settings. * @return an object for read-only access to the settings */ From 29b6302afb12a4e758e15b58491b31805aad208d Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Sat, 26 Nov 2022 00:17:57 +0000 Subject: [PATCH 09/11] Finished implementation of ROI extractor. Added cache to make it faster. --- dicoogle/pom.xml | 5 + .../src/main/java/pt/ua/dicoogle/Main.java | 8 ++ .../server/web/dicom/ROIExtractor.java | 46 +------ .../server/web/servlets/ROIServlet.java | 69 ++++------ .../server/web/utils/cache/MemoryCache.java | 46 +++++++ .../server/web/utils/cache/WSICache.java | 125 ++++++++++++++++++ 6 files changed, 211 insertions(+), 88 deletions(-) create mode 100644 dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/MemoryCache.java create mode 100644 dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java diff --git a/dicoogle/pom.xml b/dicoogle/pom.xml index bbe7e8f2c..c98ff37b5 100644 --- a/dicoogle/pom.xml +++ b/dicoogle/pom.xml @@ -471,5 +471,10 @@ dcm4che-imageio 3.3.7 + + org.dcm4che + dcm4che-core + 3.3.7 + diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/Main.java b/dicoogle/src/main/java/pt/ua/dicoogle/Main.java index 7e55116af..0d6daac34 100755 --- a/dicoogle/src/main/java/pt/ua/dicoogle/Main.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/Main.java @@ -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; @@ -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; @@ -172,6 +175,11 @@ private static void LaunchDicoogle() { // Start the initial Services of Dicoogle pt.ua.dicoogle.server.ControlServices.getInstance(); + // Register Image Reader for DICOM Objects + 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. diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java index 5a73082e2..c18d7c334 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java @@ -1,47 +1,35 @@ package pt.ua.dicoogle.server.web.dicom; -import org.dcm4che3.data.Attributes; import org.dcm4che3.imageio.plugins.dcm.DicomImageReadParam; import org.dcm4che3.imageio.plugins.dcm.DicomImageReader; import org.dcm4che3.imageio.plugins.dcm.DicomMetaData; -import org.dcm4che3.io.BulkDataDescriptor; -import org.dcm4che3.io.DicomInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pt.ua.dicoogle.sdk.StorageInputStream; import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; import pt.ua.dicoogle.sdk.datastructs.dim.Point2D; import pt.ua.dicoogle.sdk.datastructs.wsi.WSIFrame; import pt.ua.dicoogle.sdk.datastructs.wsi.WSISopDescriptor; +import pt.ua.dicoogle.server.web.utils.cache.WSICache; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; -import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.zip.GZIPInputStream; public class ROIExtractor { private static final Logger logger = LoggerFactory.getLogger(ROIExtractor.class); - private static final String EXTENSION_GZIP = ".gz"; - private static final int BUFFER_SIZE = 8192; - public BufferedImage extractROI(StorageInputStream sis, BulkAnnotation bulkAnnotation) { + public BufferedImage extractROI(DicomMetaData dicomMetaData, BulkAnnotation bulkAnnotation) { ImageReader imageReader = getImageReader(); - DicomMetaData dicomMetaData; - try { - dicomMetaData = getDicomMetadata(sis); - } catch (IOException e) { - logger.error("Error reading DICOM file", e); + if(imageReader == null) return null; - } DicomImageReadParam param; try{ @@ -64,8 +52,6 @@ public BufferedImage extractROI(StorageInputStream sis, BulkAnnotation bulkAnnot return null; } - - private static ImageReader getImageReader() { Iterator iter = ImageIO.getImageReadersByFormatName("DICOM"); while (iter.hasNext()) { @@ -206,30 +192,4 @@ private BufferedImage getROIFromAnnotation(BulkAnnotation annotation, WSISopDesc g.dispose(); return combined; } - - private DicomMetaData getDicomMetadata(StorageInputStream sis) throws IOException{ - Attributes fmi; - Attributes dataset; - DicomInputStream dis; - - if(sis == null){ - logger.info("Storage == null"); - throw new InvalidParameterException("Could not find the desired URI"); - } - - String filePath = sis.getURI().getPath(); - if (filePath.endsWith(EXTENSION_GZIP)){ - InputStream inStream = new GZIPInputStream(new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE)); - dis = new DicomInputStream(inStream); - } - else { - dis = new DicomInputStream(new File(filePath)); - } - dis.setIncludeBulkData(DicomInputStream.IncludeBulkData.URI); - dis.setBulkDataDescriptor(BulkDataDescriptor.PIXELDATA); - fmi = dis.readFileMetaInformation(); - dataset = dis.readDataset(-1, -1); - return new DicomMetaData(fmi, dataset); - } - } diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java index 78d25ad72..8c38f2c73 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java @@ -1,18 +1,13 @@ package pt.ua.dicoogle.server.web.servlets; +import org.dcm4che3.imageio.plugins.dcm.DicomMetaData; import org.restlet.data.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pt.ua.dicoogle.core.settings.ServerSettingsManager; -import pt.ua.dicoogle.plugins.PluginController; -import pt.ua.dicoogle.sdk.QueryInterface; -import pt.ua.dicoogle.sdk.StorageInputStream; -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.utils.QueryException; import pt.ua.dicoogle.server.web.dicom.ROIExtractor; -import pt.ua.dicoogle.server.web.utils.LocalImageCache; +import pt.ua.dicoogle.server.web.utils.cache.WSICache; import javax.imageio.ImageIO; import javax.servlet.ServletException; @@ -26,13 +21,11 @@ public class ROIServlet extends HttpServlet { - private static final Logger logger = LoggerFactory.getLogger(ROIServlet.class); private static final long serialVersionUID = 1L; - private final String queryProvider; + private final WSICache wsiCache; private final ROIExtractor roiExtractor; - private HashMap extraFields; /** * Creates ROI servlet servlet. @@ -40,23 +33,13 @@ public class ROIServlet extends HttpServlet { */ public ROIServlet() { this.roiExtractor = new ROIExtractor(); - List dicomProviders = ServerSettingsManager.getSettings().getArchiveSettings().getDIMProviders(); - queryProvider = dicomProviders.iterator().next(); - - extraFields = new HashMap<>(); - 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"); + this.wsiCache = WSICache.getInstance(); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String sopInstanceUID = request.getParameter("uid"); String x = request.getParameter("x"); String y = request.getParameter("y"); @@ -73,6 +56,20 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) return; } + DicomMetaData dicomMetaData; + try { + dicomMetaData = getDicomMetadata(sopInstanceUID); + } catch (IOException e) { + logger.error("Error reading DICOM file", e); + response.sendError(Status.SERVER_ERROR_INTERNAL.getCode(), "There was an error reading the file"); + return; + } + + if(dicomMetaData == null){ + response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), String.format("No instances exist with SOPInstanceUID: %s", sopInstanceUID)); + return; + } + BulkAnnotation annotation; try{ int nX = Integer.parseInt(x); @@ -93,29 +90,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) return; } - QueryInterface queryInterface = PluginController.getInstance().getQueryProviderByName(queryProvider, false); - - String query = String.format("SOPInstanceUID:%s", sopInstanceUID); - Iterable results; - try { - results = queryInterface.query(query, extraFields); - } catch (QueryException e) { - logger.error("Error requesting ROI for image {}", sopInstanceUID, e); - response.sendError(Status.SERVER_ERROR_INTERNAL.getCode(), "Error building response"); - return; - } - - if(!results.iterator().hasNext()){ - response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), String.format("No instances exist with SOPInstanceUID: %s", sopInstanceUID)); - return; - } - - //We're only expecting one result - SearchResult result = results.iterator().next(); - - StorageInputStream sis = PluginController.getInstance().resolveURI(result.getURI()).iterator().next(); - - BufferedImage bi = roiExtractor.extractROI(sis, annotation); + BufferedImage bi = roiExtractor.extractROI(dicomMetaData, annotation); if(bi != null){ response.setContentType("image/jpeg"); @@ -128,4 +103,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), "Could not build ROI with the provided UID"); } + private DicomMetaData getDicomMetadata(String sop) throws IOException{ + return wsiCache.get(sop); + } + } diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/MemoryCache.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/MemoryCache.java new file mode 100644 index 000000000..dd7311f87 --- /dev/null +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/MemoryCache.java @@ -0,0 +1,46 @@ +package pt.ua.dicoogle.server.web.utils.cache; + +import com.google.common.cache.LoadingCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ExecutionException; + +/** + * General purpose memory cache to be used by plugins + * @author Rui Jesus + * @param + */ +public abstract class MemoryCache { + + protected LoadingCache memoryCache; + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + + protected int hoursToKeep = 12; + protected int maximumSize = 1000; + + protected MemoryCache() { + } + + protected MemoryCache(int hoursToKeep, int maximumSize) { + this.hoursToKeep = hoursToKeep; + this.maximumSize = maximumSize; + } + + public T get(String key){ + try { + return memoryCache.get(key); + } catch (ExecutionException e) { + logger.error("Error retrieving key {} from cache", key, e); + return null; + } + } + + public int getHoursToKeep() { + return hoursToKeep; + } + + public int getMaximumSize() { + return maximumSize; + } +} \ No newline at end of file diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java new file mode 100644 index 000000000..d882af317 --- /dev/null +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java @@ -0,0 +1,125 @@ +package pt.ua.dicoogle.server.web.utils.cache; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import org.dcm4che3.data.Attributes; +import org.dcm4che3.imageio.plugins.dcm.DicomMetaData; +import org.dcm4che3.io.BulkDataDescriptor; +import org.dcm4che3.io.DicomInputStream; +import pt.ua.dicoogle.core.settings.ServerSettingsManager; +import pt.ua.dicoogle.plugins.PluginController; +import pt.ua.dicoogle.sdk.QueryInterface; +import pt.ua.dicoogle.sdk.StorageInputStream; +import pt.ua.dicoogle.sdk.datastructs.SearchResult; +import pt.ua.dicoogle.sdk.utils.QueryException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URI; +import java.security.InvalidParameterException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; + +/** + * Cache used to store DicomMetadata objects temporarily, as they are quite heavy to build on-demand + */ +public class WSICache extends MemoryCache{ + + private static WSICache instance = null; + private static final String EXTENSION_GZIP = ".gz"; + private static final int BUFFER_SIZE = 8192; + + private QueryInterface queryInterface; + private final String queryProvider; + + private WSICache(){ + super(); + memoryCache = CacheBuilder.newBuilder() + .maximumSize(maximumSize) + .expireAfterAccess(hoursToKeep, TimeUnit.HOURS) + .build(new WsiDcmLoader()); + + List dicomProviders = ServerSettingsManager.getSettings().getArchiveSettings().getDIMProviders(); + queryProvider = dicomProviders.iterator().next(); + } + + public static synchronized WSICache getInstance(){ + if (instance==null){ + instance = new WSICache(); + } + return instance; + } + + private class WsiDcmLoader extends CacheLoader { + + @Override + public DicomMetaData load(String key) throws Exception { + + queryInterface = PluginController.getInstance().getQueryProviderByName(queryProvider, false); + + URI uri = retrieveURI(key); + if(uri == null){ + logger.info("URI == null"); + throw new InvalidParameterException("Could not find the desired URI"); + } + + Attributes fmi; + Attributes dataset; + DicomInputStream dis; + StorageInputStream sis = retrieveInputStream(uri); + + if(sis == null){ + throw new InvalidParameterException("Could not find the desired URI"); + } + + String filePath = sis.getURI().getPath(); + if (filePath.endsWith(EXTENSION_GZIP)){ + InputStream inStream = new GZIPInputStream(new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE)); + dis = new DicomInputStream(inStream); + } + else { + dis = new DicomInputStream(new File(filePath)); + } + + dis.setIncludeBulkData(DicomInputStream.IncludeBulkData.URI); + dis.setBulkDataDescriptor(BulkDataDescriptor.PIXELDATA); + fmi = dis.readFileMetaInformation(); + dataset = dis.readDataset(-1, -1); + return new DicomMetaData(fmi, dataset); + } + + } + + /** + * Helper method to retrieve the URI from SOPInstanceUID in lucene. + * + * @param sop SopInstanceUID + * @return uri of the SopInstance + */ + private URI retrieveURI(String sop){ + String query = "SOPInstanceUID:" + sop; + + Iterable results; + try { + results = queryInterface.query(query); + } catch (QueryException e) { + logger.error("Could not complete query:", e); + return null; + } + + for (SearchResult first : results) { + if (first != null) { + return first.getURI(); + } + } + return null; + } + + private StorageInputStream retrieveInputStream(URI uri){ + return PluginController.getInstance().resolveURI(uri).iterator().next(); + } + +} From 8e56a4a96acd53e1520a084ad5a2a353a1119df3 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Sun, 27 Nov 2022 00:06:09 +0000 Subject: [PATCH 10/11] Moved some code into roi extractor. --- .../server/web/dicom/ROIExtractor.java | 44 ++++++++++++++----- .../server/web/servlets/ROIServlet.java | 23 +--------- .../server/web/utils/cache/WSICache.java | 5 ++- .../dicoogle/sdk/datastructs/dim/Point2D.java | 3 ++ 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java index c18d7c334..f59b36a9a 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/dicom/ROIExtractor.java @@ -23,14 +23,31 @@ public class ROIExtractor { private static final Logger logger = LoggerFactory.getLogger(ROIExtractor.class); + private final WSICache wsiCache; - public BufferedImage extractROI(DicomMetaData dicomMetaData, BulkAnnotation bulkAnnotation) { + public ROIExtractor() { + this.wsiCache = WSICache.getInstance(); + } + + public BufferedImage extractROI(String sopInstanceUID, BulkAnnotation bulkAnnotation) { ImageReader imageReader = getImageReader(); if(imageReader == null) return null; + DicomMetaData dicomMetaData; + try { + dicomMetaData = getDicomMetadata(sopInstanceUID); + } catch (IOException e) { + logger.error("Error reading DICOM file", e); + return null; + } + + if(dicomMetaData == null){ + return null; + } + DicomImageReadParam param; try{ imageReader.setInput(dicomMetaData); @@ -72,26 +89,26 @@ private static ImageReader getImageReader() { private List> getFrameMatrixFromAnnotation(WSISopDescriptor descriptor, BulkAnnotation annotation) { //Number of tiles along the x direction, number of columns in the frame matrix - int nx_tiles = (int) Math.ceil(descriptor.getTotalPixelMatrixColumns() / descriptor.getTileWidth()); + int nx_tiles = (int) Math.ceil((descriptor.getTotalPixelMatrixColumns() * 1.0) / descriptor.getTileWidth()); //Number of tiles along the y direction, number of rows in the frame matrix - int ny_tiles = (int) Math.ceil(descriptor.getTotalPixelMatrixRows() / descriptor.getTileHeight()); + int ny_tiles = (int) Math.ceil((descriptor.getTotalPixelMatrixRows() * 1.0) / descriptor.getTileHeight()); List> matrix = new ArrayList<>(); switch (annotation.getAnnotationType()) { case RECTANGLE: // Calculate the starting position of the annotation in frame coordinates - int x_c = (int) Math.floor(annotation.getPoints().get(0).getX() / descriptor.getTileWidth()); - int y_c = (int) Math.floor(annotation.getPoints().get(0).getY() / descriptor.getTileHeight()); + int x_c = annotation.getPoints().get(0).getX() / descriptor.getTileWidth(); + int y_c = annotation.getPoints().get(0).getY() / descriptor.getTileHeight(); //Annotation is completely out of bounds, no intersection possible if(x_c > nx_tiles || y_c > ny_tiles) return matrix; // Calculate the ending position of the annotation in frame coordinates - int x_e = (int) Math.floor(annotation.getPoints().get(3).getX() / descriptor.getTileWidth()); - int y_e = (int) Math.floor(annotation.getPoints().get(3).getY() / descriptor.getTileHeight()); + int x_e = annotation.getPoints().get(3).getX() / descriptor.getTileWidth(); + int y_e = annotation.getPoints().get(3).getY() / descriptor.getTileHeight(); //Annotation might be out of bonds, adjust that if(x_e > (nx_tiles - 1)) @@ -115,12 +132,13 @@ private List> getFrameMatrixFromAnnotation(WSISopDescriptor descr /** * Given an annotation and an image, return the section of the image the annotation intersects. - * @param annotation - * @param descriptor + * It only works with rectangle type annotations. + * @param annotation the annotation to intersect + * @param descriptor descriptor of the WSI pyramid, contains information about the dimmensions of the image. * @param imageReader * @param param - * @return - * @throws IllegalArgumentException + * @return the intersection of the annotation on the image. + * @throws IllegalArgumentException when the annotation is not one of the supported types. */ private BufferedImage getROIFromAnnotation(BulkAnnotation annotation, WSISopDescriptor descriptor, ImageReader imageReader, DicomImageReadParam param) throws IllegalArgumentException { if(annotation.getAnnotationType() != BulkAnnotation.AnnotationType.RECTANGLE){ @@ -192,4 +210,8 @@ private BufferedImage getROIFromAnnotation(BulkAnnotation annotation, WSISopDesc g.dispose(); return combined; } + + private DicomMetaData getDicomMetadata(String sop) throws IOException{ + return wsiCache.get(sop); + } } diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java index 8c38f2c73..ad4f6ac4a 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java @@ -1,6 +1,5 @@ package pt.ua.dicoogle.server.web.servlets; -import org.dcm4che3.imageio.plugins.dcm.DicomMetaData; import org.restlet.data.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,7 +23,6 @@ public class ROIServlet extends HttpServlet { private static final Logger logger = LoggerFactory.getLogger(ROIServlet.class); private static final long serialVersionUID = 1L; - private final WSICache wsiCache; private final ROIExtractor roiExtractor; /** @@ -33,7 +31,6 @@ public class ROIServlet extends HttpServlet { */ public ROIServlet() { this.roiExtractor = new ROIExtractor(); - this.wsiCache = WSICache.getInstance(); } @Override @@ -56,20 +53,6 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) return; } - DicomMetaData dicomMetaData; - try { - dicomMetaData = getDicomMetadata(sopInstanceUID); - } catch (IOException e) { - logger.error("Error reading DICOM file", e); - response.sendError(Status.SERVER_ERROR_INTERNAL.getCode(), "There was an error reading the file"); - return; - } - - if(dicomMetaData == null){ - response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), String.format("No instances exist with SOPInstanceUID: %s", sopInstanceUID)); - return; - } - BulkAnnotation annotation; try{ int nX = Integer.parseInt(x); @@ -90,7 +73,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) return; } - BufferedImage bi = roiExtractor.extractROI(dicomMetaData, annotation); + BufferedImage bi = roiExtractor.extractROI(sopInstanceUID, annotation); if(bi != null){ response.setContentType("image/jpeg"); @@ -103,8 +86,4 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), "Could not build ROI with the provided UID"); } - private DicomMetaData getDicomMetadata(String sop) throws IOException{ - return wsiCache.get(sop); - } - } diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java index d882af317..8b4a7dea6 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/utils/cache/WSICache.java @@ -25,6 +25,7 @@ /** * Cache used to store DicomMetadata objects temporarily, as they are quite heavy to build on-demand + * @author Rui Jesus */ public class WSICache extends MemoryCache{ @@ -56,11 +57,11 @@ public static synchronized WSICache getInstance(){ private class WsiDcmLoader extends CacheLoader { @Override - public DicomMetaData load(String key) throws Exception { + public DicomMetaData load(String sopInstanceUID) throws Exception { queryInterface = PluginController.getInstance().getQueryProviderByName(queryProvider, false); - URI uri = retrieveURI(key); + URI uri = retrieveURI(sopInstanceUID); if(uri == null){ logger.info("URI == null"); throw new InvalidParameterException("Could not find the desired URI"); diff --git a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java index 63ff33017..e5d2294a7 100644 --- a/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java +++ b/sdk/src/main/java/pt/ua/dicoogle/sdk/datastructs/dim/Point2D.java @@ -1,5 +1,8 @@ package pt.ua.dicoogle.sdk.datastructs.dim; +/** + * A point in pixel coordinates, to be used to identify coordinates in pixel space. + */ public class Point2D { private int x; From f0aaa8377ab8c552150e42eb8f12fb62c2a45502 Mon Sep 17 00:00:00 2001 From: Rui Jesus Date: Wed, 30 Nov 2022 07:50:15 +0000 Subject: [PATCH 11/11] Removed unused import. --- .../java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java index ad4f6ac4a..638882bdd 100644 --- a/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java +++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/web/servlets/ROIServlet.java @@ -6,7 +6,6 @@ import pt.ua.dicoogle.sdk.datastructs.dim.BulkAnnotation; import pt.ua.dicoogle.sdk.datastructs.dim.Point2D; import pt.ua.dicoogle.server.web.dicom.ROIExtractor; -import pt.ua.dicoogle.server.web.utils.cache.WSICache; import javax.imageio.ImageIO; import javax.servlet.ServletException; @@ -86,4 +85,4 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) response.sendError(Status.CLIENT_ERROR_NOT_FOUND.getCode(), "Could not build ROI with the provided UID"); } -} +} \ No newline at end of file