Skip to content

Commit

Permalink
gs-cv: Deskewer: added a RANSAC to get a better angle
Browse files Browse the repository at this point in the history
  • Loading branch information
plassalas committed Oct 12, 2017
1 parent 5c6474a commit 2c341fa
Showing 1 changed file with 55 additions and 5 deletions.
60 changes: 55 additions & 5 deletions gs-cv/src/main/java/org/genericsystem/cv/utils/Deskewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;

import org.apache.commons.io.FilenameUtils;
import org.genericsystem.cv.Img;
import org.genericsystem.layout.Ransac;
import org.genericsystem.layout.Ransac.Model;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
Expand Down Expand Up @@ -194,7 +200,8 @@ private static double contoursDetection(final Mat dilated) {
rotatedRect.size.height = tmp;
}
}
return getInliers(rotatedRects, 1.0).stream().mapToDouble(i -> i.angle).average().getAsDouble();
// return getInliers(rotatedRects, 1.0).stream().mapToDouble(i -> i.angle).average().getAsDouble();
return getRansacInliers(rotatedRects, 0.1).stream().mapToDouble(i -> i.angle).average().getAsDouble();
}

private static List<RotatedRect> getInliers(final List<RotatedRect> data, final double confidence) {
Expand All @@ -211,10 +218,53 @@ private static List<RotatedRect> getInliers(final List<RotatedRect> data, final
else
median = DoubleStream.of(data.get(middle).angle, data.get(middle - 1).angle).average().getAsDouble();

List<RotatedRect> result = data.stream().filter(rect -> Math.abs(rect.angle - median) < confidence * sd).collect(Collectors.toList());
// List<RotatedRect> result = data.stream().filter(rect -> Math.abs(rect.angle - average) < confidence * sd).collect(Collectors.toList());
// List<RotatedRect> result = data.stream().filter(rect -> Math.abs(rect.angle - average) < 5).collect(Collectors.toList());
return result;
return data.stream().filter(rect -> Math.abs(rect.angle - median) < confidence * sd).collect(Collectors.toList());
}

private static List<RotatedRect> getRansacInliers(final List<RotatedRect> data, final double error) {
int n = 3; // number of random samples
int k = 50; // number of iterations
double t = error; // error margin
int d = data.size() / 2; // number of minimum matches
Map<Integer, RotatedRect> bestFit = new HashMap<>();
for (int i = 1, maxAttempts = 10; bestFit.size() <= 3 && i <= maxAttempts; ++i) {
Ransac<RotatedRect> ransac = new Ransac<>(data, getModelProvider(), n, k * i, t, d);
try {
ransac.compute();
bestFit = ransac.getBestDataSet();
// bestFit.entrySet().forEach(entry -> logger.debug("key: {} | | value: {}", entry.getKey(), entry.getValue()));
} catch (Exception e) {
t *= 1.5;
logger.trace("Can't get a good model. Increase the error margin to {}", t);
}
}
return bestFit.values().stream().collect(Collectors.toList());
}

private static Function<Collection<RotatedRect>, Model<RotatedRect>> getModelProvider() {
return datas -> {
double average = datas.stream().mapToDouble(rect -> rect.angle).average().getAsDouble();

return new Model<RotatedRect>() {
@Override
public double computeError(RotatedRect data) {
return Math.abs(data.angle - average);
}

@Override
public double computeGlobalError(Collection<RotatedRect> datas) {
double error = 0;
for (RotatedRect rect : datas)
error += Math.pow(computeError(rect), 2);
return error;
}

@Override
public Object[] getParams() {
return new Object[] { average };
}
};
};
}

private static List<RotatedRect> getRotatedRects(final Mat dilated) {
Expand Down

0 comments on commit 2c341fa

Please sign in to comment.