RED-8642: Use LineMode from cv-analysis-service instead of table cell mode

* initial changes
This commit is contained in:
maverickstuder 2024-03-07 13:24:44 +01:00
parent f146beeb44
commit e99fad0c3d
7 changed files with 49 additions and 77 deletions

View File

@ -29,13 +29,13 @@ import com.knecon.fforesight.service.layoutparser.processor.model.Classification
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document; import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage; import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage;
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings; import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPageBlock;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence; import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.CvTableParsingAdapter; import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.CvTableParsingAdapter;
import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.ImageServiceResponseAdapter; import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.ImageServiceResponseAdapter;
import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.VisualLayoutParsingAdapter; import com.knecon.fforesight.service.layoutparser.processor.python_api.adapter.VisualLayoutParsingAdapter;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingResponse; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingResponse;
import com.knecon.fforesight.service.layoutparser.processor.services.BodyTextFrameService; import com.knecon.fforesight.service.layoutparser.processor.services.BodyTextFrameService;
@ -219,7 +219,7 @@ public class LayoutParsingPipeline {
PDDocument originDocument = openDocument(originFile); PDDocument originDocument = openDocument(originFile);
addNumberOfPagesToTrace(originDocument.getNumberOfPages(), Files.size(originFile.toPath())); addNumberOfPagesToTrace(originDocument.getNumberOfPages(), Files.size(originFile.toPath()));
Map<Integer, List<TableCells>> pdfTableCells = cvTableParsingAdapter.buildCvParsedTablesPerPage(tableServiceResponse); Map<Integer, List<Ruling>> pdfTableRulings = cvTableParsingAdapter.buildCvParsedRulingsPerPage(tableServiceResponse);
Map<Integer, List<ClassifiedImage>> pdfImages = imageServiceResponseAdapter.buildClassifiedImagesPerPage(imageServiceResponse); Map<Integer, List<ClassifiedImage>> pdfImages = imageServiceResponseAdapter.buildClassifiedImagesPerPage(imageServiceResponse);
Map<Integer, List<ClassifiedImage>> signatures = visualLayoutParsingAdapter.buildExtractedSignaturesPerPage(visualLayoutParsingResponse); Map<Integer, List<ClassifiedImage>> signatures = visualLayoutParsingAdapter.buildExtractedSignaturesPerPage(visualLayoutParsingResponse);
ClassificationDocument classificationDocument = new ClassificationDocument(); ClassificationDocument classificationDocument = new ClassificationDocument();
@ -258,7 +258,7 @@ public class LayoutParsingPipeline {
boolean isLandscape = pdr.getWidth() > pdr.getHeight() && (rotation == 0 || rotation == 180) || pdr.getHeight() > pdr.getWidth() && (rotation == 90 || rotation == 270); boolean isLandscape = pdr.getWidth() > pdr.getHeight() && (rotation == 0 || rotation == 180) || pdr.getHeight() > pdr.getWidth() && (rotation == 90 || rotation == 270);
PDRectangle cropbox = pdPage.getCropBox(); PDRectangle cropbox = pdPage.getCropBox();
CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableCells.get(pageNumber), stripper.getRulings()); CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(pdfTableRulings.get(pageNumber), stripper.getRulings());
ClassificationPage classificationPage = switch (layoutParsingType) { ClassificationPage classificationPage = switch (layoutParsingType) {
case REDACT_MANAGER -> redactManagerBlockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical()); case REDACT_MANAGER -> redactManagerBlockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical());

View File

@ -36,6 +36,7 @@ public class LayoutParsingStorageService {
private final StorageService storageService; private final StorageService storageService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@Observed(name = "LayoutParsingStorageService", contextualName = "get-origin-file") @Observed(name = "LayoutParsingStorageService", contextualName = "get-origin-file")
public File getOriginFile(String storageId) throws IOException { public File getOriginFile(String storageId) throws IOException {
@ -61,28 +62,25 @@ public class LayoutParsingStorageService {
try (InputStream inputStream = getObject(storageId)) { try (InputStream inputStream = getObject(storageId)) {
ImageServiceResponse imageServiceResponse = objectMapper.readValue(inputStream, ImageServiceResponse.class); return objectMapper.readValue(inputStream, ImageServiceResponse.class);
inputStream.close();
return imageServiceResponse;
} }
} }
public TableServiceResponse getTablesFile(String storageId) throws IOException { public TableServiceResponse getTablesFile(String storageId) throws IOException {
try (var tableClassificationStream = getObject(storageId)) { try (InputStream tableClassificationStream = getObject(storageId)) {
TableServiceResponse tableServiceResponse = objectMapper.readValue(tableClassificationStream, TableServiceResponse.class); return objectMapper.readValue(tableClassificationStream, TableServiceResponse.class);
tableClassificationStream.close();
return tableServiceResponse;
} }
} }
public VisualLayoutParsingResponse getVisualLayoutParsingFile(String storageId) throws IOException { public VisualLayoutParsingResponse getVisualLayoutParsingFile(String storageId) throws IOException {
try (InputStream inputStream = getObject(storageId)) { try (InputStream inputStream = getObject(storageId)) {
VisualLayoutParsingResponse visualLayoutParsingResponse = objectMapper.readValue(inputStream, VisualLayoutParsingResponse.class);
return visualLayoutParsingResponse; return objectMapper.readValue(inputStream, VisualLayoutParsingResponse.class);
} }
} }

View File

@ -1,15 +1,19 @@
package com.knecon.fforesight.service.layoutparser.processor.python_api.adapter; package com.knecon.fforesight.service.layoutparser.processor.python_api.adapter;
import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.PageInfo; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.PageInfo;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCell;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableLine;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse; import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -20,33 +24,24 @@ import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor @RequiredArgsConstructor
public class CvTableParsingAdapter { public class CvTableParsingAdapter {
public Map<Integer, List<TableCells>> buildCvParsedTablesPerPage(TableServiceResponse tableServiceResponse) { public Map<Integer, List<Ruling>> buildCvParsedRulingsPerPage(TableServiceResponse tableServiceResponse) {
Map<Integer, List<TableCells>> tableCells = new HashMap<>(); Map<Integer, List<Ruling>> rulings = new HashMap<>();
tableServiceResponse.getData() tableServiceResponse.getData()
.forEach(tableData -> tableCells.computeIfAbsent(tableData.getPageInfo().getNumber(), tableCell -> new ArrayList<>()) .forEach(tableData -> rulings.computeIfAbsent(tableData.getPageInfo().getNumber(), ruling -> new ArrayList<>())
.addAll(convertTableCells(tableData.getTableCells(), tableData.getPageInfo()))); .addAll(convertTableLines(tableData.getTableLines(), tableData.getPageInfo())));
return tableCells; return rulings;
} }
private Collection<TableCells> convertTableCells(List<TableCells> tableCells, PageInfo pageInfo) { private Collection<Ruling> convertTableLines(List<TableLine> tableLines, PageInfo pageInfo) {
List<TableCells> cvParsedTableCells = new ArrayList<>(); List<Ruling> cvParsedRulings = new ArrayList<>();
tableCells.stream() tableLines.forEach(l -> cvParsedRulings.add(new Ruling(new Point2D.Double(l.getX0(), l.getY0()), new Point2D.Double(l.getX1(), l.getY1()))));
.filter(cell -> cell.getWidth() < pageInfo.getWidth() * 0.98 && cell.getHeight() < pageInfo.getHeight() * 0.98)
.forEach(t -> cvParsedTableCells.add(TableCells.builder()
.y0(t.getY0())
.x1(t.getX1())
.y1(t.getY1())
.x0(t.getX0())
.width(t.getWidth())
.height(t.getHeight())
.build()));
return cvParsedTableCells; return cvParsedRulings.stream().filter(ruling -> ruling.getWidth() < pageInfo.getWidth() * 0.98 && ruling.getHeight() < pageInfo.getHeight() * 0.98).toList();
} }
} }

View File

@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class TableCells { public class TableCell {
private float x0; private float x0;
private float y0; private float y0;

View File

@ -14,7 +14,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public class TableData { public class TableData {
private PageInfo pageInfo; private PageInfo pageInfo;;
private List<TableCells> tableCells = new ArrayList<>(); private List<TableLine> tableLines = new ArrayList<>();
} }

View File

@ -0,0 +1,19 @@
package com.knecon.fforesight.service.layoutparser.processor.python_api.model.table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TableLine {
private float x0;
private float y0;
private float x1;
private float y1;
}

View File

@ -14,7 +14,6 @@ import org.springframework.stereotype.Service;
import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings; import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Rectangle; import com.knecon.fforesight.service.layoutparser.processor.model.table.Rectangle;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling; import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableCells;
import com.knecon.fforesight.service.layoutparser.processor.utils.UnionFind; import com.knecon.fforesight.service.layoutparser.processor.utils.UnionFind;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -31,14 +30,15 @@ public class RulingCleaningService {
private static final float THRESHOLD_Y_HORIZONTAL = 3; private static final float THRESHOLD_Y_HORIZONTAL = 3;
public CleanRulings getCleanRulings(List<TableCells> tableCells, List<Ruling> rulings) { public CleanRulings getCleanRulings(List<Ruling> parsedRulings, List<Ruling> rulings) {
Rulings verticalAndHorizontalRulingLines; Rulings verticalAndHorizontalRulingLines;
// todo 8642: set cv parsed only when no rulings exist or just always add them?
if (!rulings.isEmpty()) { if (!rulings.isEmpty()) {
verticalAndHorizontalRulingLines = extractVerticalAndHorizontalRulingLines(rulings); verticalAndHorizontalRulingLines = extractVerticalAndHorizontalRulingLines(rulings);
} else { } else {
verticalAndHorizontalRulingLines = getRulingsFromParsedCells(tableCells); verticalAndHorizontalRulingLines = extractVerticalAndHorizontalRulingLines(parsedRulings);
} }
verticalAndHorizontalRulingLines.verticalLines.sort(X_FIRST_RULING_COMPARATOR); verticalAndHorizontalRulingLines.verticalLines.sort(X_FIRST_RULING_COMPARATOR);
@ -175,46 +175,6 @@ public class RulingCleaningService {
} }
private Rulings getRulingsFromParsedCells(List<TableCells> tableCells) {
List<Ruling> vrs = extractVerticalRulingsFromParsedCells(tableCells);
List<Ruling> hrs = extractHorizontalRulingsFromParsedCells(tableCells);
return new Rulings(vrs, hrs);
}
private List<Ruling> extractVerticalRulingsFromParsedCells(List<TableCells> tableCells) {
List<Ruling> vrs = new ArrayList<>();
if (tableCells != null) {
for (TableCells tableCell : tableCells) {
Ruling leftLine = createRuling(tableCell.getX0(), tableCell.getX0(), tableCell.getY0(), tableCell.getY1());
Ruling rightLine = createRuling(tableCell.getX1(), tableCell.getX1(), tableCell.getY0(), tableCell.getY1());
vrs.add(leftLine);
vrs.add(rightLine);
}
}
return vrs;
}
private List<Ruling> extractHorizontalRulingsFromParsedCells(List<TableCells> tableCells) {
List<Ruling> hrs = new ArrayList<>();
if (tableCells != null) {
for (TableCells tableCell : tableCells) {
Ruling topLine = createRuling(tableCell.getX0(), tableCell.getX1(), tableCell.getY1(), tableCell.getY1());
Ruling baseLine = createRuling(tableCell.getX0(), tableCell.getX1(), tableCell.getY0(), tableCell.getY0());
hrs.add(topLine);
hrs.add(baseLine);
}
}
return hrs;
}
private Ruling createRuling(float tableCellX0, float tableCellX1, float tableCellY0, float tableCellY1) { private Ruling createRuling(float tableCellX0, float tableCellX1, float tableCellY0, float tableCellY1) {
float x0 = tableCellX0; float x0 = tableCellX0;