com.recognition.software.jdeskew.ImageDeskew Maven / Gradle / Ivy
/**
* JDeskew
*/
package com.recognition.software.jdeskew;
import net.sourceforge.tess4j.util.LoggHelper;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
public class ImageDeskew {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(new LoggHelper().toString());
/**
* Representation of a line in the image.
*/
public class HoughLine {
// count of points in the line
public int count = 0;
// index in matrix.
public int index = 0;
// the line is represented as all x, y that solve y * cos(alpha) - x *
// sin(alpha) = d
public double alpha;
public double d;
}
// the source image
private BufferedImage cImage;
// the range of angles to search for lines
private double cAlphaStart = -20;
private double cAlphaStep = 0.2;
private int cSteps = 40 * 5;
// pre-calculation of sin and cos
private double[] cSinA;
private double[] cCosA;
// range of d
private double cDMin;
private double cDStep = 1.0;
private int cDCount;
// count of points that fit in a line
private int[] cHMatrix;
/**
* Constructor.
*
* @param image
*/
public ImageDeskew(BufferedImage image) {
this.cImage = image;
}
/**
* Calculates the skew angle of the image cImage.
*
* @return
*/
public double getSkewAngle() {
ImageDeskew.HoughLine[] hl;
double sum = 0.0;
int count = 0;
// perform Hough Transformation
calc();
// top 20 of the detected lines in the image
hl = getTop(20);
if (hl.length >= 20) {
// average angle of the lines
for (int i = 0; i < 19; i++) {
sum += hl[i].alpha;
count++;
}
return (sum / count);
} else {
return 0.0d;
}
}
// calculate the count lines in the image with most points
private ImageDeskew.HoughLine[] getTop(int count) {
ImageDeskew.HoughLine[] hl = new ImageDeskew.HoughLine[count];
for (int i = 0; i < count; i++) {
hl[i] = new ImageDeskew.HoughLine();
}
ImageDeskew.HoughLine tmp;
for (int i = 0; i < (this.cHMatrix.length - 1); i++) {
if (this.cHMatrix[i] > hl[count - 1].count) {
hl[count - 1].count = this.cHMatrix[i];
hl[count - 1].index = i;
int j = count - 1;
while ((j > 0) && (hl[j].count > hl[j - 1].count)) {
tmp = hl[j];
hl[j] = hl[j - 1];
hl[j - 1] = tmp;
j--;
}
}
}
int alphaIndex;
int dIndex;
for (int i = 0; i < count; i++) {
dIndex = hl[i].index / cSteps; // integer division, no
// remainder
alphaIndex = hl[i].index - dIndex * cSteps;
hl[i].alpha = getAlpha(alphaIndex);
hl[i].d = dIndex + cDMin;
}
return hl;
}
// Hough Transformation
private void calc() {
int hMin = (int) ((this.cImage.getHeight()) / 4.0);
int hMax = (int) ((this.cImage.getHeight()) * 3.0 / 4.0);
init();
for (int y = hMin; y < hMax; y++) {
for (int x = 1; x < (this.cImage.getWidth() - 2); x++) {
// only lower edges are considered
if (ImageUtil.isBlack(this.cImage, x, y)) {
if (!ImageUtil.isBlack(this.cImage, x, y + 1)) {
calc(x, y);
}
}
}
}
}
// calculate all lines through the point (x,y)
private void calc(int x, int y) {
double d;
int dIndex;
int index;
for (int alpha = 0; alpha < (this.cSteps - 1); alpha++) {
d = y * this.cCosA[alpha] - x * this.cSinA[alpha];
dIndex = (int) (d - this.cDMin);
index = dIndex * this.cSteps + alpha;
try {
this.cHMatrix[index] += 1;
} catch (Exception e) {
logger.warn("", e);
}
}
}
private void init() {
double angle;
// pre-calculation of sin and cos
this.cSinA = new double[this.cSteps - 1];
this.cCosA = new double[this.cSteps - 1];
for (int i = 0; i < (this.cSteps - 1); i++) {
angle = getAlpha(i) * Math.PI / 180.0;
this.cSinA[i] = Math.sin(angle);
this.cCosA[i] = Math.cos(angle);
}
// range of d
this.cDMin = -this.cImage.getWidth();
this.cDCount = (int) (2.0 * ((this.cImage.getWidth() + this.cImage.getHeight())) / this.cDStep);
this.cHMatrix = new int[this.cDCount * this.cSteps];
}
public double getAlpha(int index) {
return this.cAlphaStart + (index * this.cAlphaStep);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy