|
5 | 5 | import com.google.gson.JsonObject; |
6 | 6 |
|
7 | 7 | import org.elephant.sam.entities.SAMOutput; |
| 8 | +import org.elephant.sam.http.MultipartBodyBuilder; |
8 | 9 | import org.locationtech.jts.geom.Coordinate; |
9 | 10 | import org.slf4j.Logger; |
10 | 11 | import org.slf4j.LoggerFactory; |
| 12 | + |
| 13 | +import qupath.lib.awt.common.AwtTools; |
11 | 14 | import qupath.lib.common.GeneralTools; |
12 | 15 | import qupath.lib.geom.Point2; |
13 | 16 | import qupath.lib.gui.images.servers.RenderedImageServer; |
|
18 | 21 | import qupath.lib.objects.PathObjectTools; |
19 | 22 | import qupath.lib.objects.classes.PathClass; |
20 | 23 | import qupath.lib.regions.ImagePlane; |
| 24 | +import qupath.lib.regions.ImageRegion; |
21 | 25 | import qupath.lib.regions.RegionRequest; |
22 | 26 | import qupath.lib.roi.RectangleROI; |
23 | 27 | import qupath.lib.roi.interfaces.ROI; |
@@ -92,10 +96,14 @@ private static PathObject parsePathObject(Gson gson, JsonElement element) { |
92 | 96 | if (jsonObj.has("properties")) { |
93 | 97 | // Set quality from properties if we can |
94 | 98 | JsonObject properties = jsonObj.get("properties").getAsJsonObject(); |
95 | | - JsonElement quality = properties.has("quality") ? properties.get("quality") : null; |
| 99 | + JsonElement quality = properties.get("quality"); |
96 | 100 | if (quality != null && quality.isJsonPrimitive()) { |
97 | 101 | pathObject.getMeasurementList().put(SAM_QUALITY_MEASUREMENT, quality.getAsDouble()); |
98 | 102 | } |
| 103 | + JsonElement objectId = properties.get("object_idx"); |
| 104 | + if (objectId != null && objectId.isJsonPrimitive()) { |
| 105 | + pathObject.setPathClass(PathClass.getInstance(objectId.getAsString())); |
| 106 | + } |
99 | 107 | } |
100 | 108 | return pathObject; |
101 | 109 | } |
@@ -154,7 +162,11 @@ public static PathObject applyTransformAndClassification(PathObject pathObject, |
154 | 162 | } |
155 | 163 | if (plane != null && !Objects.equals(plane, pathObject.getROI().getImagePlane())) |
156 | 164 | pathObject = PathObjectTools.updatePlane(pathObject, plane, true, false); |
157 | | - pathObject.setPathClass(pathClass); |
| 165 | + if (pathClass == PathClass.NULL_CLASS) { |
| 166 | + pathObject.resetPathClass(); |
| 167 | + } else if (pathClass != null) { |
| 168 | + pathObject.setPathClass(pathClass); |
| 169 | + } |
158 | 170 | return pathObject; |
159 | 171 | } |
160 | 172 |
|
@@ -214,6 +226,43 @@ public static ImageServer<BufferedImage> createRenderedServer(QuPathViewer viewe |
214 | 226 | .build(); |
215 | 227 | } |
216 | 228 |
|
| 229 | + /** |
| 230 | + * Get a region request for the viewer, intersected with the rendered server. |
| 231 | + * |
| 232 | + * @param viewer |
| 233 | + * @param renderedServer |
| 234 | + * @return the region request |
| 235 | + */ |
| 236 | + public static RegionRequest getViewerRegion(QuPathViewer viewer, ImageServer<BufferedImage> renderedServer) { |
| 237 | + return getViewerRegion(viewer, renderedServer, viewer.getZPosition(), viewer.getTPosition()); |
| 238 | + } |
| 239 | + |
| 240 | + /** |
| 241 | + * Get a region request for the viewer, intersected with the rendered server. |
| 242 | + * |
| 243 | + * @param viewer |
| 244 | + * @param renderedServer |
| 245 | + * @param z |
| 246 | + * @param t |
| 247 | + * @return the region request |
| 248 | + */ |
| 249 | + public static RegionRequest getViewerRegion(QuPathViewer viewer, ImageServer<BufferedImage> renderedServer, int z, |
| 250 | + int t) { |
| 251 | + if (renderedServer == null) { |
| 252 | + try { |
| 253 | + renderedServer = Utils.createRenderedServer(viewer); |
| 254 | + } catch (IOException e) { |
| 255 | + logger.error("Failed to create rendered server", e); |
| 256 | + } |
| 257 | + } |
| 258 | + // Find the region and downsample currently used within the viewer |
| 259 | + ImageRegion region = AwtTools.getImageRegion(viewer.getDisplayedRegionShape(), z, t); |
| 260 | + RegionRequest viewerRegion = RegionRequest.createInstance(renderedServer.getPath(), |
| 261 | + viewer.getDownsampleFactor(), region); |
| 262 | + viewerRegion = viewerRegion.intersect2D(0, 0, renderedServer.getWidth(), renderedServer.getHeight()); |
| 263 | + return viewerRegion; |
| 264 | + } |
| 265 | + |
217 | 266 | /** |
218 | 267 | * Encode a BufferedImage as a base64-encoded PNG. |
219 | 268 | * |
@@ -333,4 +382,30 @@ public static boolean isPotentialPromptObject(PathObject pathObject) { |
333 | 382 | return pathObject.isAnnotation() && pathObject.hasROI() && !pathObject.getROI().isEmpty(); |
334 | 383 | } |
335 | 384 |
|
| 385 | + /** |
| 386 | + * Converts a BufferedImage to a JPEG byte array. |
| 387 | + * |
| 388 | + * @param image |
| 389 | + * The BufferedImage to convert. |
| 390 | + * @return A byte array containing the JPEG data. |
| 391 | + * @throws IOException |
| 392 | + * If an error occurs during writing. |
| 393 | + */ |
| 394 | + public static byte[] bufferedImageToJpegBytes(BufferedImage image) throws IOException { |
| 395 | + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { |
| 396 | + ImageIO.write(image, "jpg", baos); |
| 397 | + return baos.toByteArray(); |
| 398 | + } |
| 399 | + } |
| 400 | + |
| 401 | + public static byte[] createImageUploadMultipartBody(String boundary, String dirname, String filename, |
| 402 | + BufferedImage image) |
| 403 | + throws IOException { |
| 404 | + byte[] imageBytes = bufferedImageToJpegBytes(image); |
| 405 | + final MultipartBodyBuilder builder = new MultipartBodyBuilder(boundary); |
| 406 | + builder.addFormField("dirname", dirname); |
| 407 | + builder.addFilePart("file", filename, "image/jpeg", imageBytes); |
| 408 | + return builder.build(); |
| 409 | + } |
| 410 | + |
336 | 411 | } |
0 commit comments