You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
5.0 KiB
181 lines
5.0 KiB
4 years ago
|
/**
|
||
|
* <a url=http://www.jdeskew.com/>JDeskew</a>
|
||
|
*/
|
||
|
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);
|
||
|
}
|
||
|
}
|