diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/nodes/SemanticNode.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/nodes/SemanticNode.java index 988fc37f..3e18869b 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/nodes/SemanticNode.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/nodes/SemanticNode.java @@ -465,7 +465,7 @@ public interface SemanticNode { private Map getBBoxFromLeafTextBlock(Map bBoxPerPage) { Map> atomicTextBlockPerPage = getTextBlock().getAtomicTextBlocks().stream().collect(Collectors.groupingBy(AtomicTextBlock::getPage)); - atomicTextBlockPerPage.forEach((page, atbs) -> bBoxPerPage.put(page, RectangleTransformations.bBoxUnionAtomicTextBlock(atbs))); + atomicTextBlockPerPage.forEach((page, atbs) -> bBoxPerPage.put(page, RectangleTransformations.atomicTextBlockBBox(atbs))); return bBoxPerPage; } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/textblock/AtomicTextBlock.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/textblock/AtomicTextBlock.java index 1926192f..7a7376ec 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/textblock/AtomicTextBlock.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/graph/textblock/AtomicTextBlock.java @@ -191,7 +191,7 @@ public class AtomicTextBlock implements TextBlock { List rectanglesPerLine = stringBoundary.split(getAllLineBreaksInBoundary(stringBoundary)) .stream() .map(this::getPositions) - .map(RectangleTransformations::rectangleUnionWithGaps) + .map(RectangleTransformations::rectangleBBoxWithGaps) .flatMap(Collection::stream) .toList(); Map> rectanglePerLinePerPage = new HashMap<>(); diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/utils/RectangleTransformations.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/utils/RectangleTransformations.java index 9aa2d2f0..fdc3639f 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/utils/RectangleTransformations.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/layoutparsing/document/utils/RectangleTransformations.java @@ -1,6 +1,5 @@ package com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils; -import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.util.Collections; @@ -19,11 +18,14 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlo import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.Rectangle; import com.iqser.red.service.redaction.v1.server.layoutparsing.document.graph.textblock.AtomicTextBlock; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + public class RectangleTransformations { - public static PDRectangle toPDRectangleUnion(List rectangles) { + public static PDRectangle toPDRectangleBBox(List rectangles) { - Rectangle2D rectangle2D = RectangleTransformations.bBoxUnionRectangle(rectangles); + Rectangle2D rectangle2D = RectangleTransformations.rectangleBBox(rectangles); PDRectangle annotationPosition = new PDRectangle(); annotationPosition.setLowerLeftX((float) rectangle2D.getMinX()); @@ -34,15 +36,15 @@ public class RectangleTransformations { } - public static Rectangle2D bBoxUnionAtomicTextBlock(List atomicTextBlocks) { + public static Rectangle2D atomicTextBlockBBox(List atomicTextBlocks) { - return atomicTextBlocks.stream().flatMap(atomicTextBlock -> atomicTextBlock.getPositions().stream()).collect(new Rectangle2DUnion()); + return atomicTextBlocks.stream().flatMap(atomicTextBlock -> atomicTextBlock.getPositions().stream()).collect(new Rectangle2DBBoxCollector()); } - public static Rectangle2D bBoxUnionRectangle(List rectangles) { + public static Rectangle2D rectangleBBox(List rectangles) { - return rectangles.stream().map(RectangleTransformations::toRectangle2D).collect(new Rectangle2DUnion()); + return rectangles.stream().map(RectangleTransformations::toRectangle2D).collect(new Rectangle2DBBoxCollector()); } @@ -64,9 +66,9 @@ public class RectangleTransformations { } - public static Rectangle2D rectangleUnion(List rectangle2DList) { + public static Rectangle2D rectangle2DBBox(List rectangle2DList) { - return rectangle2DList.stream().collect(new Rectangle2DUnion()); + return rectangle2DList.stream().collect(new Rectangle2DBBoxCollector()); } @@ -76,7 +78,7 @@ public class RectangleTransformations { * @param rectangle2DList A list of rectangles to combine * @return A list of rectangles which are combined if they are closer than the split threshold */ - public static List rectangleUnionWithGaps(List rectangle2DList) { + public static List rectangleBBoxWithGaps(List rectangle2DList) { if (rectangle2DList.isEmpty()) { return Collections.emptyList(); @@ -98,47 +100,85 @@ public class RectangleTransformations { previousRectangle = currentRectangle; } } - return rectangleListsWithGaps.stream().map(RectangleTransformations::rectangleUnion).toList(); + return rectangleListsWithGaps.stream().map(RectangleTransformations::rectangle2DBBox).toList(); } - private static class Rectangle2DUnion implements Collector { + private static class Rectangle2DBBoxCollector implements Collector { @Override - public Supplier supplier() { + public Supplier supplier() { - return Area::new; + return BBox::new; } @Override - public BiConsumer accumulator() { + public BiConsumer accumulator() { - return (area, rectangle2D) -> area.add(new Area(rectangle2D)); + return (bb, rect) -> bb.addRectangle(rect.getMinX(), rect.getMinY(), rect.getMaxX(), rect.getMaxY()); } @Override - public BinaryOperator combiner() { + public BinaryOperator combiner() { - return (area1, area2) -> { - area1.add(area2); - return area1; - }; + return (b1, b2) -> new BBox(Math.min(b1.lowerLeftX, b2.lowerLeftX), + Math.min(b1.lowerLeftY, b2.lowerLeftY), + Math.max(b1.upperRightX, b2.upperRightX), + Math.max(b1.upperRightY, b2.upperRightY)); } @Override - public Function finisher() { + public Function finisher() { - return Area::getBounds2D; + return bb -> new Rectangle2D.Double(bb.lowerLeftX, bb.lowerLeftY, bb.upperRightX - bb.lowerLeftX, bb.upperRightY - bb.lowerLeftY); } @Override public Set characteristics() { - return Set.of(Characteristics.CONCURRENT, Characteristics.UNORDERED); + return Set.of(Characteristics.UNORDERED); + } + + + @AllArgsConstructor + @NoArgsConstructor + private static class BBox { + + Double lowerLeftX; + Double lowerLeftY; + Double upperRightX; + Double upperRightY; + + + public void addRectangle(double lowerLeftX, double lowerLeftY, double upperRightX, double upperRightY) { + + if (this.lowerLeftX == null) { + this.lowerLeftX = lowerLeftX; + } else if (this.lowerLeftX > lowerLeftX) { + this.lowerLeftX = lowerLeftX; + } + if (this.lowerLeftY == null) { + this.lowerLeftY = lowerLeftY; + } else if (this.lowerLeftY > lowerLeftY) { + this.lowerLeftY = lowerLeftY; + } + if (this.upperRightX == null) { + this.upperRightX = upperRightX; + } else if (this.upperRightX < upperRightX) { + this.upperRightX = upperRightX; + } + if (this.upperRightY == null) { + this.upperRightY = upperRightY; + } else if (this.upperRightY < upperRightY) { + this.upperRightY = upperRightY; + } + + } + } } diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java index 046fec99..0fbc010b 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/annotate/AnnotationService.java @@ -1,6 +1,6 @@ package com.iqser.red.service.redaction.v1.server.annotate; -import static com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RectangleTransformations.toPDRectangleUnion; +import static com.iqser.red.service.redaction.v1.server.layoutparsing.document.utils.RectangleTransformations.toPDRectangleBBox; import java.awt.Color; import java.awt.geom.Rectangle2D; @@ -136,7 +136,7 @@ public class AnnotationService { PDAnnotationTextMarkup annotation = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); annotation.constructAppearances(); - PDRectangle pdRectangle = toPDRectangleUnion(rectangles); + PDRectangle pdRectangle = toPDRectangleBBox(rectangles); annotation.setRectangle(pdRectangle); annotation.setQuadPoints(Floats.toArray(toQuadPoints(rectangles))); if (!redactionLogEntry.isHint()) {