use of com.joliciel.jochre.graphics.Shape in project jochre by urieli.
the class OriginalShapeLetterAssigner method onGuessSequence.
@Override
public void onGuessSequence(LetterSequence letterSequence) {
stillValid = true;
ShapeSequence shapeSequence = letterSequence.getUnderlyingShapeSequence();
Shape previousOriginalShape = null;
List<ShapeInSequence> subsequenceForPrevOriginalShape = new ArrayList<ShapeInSequence>();
for (ShapeInSequence shapeInSequence : shapeSequence) {
// cases that are possible:
// 1) shapeInSequence is 1-to-1 with an original shape (A from original shape A)
// 2) shapeInSequence shares an original shape with previous (B from original shape AB)
// 3) shapeInSequence shares an original shape with next (A from original shape AB)
// 4) shapeInSequence shares an original shape with previous and next (B from original shape ABC)
// 5) shapeInSequence has two original shapes (A from original shapes |A A|)
// 6) shapeInSequence has 3 original shapes (A from original shapes |A * A|)
// 7) shapeInSequence shares with previous and has 2+ original shapes (A from |A A|B)
// 8) shapeInSequence shares with next and has 2+ original shapes (B from A|B B|)
// So, when we reach a new original shape,
// either it coincides with a previous shape border, or it doesn't
List<Shape> originalShapes = shapeInSequence.getOriginalShapes();
for (Shape nextOriginalShape : originalShapes) {
if (!nextOriginalShape.equals(previousOriginalShape)) {
// new original shape, we need to populate the letters of the previous one
if (previousOriginalShape != null)
this.assignLetter(previousOriginalShape, subsequenceForPrevOriginalShape);
previousOriginalShape = nextOriginalShape;
subsequenceForPrevOriginalShape = new ArrayList<ShapeInSequence>();
}
subsequenceForPrevOriginalShape.add(shapeInSequence);
}
// next original shape
}
// next underlying shape sequence shape
if (previousOriginalShape != null)
this.assignLetter(previousOriginalShape, subsequenceForPrevOriginalShape);
}
use of com.joliciel.jochre.graphics.Shape in project jochre by urieli.
the class OriginalShapeLetterAssigner method assignLetter.
void assignLetter(Shape originalShape, List<ShapeInSequence> subsequenceForOriginalShape) {
String guessedLetter = "";
for (ShapeInSequence shapeInSubSequence : subsequenceForOriginalShape) {
if (shapeInSubSequence.getOriginalShapes().size() == 1) {
// if this subsequence shape has only one original shape,
// we can go ahead and add the subsequence shape's letter to the original shape
guessedLetter += shapeInSubSequence.getShape().getOriginalGuess();
} else {
// the subsequence shape has multiple original shapes, so its letter has to be
// split among all of them (these original shapes were joined into a single new shape)
int j = 0;
int myIndex = -1;
for (Shape myOriginalShape : shapeInSubSequence.getOriginalShapes()) {
if (myOriginalShape.equals(originalShape)) {
myIndex = j;
break;
}
j++;
}
if (myIndex == 0) {
// the original shape starts this subsequence shape
if (shapeInSubSequence.getShape().getOriginalGuess().length() > 0)
guessedLetter += "|" + shapeInSubSequence.getShape().getOriginalGuess();
} else if (myIndex == shapeInSubSequence.getOriginalShapes().size() - 1) {
// the original shape ends this subsequence shape
if (shapeInSubSequence.getShape().getOriginalGuess().length() > 0)
guessedLetter += shapeInSubSequence.getShape().getOriginalGuess() + "|";
} else {
// the original shape is in the middle of this subsequence shape
// nothing to do here, since we leave these blank
}
// if more than one, where is the original shape in this subsequence's original shapes
}
// only one original shape for this subsequence shape, or more?
}
// next shape in subsequence for this original shape
originalShape.setOriginalGuess(guessedLetter);
if (currentImage.getImageStatus().equals(ImageStatus.AUTO_NEW))
originalShape.setLetter(guessedLetter);
if (save)
originalShape.save();
if (evaluate && stillValid) {
if (letterValidator == null) {
throw new JochreException("Cannot evaluate without a letter validator.");
}
String realLetter = originalShape.getLetter();
String realLetterForCheck = realLetter.replace("|", "");
if (letterValidator.validate(realLetterForCheck)) {
if (guessedLetter.startsWith("|") && guessedLetter.length() == 3 && realLetter.equals("" + guessedLetter.charAt(1))) {
// the guessed letter is the first half of a split dual letter, and is the same as a real letter
this.incrementFScore(realLetter, realLetter);
} else if (guessedLetter.endsWith("|") && guessedLetter.length() == 3 && realLetter.equals("" + guessedLetter.charAt(1))) {
// the guessed letter is the second half of a split dual letter, and is the same as a real letter
this.incrementFScore(realLetter, realLetter);
} else if (realLetter.startsWith("|") && realLetter.length() == 3 && guessedLetter.equals("" + realLetter.charAt(1))) {
// the real letter is the first half of a split dual letter, and we correctly guessed the first letter of the two
this.incrementFScore(realLetter, realLetter);
} else if (realLetter.endsWith("|") && realLetter.length() == 3 && guessedLetter.equals("" + realLetter.charAt(1))) {
// the real letter is the second half of a split dual letter, and we correctly guessed the second letter of the two
this.incrementFScore(realLetter, realLetter);
} else {
this.incrementFScore(realLetter, guessedLetter);
if (realLetter.equals(guessedLetter))
hasError = true;
}
} else {
// check if there are any invalid characters
String prevChar = "";
for (int i = 0; i < realLetterForCheck.length(); i++) {
String nextChar = "" + realLetterForCheck.charAt(i);
if (letterValidator.validate(nextChar)) {
// do nothing
} else if (letterValidator.validate(prevChar + nextChar)) {
// do nothing
} else {
stillValid = false;
break;
}
prevChar = nextChar;
}
if (stillValid) {
this.incrementFScore(realLetter, guessedLetter);
}
}
}
}
use of com.joliciel.jochre.graphics.Shape in project jochre by urieli.
the class SimpleLetterFScoreObserver method onGuessLetter.
@Override
public void onGuessLetter(ShapeInSequence shapeInSequence, String bestGuess) {
if (stillValid) {
Shape shape = shapeInSequence.getShape();
String realLetter = shape.getLetter();
if (letterValidator.validate(realLetter)) {
if (realLetter.length() == 0)
realLetter = "■";
else if (!jochreSession.getLinguistics().getValidLetters().contains(realLetter)) {
if (realLetter.contains("|"))
realLetter = "□" + realLetter;
else
realLetter = "■" + realLetter;
}
if (bestGuess.length() == 0)
bestGuess = "■";
else if (!jochreSession.getLinguistics().getValidLetters().contains(bestGuess))
if (bestGuess.contains("|"))
bestGuess = "□" + bestGuess;
else
bestGuess = "■" + bestGuess;
fScoreCalculator.increment(realLetter, bestGuess);
if (!realLetter.equals(bestGuess))
hasError = true;
} else {
stillValid = false;
}
}
}
use of com.joliciel.jochre.graphics.Shape in project jochre by urieli.
the class JochreMergeEventStream method hasNext.
@Override
public boolean hasNext() {
this.initialiseStream();
while (mergeCandidate == null && group != null) {
if (shapeIndex < group.getShapes().size() - 1) {
Shape shape1 = group.getShapes().get(shapeIndex);
Shape shape2 = group.getShapes().get(shapeIndex + 1);
ShapePair shapePair = new ShapePair(shape1, shape2);
double widthRatio = (double) shapePair.getWidth() / (double) shapePair.getXHeight();
double distanceRatio = (double) shapePair.getInnerDistance() / (double) shapePair.getXHeight();
if (widthRatio <= maxWidthRatio && distanceRatio <= maxDistanceRatio) {
belowRatioCount++;
mergeCandidate = shapePair;
} else {
aboveRatioCount++;
mergeCandidate = null;
}
shapeIndex++;
} else {
group = null;
shapeIndex = 0;
if (groupReader.hasNext())
group = groupReader.next();
}
}
if (mergeCandidate == null) {
LOG.debug("aboveRatioCount: " + aboveRatioCount);
LOG.debug("belowRatioCount: " + belowRatioCount);
LOG.debug("yesCount: " + yesCount);
LOG.debug("noCount: " + noCount);
}
return mergeCandidate != null;
}
use of com.joliciel.jochre.graphics.Shape in project jochre by urieli.
the class LetterByLetterBoundaryDetector method findBoundaries.
@Override
public List<ShapeSequence> findBoundaries(GroupOfShapes group) {
// find the possible shape sequences that make up this group
ShapeSequence emptySequence = new ShapeSequence();
PriorityQueue<ShapeSequence> heap = new PriorityQueue<ShapeSequence>();
heap.add(emptySequence);
for (Shape shape : group.getShapes()) {
PriorityQueue<ShapeSequence> previousHeap = heap;
heap = new PriorityQueue<ShapeSequence>();
// check if shape is wide enough to bother with
double widthRatio = (double) shape.getWidth() / (double) shape.getXHeight();
double heightRatio = (double) shape.getHeight() / (double) shape.getXHeight();
// Splitting/merging shapes as required
List<ShapeSequence> splitSequences = null;
if (this.shapeSplitter != null && widthRatio >= minWidthRatioForSplit && heightRatio >= minHeightRatioForSplit) {
splitSequences = shapeSplitter.split(shape);
} else {
// create a sequence containing only this shape
ShapeSequence singleShapeSequence = new ShapeSequence();
singleShapeSequence.addShape(shape);
splitSequences = new ArrayList<ShapeSequence>();
splitSequences.add(singleShapeSequence);
}
// limit the breadth to K
int maxSequences = previousHeap.size() > this.beamWidth ? this.beamWidth : previousHeap.size();
for (int j = 0; j < maxSequences; j++) {
ShapeSequence history = previousHeap.poll();
for (ShapeSequence splitSequence : splitSequences) {
ShapeInSequence previousShapeInSequence = null;
Shape previousShape = null;
if (history.size() > 0) {
previousShapeInSequence = history.get(history.size() - 1);
previousShape = previousShapeInSequence.getShape();
}
ShapeInSequence firstShapeInSequence = splitSequence.get(0);
Shape firstShape = firstShapeInSequence.getShape();
double mergeProb = 0;
if (this.shapeMerger != null && previousShape != null) {
ShapePair mergeCandidate = new ShapePair(previousShape, shape);
double mergeCandidateWidthRatio = 0;
double mergeCandidateDistanceRatio = 0;
mergeCandidateWidthRatio = (double) mergeCandidate.getWidth() / (double) mergeCandidate.getXHeight();
mergeCandidateDistanceRatio = (double) mergeCandidate.getInnerDistance() / (double) mergeCandidate.getXHeight();
if (mergeCandidateWidthRatio <= maxWidthRatioForMerge && mergeCandidateDistanceRatio <= maxDistanceRatioForMerge) {
mergeProb = shapeMerger.checkMerge(previousShape, firstShape);
}
}
if (mergeProb > 0) {
Shape mergedShape = shapeMerger.merge(previousShape, firstShape);
ShapeSequence mergedSequence = new ShapeSequence(history);
mergedSequence.remove(mergedSequence.size() - 1);
List<Shape> originalShapesForMerge = new ArrayList<Shape>();
originalShapesForMerge.addAll(previousShapeInSequence.getOriginalShapes());
originalShapesForMerge.addAll(firstShapeInSequence.getOriginalShapes());
mergedSequence.addShape(mergedShape, originalShapesForMerge);
boolean isFirstShape = true;
for (ShapeInSequence splitShape : splitSequence) {
if (!isFirstShape)
mergedSequence.add(splitShape);
isFirstShape = false;
}
heap.add(mergedSequence);
Decision mergeDecision = new Decision(MergeOutcome.DO_MERGE.name(), mergeProb);
mergedSequence.addDecision(mergeDecision);
for (Decision splitDecision : splitSequence.getDecisions()) mergedSequence.addDecision(splitDecision);
}
if (mergeProb < 1) {
ShapeSequence totalSequence = new ShapeSequence(history);
if (mergeProb > 0) {
Decision mergeDecision = new Decision(MergeOutcome.DO_NOT_MERGE.name(), 1 - mergeProb);
totalSequence.addDecision(mergeDecision);
}
for (Decision splitDecision : splitSequence.getDecisions()) totalSequence.addDecision(splitDecision);
for (ShapeInSequence splitShape : splitSequence) {
totalSequence.add(splitShape);
}
heap.add(totalSequence);
}
}
// next split sequence for this shape
}
// next history from previous heap
}
// next shape in group
List<ShapeSequence> result = new ArrayList<ShapeSequence>();
for (int i = 0; i < this.beamWidth; i++) {
if (heap.isEmpty())
break;
ShapeSequence nextSequence = heap.poll();
result.add(nextSequence);
}
return result;
}
Aggregations