RSS-83: Some ideas

This commit is contained in:
deiflaender 2022-10-27 09:57:06 +02:00
parent 3269f026f3
commit 811585d0ea
6 changed files with 201 additions and 54 deletions

View File

@ -3,6 +3,7 @@ package com.iqser.red.service.redaction.v1.server.classification.service;
import static java.util.stream.Collectors.toSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@ -15,6 +16,7 @@ import com.iqser.red.service.redaction.v1.server.classification.model.Page;
import com.iqser.red.service.redaction.v1.server.classification.model.StringFrequencyCounter;
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
import com.iqser.red.service.redaction.v1.server.classification.utils.RulingTextDirAdjustUtil;
import com.iqser.red.service.redaction.v1.server.classification.utils.TextBlockComparator;
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.Ruling;
@ -38,7 +40,7 @@ public class BlockificationService {
public Page blockify(List<TextPositionSequence> textPositions, List<Ruling> horizontalRulingLines, List<Ruling> verticalRulingLines) {
List<TextPositionSequence> chunkWords = new ArrayList<>();
List<AbstractTextContainer> chunkBlockList1 = new ArrayList<>();
List<TextBlock> chunkBlockList1 = new ArrayList<>();
float minX = 1000, maxX = 0, minY = 1000, maxY = 0;
TextPositionSequence prev = null;
@ -50,13 +52,14 @@ public class BlockificationService {
boolean lineSeparation = word.getMinYDirAdj() - maxY > word.getHeight() * 1.25;
boolean startFromTop = prev != null && word.getMinYDirAdj() < prev.getMinYDirAdj() - prev.getTextHeight();
boolean splitByX = prev != null && maxX + 50 < word.getMinXDirAdj() && prev.getMinYDirAdj() == word.getMinYDirAdj();
boolean xIsBeforeFirstX = prev != null && word.getMinXDirAdj() < minX;
boolean xIsBeforeFirstX = prev != null && word.getMinXDirAdj() < minX - 10;
boolean newLineAfterSplit = prev != null && word.getMinYDirAdj() != prev.getMinYDirAdj() && wasSplitted && splitX1 != word.getMinXDirAdj();
boolean isSplitByRuling = isSplitByRuling(minX, minY, maxX, maxY, word, horizontalRulingLines, verticalRulingLines);
boolean splitByDir = prev != null && !prev.getDir().equals(word.getDir());
boolean splitByOtherFontAndOtherY = prev != null && prev.getMaxYDirAdj() != word.getMaxYDirAdj() && (word.getFontStyle().contains("bold") && !prev.getFontStyle().contains("bold") || prev.getFontStyle().contains("bold") && !word.getFontStyle().contains("bold"));
boolean isOtherFont = prev != null && prev.getMaxYDirAdj() != word.getMaxYDirAdj() && Math.abs(Math.floor(word.getFontSize()) - Math.floor(prev.getFontSize())) > 1;
if (prev != null && (lineSeparation || startFromTop || splitByDir || isSplitByRuling || splitByOtherFontAndOtherY)) {
if (prev != null && (isOtherFont || lineSeparation || startFromTop || splitByDir || isSplitByRuling || splitByOtherFontAndOtherY || splitByX || xIsBeforeFirstX)) {
Orientation prevOrientation = null;
if (!chunkBlockList1.isEmpty()) {
@ -108,53 +111,61 @@ public class BlockificationService {
chunkBlockList1.add(cb1);
}
// Iterator<AbstractTextContainer> itty = chunkBlockList1.iterator();
//
// TextBlock previousLeft = null;
// TextBlock previousRight = null;
// while (itty.hasNext()) {
// TextBlock block = (TextBlock) itty.next();
//
// if (previousLeft != null && block.getOrientation().equals(Orientation.LEFT)) {
// if (previousLeft.getMinY() > block.getMinY() && block.getMaxY() + block.getMostPopularWordHeight() > previousLeft.getMinY()) {
// previousLeft.add(block);
// itty.remove();
// continue;
// }
// }
//
// if (previousRight != null && block.getOrientation().equals(Orientation.RIGHT)) {
// if (previousRight.getMinY() > block.getMinY() && block.getMaxY() + block.getMostPopularWordHeight() > previousRight.getMinY()) {
// previousRight.add(block);
// itty.remove();
// continue;
// }
// }
//
// if (block.getOrientation().equals(Orientation.LEFT)) {
// previousLeft = block;
// } else if (block.getOrientation().equals(Orientation.RIGHT)) {
// previousRight = block;
// }
// }
//
// itty = chunkBlockList1.iterator();
// TextBlock previous = null;
// while (itty.hasNext()) {
// TextBlock block = (TextBlock) itty.next();
//
// if (previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation().equals(Orientation.LEFT) && equalsWithThreshold(block.getMaxY(),
// previous.getMaxY()) || previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation()
// .equals(Orientation.RIGHT) && equalsWithThreshold(block.getMaxY(), previous.getMaxY())) {
// previous.add(block);
// itty.remove();
// continue;
// }
//
// previous = block;
// }
Iterator<TextBlock> itty = chunkBlockList1.iterator();
return new Page(chunkBlockList1);
TextBlock previousLeft = null;
TextBlock previousRight = null;
while (itty.hasNext()) {
TextBlock block = (TextBlock) itty.next();
if (previousLeft != null && block.getOrientation().equals(Orientation.LEFT)) {
if (previousLeft.getMinY() > block.getMinY() && block.getMaxY() + block.getMostPopularWordHeight() > previousLeft.getMinY()) {
previousLeft.add(block);
itty.remove();
continue;
}
}
if (previousRight != null && block.getOrientation().equals(Orientation.RIGHT)) {
if (previousRight.getMinY() > block.getMinY() && block.getMaxY() + block.getMostPopularWordHeight() > previousRight.getMinY()) {
previousRight.add(block);
itty.remove();
continue;
}
}
if (block.getOrientation().equals(Orientation.LEFT)) {
previousLeft = block;
} else if (block.getOrientation().equals(Orientation.RIGHT)) {
previousRight = block;
}
}
itty = chunkBlockList1.iterator();
TextBlock previous = null;
while (itty.hasNext()) {
TextBlock block = (TextBlock) itty.next();
if (previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation().equals(Orientation.LEFT) && equalsWithThreshold(block.getMaxY(),
previous.getMaxY()) || previous != null && previous.getOrientation().equals(Orientation.LEFT) && block.getOrientation()
.equals(Orientation.RIGHT) && equalsWithThreshold(block.getMaxY(), previous.getMaxY())) {
previous.add(block);
itty.remove();
continue;
}
previous = block;
}
//-----
// Collections.sort(chunkBlockList1, new TextBlockComparator());
List<AbstractTextContainer> pl = new ArrayList<>();
pl.addAll(chunkBlockList1);
return new Page(pl);
}

View File

@ -0,0 +1,40 @@
package com.iqser.red.service.redaction.v1.server.classification.utils;
import java.util.Comparator;
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
public class TextBlockComparator implements Comparator<TextBlock> {
@Override
public int compare(TextBlock pos1, TextBlock pos2) {
// only compare text that is in the same direction
int cmp1 = Float.compare(pos1.getDir().getDegrees(), pos2.getDir().getDegrees());
if (cmp1 != 0) {
return cmp1;
}
// get the text direction adjusted coordinates
float x1 = pos1.getMinX();
float x2 = pos2.getMinX();
float pos1YBottom = pos1.getPdfMaxY();
float pos2YBottom = pos2.getPdfMaxY();
// note that the coordinates have been adjusted so 0,0 is in upper left
float pos1YTop = pos1YBottom - pos1.getHeight();
float pos2YTop = pos2YBottom - pos2.getHeight();
float yDifference = Math.abs(pos1YBottom - pos2YBottom);
// we will do a simple tolerance comparison
if (yDifference < .1 || pos2YBottom >= pos1YTop && pos2YBottom <= pos1YBottom || pos1YBottom >= pos2YTop && pos1YBottom <= pos2YBottom) {
return Float.compare(x1, x2);
} else if (pos1YBottom < pos2YBottom) {
return -1;
} else {
return 1;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.iqser.red.service.redaction.v1.server.classification.utils;
import java.util.Comparator;
import java.util.regex.Matcher;
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.utils.Patterns;
public class TextPositionSequenceComparator implements Comparator<TextPositionSequence>
{
@Override
public int compare(TextPositionSequence pos1, TextPositionSequence pos2)
{
// only compare text that is in the same direction
int cmp1 = Float.compare(pos1.getDir().getDegrees(), pos2.getDir().getDegrees());
if (cmp1 != 0)
{
return cmp1;
}
// get the text direction adjusted coordinates
float x1 = pos1.getMinXDirAdj();
float x2 = pos2.getMinXDirAdj();
float pos1YBottom = pos1.getMaxYDirAdj();
float pos2YBottom = pos2.getMaxYDirAdj();
// note that the coordinates have been adjusted so 0,0 is in upper left
float pos1YTop = pos1YBottom - pos1.getHeight();
float pos2YTop = pos2YBottom - pos2.getHeight();
float yDifference = Math.abs(pos1YBottom - pos2YBottom);
// we will do a simple tolerance comparison
if (yDifference < .1 ||
pos2YBottom >= pos1YTop && pos2YBottom <= pos1YBottom ||
pos1YBottom >= pos2YTop && pos1YBottom <= pos2YBottom)
{
return Float.compare(x1, x2);
}
else if (pos1YBottom < pos2YBottom)
{
return -1;
}
else
{
return 1;
}
}
}

View File

@ -9,10 +9,13 @@ import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
@ -27,10 +30,12 @@ import com.iqser.red.service.redaction.v1.server.classification.model.Page;
import com.iqser.red.service.redaction.v1.server.classification.model.TextBlock;
import com.iqser.red.service.redaction.v1.server.classification.service.BlockificationService;
import com.iqser.red.service.redaction.v1.server.classification.service.ClassificationService;
import com.iqser.red.service.redaction.v1.server.classification.utils.TextPositionSequenceComparator;
import com.iqser.red.service.redaction.v1.server.parsing.PDFLinesTextStripper;
import com.iqser.red.service.redaction.v1.server.parsing.model.TextPositionSequence;
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfImage;
import com.iqser.red.service.redaction.v1.server.redaction.model.PdfTableCell;
import com.iqser.red.service.redaction.v1.server.redaction.utils.Patterns;
import com.iqser.red.service.redaction.v1.server.settings.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.AbstractTextContainer;
import com.iqser.red.service.redaction.v1.server.tableextraction.model.CleanRulings;
@ -108,6 +113,7 @@ public class PdfSegmentationService {
stripper.getRulings(),
stripper.getMinCharWidth(),
stripper.getMaxCharHeight());
// Collections.sort(stripper.getTextPositionSequences(), new TextPositionSequenceComparator());
Page page = blockificationService.blockify(stripper.getTextPositionSequences(), cleanRulings.getHorizontal(), cleanRulings.getVertical());
page.setRotation(rotation);
@ -125,6 +131,28 @@ public class PdfSegmentationService {
imageService.findOcr(page);
}
// var pattern = Patterns.getCompiledPattern("^\\d[\\d.]{0,5}", true);
// page.getTextBlocks().sort(Comparator.comparing(a -> a.getMinY(), Comparator.reverseOrder()));
//
// AbstractTextContainer prev = null;
// List<AbstractTextContainer> toRemove = new ArrayList<>();
// for (AbstractTextContainer current : page.getTextBlocks()) {
//
// if(prev != null && prev instanceof TextBlock && current instanceof TextBlock && Math.abs(prev.getMinY() - current.getMinY()) <=1){
// Matcher matcher = pattern.matcher(prev.getText());
// if (matcher.matches()) {
// ((TextBlock) current).getSequences().add(0, ((TextBlock) prev).getSequences().get(0));
// toRemove.add(prev);
// current.setMinX(prev.getMinX());
// }
// }
// prev = current;
// }
//
// page.getTextBlocks().removeAll(toRemove);
pages.add(page);
}

View File

@ -48,7 +48,7 @@ public class PdfVisualisationService {
}
if (textBlock instanceof TextBlock) {
textBlock.setClassification((i + 1) + "/" + paragraph.getPageBlocks().size());
visualizeTextBlock((TextBlock) textBlock, contentStream);
visualizeTextBlock((TextBlock) textBlock, contentStream, 0);
} else if (textBlock instanceof Table) {
textBlock.setClassification((i + 1) + "/" + paragraph.getPageBlocks().size());
visualizeTable((Table) textBlock, contentStream);
@ -71,15 +71,17 @@ public class PdfVisualisationService {
PDPage pdPage = document.getPage(page - 1);
PDPageContentStream contentStream = new PDPageContentStream(document, pdPage, PDPageContentStream.AppendMode.APPEND, true);
int tbnumber = 0;
for (AbstractTextContainer textBlock : analyzedPage.getTextBlocks()) {
if (textBlock == null) {
continue;
}
if (textBlock instanceof TextBlock) {
visualizeTextBlock((TextBlock) textBlock, contentStream);
visualizeTextBlock((TextBlock) textBlock, contentStream, tbnumber);
} else if (textBlock instanceof Table) {
visualizeTable((Table) textBlock, contentStream);
}
tbnumber++;
}
contentStream.setStrokingColor(Color.YELLOW);
@ -95,7 +97,7 @@ public class PdfVisualisationService {
}
private void visualizeTextBlock(TextBlock textBlock, PDPageContentStream contentStream) throws IOException {
private void visualizeTextBlock(TextBlock textBlock, PDPageContentStream contentStream, int number) throws IOException {
contentStream.setStrokingColor(Color.RED);
@ -109,7 +111,7 @@ public class PdfVisualisationService {
contentStream.setFont(PDType1Font.TIMES_ROMAN, 9f);
contentStream.newLineAtOffset(textBlock.getPdfMinX(), textBlock.getPdfMaxY() + 2);
contentStream.showText(textBlock.getClassification() + textBlock.getOrientation() + "-->" + textBlock.getSequences().get(0).getDir());
contentStream.showText(number + ": " +textBlock.getClassification() + textBlock.getOrientation() + "-->" + textBlock.getSequences().get(0).getDir());
contentStream.endText();

View File

@ -143,7 +143,7 @@ public class HeadlinesGoldStandardIntegrationTest {
@Test
public void extractHeadlines() throws IOException {
AnalyzeRequest request = prepareStorage("files/RSS/26 - Sedaxane - Acute Oral Toxicity - Rat.pdf");
AnalyzeRequest request = prepareStorage("files/RSS/08 - Acute Oral Toxicity Up and Down Procedur.pdf");
analyzeService.analyzeDocumentStructure(new StructureAnalyzeRequest(request.getDossierId(), request.getFileId()));
analyzeService.analyze(request);