Skip to content
Snippets Groups Projects
Commit 84e84274 authored by Yonatan's avatar Yonatan
Browse files

Refactor and unit grouping code separation (WIP)

parent fca43b2a
No related branches found
No related tags found
1 merge request!1Final merge
Showing
with 153 additions and 76 deletions
package es.yonatan.tfg;
import es.yonatan.tfg.boxing.HasBoundingBox;
import es.yonatan.tfg.boxing.UnitGroup;
import es.yonatan.tfg.boxing.Unit;
import es.yonatan.tfg.recognizer.ColorRecognizer;
import es.yonatan.tfg.recognizer.TesseractRecognizer;
import es.yonatan.tfg.model.HasBoundingBox;
import es.yonatan.tfg.model.Unit;
import es.yonatan.tfg.model.UnitGroup;
import es.yonatan.tfg.recognizer.color.ColorRecognizer;
import es.yonatan.tfg.recognizer.unit.TesseractRecognizer;
import es.yonatan.tfg.util.HashGrid2D;
import es.yonatan.tfg.util.SimpleHashGrid2D;
import net.sourceforge.tess4j.util.ImageHelper;
import javax.imageio.ImageIO;
......@@ -32,12 +33,12 @@ public class Main {
.setDataPath(new File("C:/Proyectos/TFG/src/main/resources/tessdata/"))
.setLanguage("spa")
//.excludeNumbers()
.setVariable("user_defined_dpi", "300")
.setVariable("user_defined_dpi", "300") // TODO: 10/04/2023 300 dpi is too high, test lower dpi and see how it affects the results and completion time
.build();
var words = tess.recognize(image, 80);
var words = tess.recognizeSync(image, 80);
var grid = new HashGrid2D<>(image.getWidth(), image.getHeight(), 4, 4);
var grid = new SimpleHashGrid2D<>(image.getWidth(), image.getHeight(), 4, 4);
words.forEach(grid::insert);
......@@ -91,6 +92,7 @@ public class Main {
System.out.println(System.currentTimeMillis() - time);
}
// TODO: 10/04/2023 move the unit merging logic into a separate class
private static void merge(HashGrid2D<HasBoundingBox> grid) {
var checked = new HashSet<HasBoundingBox>();
......
package es.yonatan.tfg.util;
package es.yonatan.tfg.model;
import org.jetbrains.annotations.NotNull;
......
package es.yonatan.tfg.boxing;
package es.yonatan.tfg.model;
import es.yonatan.tfg.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
......
package es.yonatan.tfg.boxing;
package es.yonatan.tfg.model;
import es.yonatan.tfg.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
......
package es.yonatan.tfg.boxing;
package es.yonatan.tfg.model;
import es.yonatan.tfg.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
......
package es.yonatan.tfg.recognizer;
import java.awt.*;
import java.io.File;
public interface IColorRecognizer {
Color getColorAt(File image, int u, int v, int width, int height);
}
package es.yonatan.tfg.recognizer;
import es.yonatan.tfg.boxing.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.image.BufferedImage;
import java.util.List;
public interface IRecognizer {
default @Nullable List<Unit> recognize(@NotNull BufferedImage image, double minConfidence) {
return recognize(List.of(image), minConfidence);
}
@Nullable List<Unit> recognize(@NotNull List<BufferedImage> images, double minConfidence);
}
package es.yonatan.tfg.recognizer;
package es.yonatan.tfg.recognizer.color;
import es.yonatan.tfg.model.ColorData;
import java.awt.*;
import java.io.File;
......@@ -6,8 +8,8 @@ import java.io.File;
public class ColorRecognizer implements IColorRecognizer {
@Override
public Color getColorAt(File image, int u, int v, int width, int height) {
public ColorData getColorAt(File image, int u, int v, int width, int height) {
// TODO: 4/04/2023 get primary color of image using leptonica library lept4j
return Color.WHITE;
return new ColorData(Color.BLACK, Color.WHITE);
}
}
package es.yonatan.tfg.recognizer.color;
import es.yonatan.tfg.model.ColorData;
import java.io.File;
public interface IColorRecognizer {
ColorData getColorAt(File image, int u, int v, int width, int height);
}
package es.yonatan.tfg.recognizer.group;
import es.yonatan.tfg.model.Unit;
import es.yonatan.tfg.model.UnitGroup;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface IUnitGrouper<In extends Unit, Out extends UnitGroup> {
@NotNull List<Out> group(@NotNull List<In> unit);
}
package es.yonatan.tfg.recognizer;
package es.yonatan.tfg.recognizer.unit;
public enum Accuracy {
LOW_FAST,
......
package es.yonatan.tfg.recognizer.unit;
import es.yonatan.tfg.model.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Interface for unit recognizers, common methods allow for any OCR library to be implemented on top of the application
* Default recognition mode is asynchronous, but synchronous mode is also available
*/
public interface IUnitRecognizer<T extends Unit> {
Executor DEFAULT_EXECUTOR = Executors.newSingleThreadExecutor();
default @Nullable List<T> recognizeSync(@NotNull BufferedImage image, double minConfidence) {
return recognizeSync(List.of(image), minConfidence);
}
@Nullable List<T> recognizeSync(@NotNull List<BufferedImage> images, double minConfidence);
default @Nullable CompletableFuture<List<T>> recognize(@NotNull BufferedImage image, double minConfidence) {
return recognize(image, minConfidence, DEFAULT_EXECUTOR);
}
default @Nullable CompletableFuture<List<T>> recognize(@NotNull BufferedImage image, double minConfidence, Executor threadExecutor) {
return recognize(List.of(image), minConfidence, threadExecutor);
}
default @Nullable CompletableFuture<List<T>> recognize(@NotNull List<BufferedImage> images, double minConfidence) {
return recognize(images, minConfidence, DEFAULT_EXECUTOR);
}
default @Nullable CompletableFuture<List<T>> recognize(@NotNull List<BufferedImage> images, double minConfidence, Executor threadExecutor) {
return CompletableFuture.supplyAsync(() -> recognizeSync(images, minConfidence), threadExecutor);
}
}
package es.yonatan.tfg.recognizer;
package es.yonatan.tfg.recognizer.unit;
import es.yonatan.tfg.boxing.Unit;
import es.yonatan.tfg.model.Unit;
import es.yonatan.tfg.util.BoundingBox;
import net.sourceforge.tess4j.ITessAPI;
import net.sourceforge.tess4j.Tesseract;
......@@ -14,7 +14,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class TesseractRecognizer implements IRecognizer {
public final class TesseractRecognizer implements IUnitRecognizer<Unit> {
private final Tesseract tess;
......@@ -23,7 +23,7 @@ public final class TesseractRecognizer implements IRecognizer {
}
@Override
public @NotNull List<Unit> recognize(@NotNull List<BufferedImage> images, double minConfidence) {
public @NotNull List<Unit> recognizeSync(@NotNull List<BufferedImage> images, double minConfidence) {
var units = new ArrayList<Unit>();
var iterator = images.iterator();
......
package es.yonatan.tfg.util;
import es.yonatan.tfg.boxing.HasBoundingBox;
import es.yonatan.tfg.model.HasBoundingBox;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class HashGrid2D<T extends HasBoundingBox> {
public abstract class HashGrid2D<T extends HasBoundingBox> {
private final int width;
private final int height;
private int horizontalCellCount;
private int verticalCellCount;
protected final int width;
protected final int height;
protected int horizontalCellCount;
protected int verticalCellCount;
private final Map<Integer, List<T>> grid;
protected final Map<Integer, List<T>> grid;
private double intersectExpandScaleX;
private double intersectExpandScaleY;
protected double intersectExpandScaleX;
protected double intersectExpandScaleY;
public HashGrid2D(int width, int height, int cellCount) {
this(width, height, cellCount, cellCount);
......@@ -70,37 +70,18 @@ public class HashGrid2D<T extends HasBoundingBox> {
grid.put(hash, cell);
}
public @NotNull List<T> getColliding(@NotNull T obj) {
var bounds = obj.bounds();
var cellX = getCellX(bounds.x());
var cellY = getCellY(bounds.y());
var collidingObjects = new ArrayList<T>();
for (int i = cellX - 1; i <= cellX + 1; i++)
for (int j = cellY - 1; j <= cellY + 1; j++) {
var hash = hashCoordinates(i, j);
var cell = grid.get(hash);
if (cell != null)
collidingObjects.addAll(cell.stream().filter(test -> !obj.equals(test)
&& bounds.intersects(test.bounds(), intersectExpandScaleX, intersectExpandScaleY)
// && bounds.horizontalDistanceTo(test.bounds()) < (width * 0.3)
// && bounds.verticalDistanceTo(test.bounds()) < (height * 0.2)
).toList());
}
return collidingObjects;
}
// TODO: 10/04/2023 Remove colliding method from HashGrid2D to separate data structure and logic
public abstract @NotNull List<T> getColliding(@NotNull T obj);
private int hashCoordinates(int cellX, int cellY) {
protected int hashCoordinates(int cellX, int cellY) {
return Objects.hash(cellX, cellY);
}
private int getCellX(int x) {
protected int getCellX(int x) {
return (int) (x / (width / (double) horizontalCellCount));
}
private int getCellY(int y) {
protected int getCellY(int y) {
return (int) (y / (height / (double) verticalCellCount));
}
......
package es.yonatan.tfg.util;
import es.yonatan.tfg.model.HasBoundingBox;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class SimpleHashGrid2D<T extends HasBoundingBox> extends HashGrid2D<T> {
public SimpleHashGrid2D(int width, int height, int cellCount) {
super(width, height, cellCount);
}
public SimpleHashGrid2D(int width, int height, int horizontalCellCount, int verticalCellCount) {
super(width, height, horizontalCellCount, verticalCellCount);
}
@Override
public @NotNull List<T> getColliding(@NotNull T obj) {
var bounds = obj.bounds();
var cellX = getCellX(bounds.x());
var cellY = getCellY(bounds.y());
var collidingObjects = new ArrayList<T>();
for (int i = cellX - 1; i <= cellX + 1; i++)
for (int j = cellY - 1; j <= cellY + 1; j++) {
var hash = hashCoordinates(i, j);
var cell = grid.get(hash);
if (cell != null)
collidingObjects.addAll(cell.stream().filter(test -> !obj.equals(test)
&& bounds.intersects(test.bounds(), intersectExpandScaleX, intersectExpandScaleY)
// && bounds.horizontalDistanceTo(test.bounds()) < (width * 0.3)
// && bounds.verticalDistanceTo(test.bounds()) < (height * 0.2)
).toList());
}
return collidingObjects;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment