我正在尝试检测图像链接中文本的倾斜角度。问题是这篇文章是用C++写的,我在将某些东西转换为 Java 时遇到了麻烦。
我做了Hugh Transform的实现。大部分到 java 的转换都是我从这篇文章中指导我的 (1(。但它不能正常工作。给出的角度为 0.27919363 时应该给出的角度为 15.9882。这是我正在使用的图像。
这是我的代码:
public double compute_skew1(String filename){
Log.d(TAG, "Computing skew 1");
Mat src = Highgui.imread(filename, 0);
Size size = src.size();
//double minLineSize = 20;
double minLineSize = src.width() / 2.f;
Core.bitwise_not(src, src);
Mat lines = new Mat();
double angle = 0.;
try {
Imgproc.HoughLinesP(src, lines, 1, Math.PI / 180, 100, minLineSize, 20);
Mat disp_lines = new Mat(size, CvType.CV_8UC1, new Scalar(0, 0, 0));
int nb_lines = lines.cols();
for (int i = 0; i < nb_lines; i++) {
double[] vec = lines.get(0, i);
double x1 = vec[0],
y1 = vec[1],
x2 = vec[2],
y2 = vec[3];
Point start = new Point(x1, y1);
Point end = new Point(x2, y2);
Core.line(disp_lines, start, end, new Scalar(255,0,0));
angle += Math.atan2(y2 - y1, x2 - x1);
}
angle /= nb_lines; // mean angle, in radians.*/
//Log.d(TAG, "ANGLE: "+angle);
Log.d(TAG, "ANGLE: "+ angle * 180 / Math.PI);
} catch (Exception e) {
Log.e(TAG, "Error in compute_skew1");
Log.e(TAG, e.getMessage());
}
return angle;
}
我几乎可以肯定问题出在"int nb_lines = lines.cols((;"这一行上,因为原始行是"unsigned nb_lines = lines.size((;",java 不会使用无符号变量,这就是它在 post(1( 中的工作方式。另外,我不太理解这一行"double[] vec = lines.get(0, i(;",但这也是它在帖子 (1( 中的工作方式。我做错了什么?
另外,在我得到角度之后,我需要对文本进行旋转或纠偏,并且我对这种转换也有一些麻烦,尤其是这部分代码:
std::vector<cv::Point> points;
cv::Mat_<uchar>::iterator it = img.begin<uchar>();
cv::Mat_<uchar>::iterator end = img.end<uchar>();
for (; it != end; ++it)
if (*it)
points.push_back(it.pos());
在这篇文章的帮助下2,我相信这是转换:
List <Point> points = new ArrayList<Point>();
for (int i = 0; i < img.rows(); i++) {
for (int j = 0; j < img.cols(); j++) {
double pixel = img.get(i, j)[0];
if (pixel != 0.0)
points.add(new Point(i,j));
}
}
但是不起作用,从来没有一个像素= 0.0,所以p数组只是用每个像素填充。
所以。请让我知道我做错了什么。提前谢谢。
以下是计算偏斜角度的 java 代码:
Mat source = Imgcodecs.imread(input.getName(),0);
Size size = source.size();
Core.bitwise_not(source, source);
Mat lines = new Mat();
Imgproc.HoughLinesP(source, lines, 1, Math.PI / 180, 100, size.width / 2.f, 20);
double angle = 0.;
for(int i = 0; i<lines.height(); i++){
for(int j = 0; j<lines.width();j++){
angle += Math.atan2(lines.get(i, j)[3] - lines.get(i, j)[1], lines.get(i, j)[2] - lines.get(i, j)[0]);
}
}
angle /= lines.size().area();
angle = angle * 180 / Math.PI;
纠偏图像的 Java 代码:
Mat deskew(Mat src, double angle) {
Point center = new Point(src.width()/2, src.height()/2);
Mat rotImage = Imgproc.getRotationMatrix2D(center, angle, 1.0);
//1.0 means 100 % scale
Size size = new Size(src.width(), src.height());
Imgproc.warpAffine(src, src, rotImage, size, Imgproc.INTER_LINEAR + Imgproc.CV_WARP_FILL_OUTLIERS);
return src;
}
角度应以弧度为单位,而不是以度为单位。
这可以使用tess4j类来实现,即ImageDeskew和ImageHelper
ImageDeskew imgdeskew=new ImageDeskew(img); // BufferedImage img
ImageHelper.rotateImage(bim, -imgdeskew.getSkewAngle()); // rotateImage static method
private fun main(){
val bmp:Bitmap? = null //Any bitmap (if you are working with bitmap)
var mRgba = Mat() // else you can direct use MAT on onCameraFrame
val mGray = Mat()
val bmp32: Bitmap = bmp.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmp32, mRgba)
Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_BGR2GRAY)
mRgba = makeOrientationCorrection(mRgba,mGray)// here actual magic starts
Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_BGR2GRAY)
val bmpOutX = Bitmap.createBitmap(
mRgba.cols(),
mRgba.rows(),
Bitmap.Config.ARGB_8888
)
Utils.matToBitmap(mRgba, bmpOutX)
binding.imagePreview.setImageBitmap(bmpOutX!!)
}
private fun makeOrientationCorrection(mRGBA:Mat, mGRAY:Mat):Mat{
val dst = Mat()
val cdst = Mat()
val cdstP: Mat
Imgproc.Canny(mGRAY, dst, 50.0, 200.0, 3, false)
Imgproc.cvtColor(dst, cdst, Imgproc.COLOR_GRAY2BGR)
cdstP = cdst.clone()
val linesP = Mat()
Imgproc.HoughLinesP(dst, linesP, 1.0, Math.PI/180, 50, 50.0, 10.0)
var biggestLineX1 = 0.0
var biggestLineY1 = 0.0
var biggestLineX2 = 0.0
var biggestLineY2 = 0.0
var biggestLine = 0.0
for (x in 0 until linesP.rows()) {
val l = linesP[x, 0]
Imgproc.line(
cdstP, org.opencv.core.Point(l[0], l[1]),
org.opencv.core.Point(l[2], l[3]),
Scalar(0.0, 0.0, 255.0), 3, Imgproc.LINE_AA, 0)
}
for (x in 0 until linesP.rows()) {
val l = linesP[x, 0]
val x1 = l[0]
val y1 = l[1]
val x2 = l[2]
val y2 = l[3]
val lineHeight = sqrt(((x2 - x1).pow(2.0)) + ((y2 - y1).pow(2.0)))
if(biggestLine<lineHeight){
val angleOfRotationX1 = angleOf(PointF(x1.toFloat(),y1.toFloat()),PointF(x2.toFloat(),y2.toFloat()))
Log.e("angleOfRotationX1","$angleOfRotationX1")
if(angleOfRotationX1<45.0 || angleOfRotationX1>270.0){
biggestLine = lineHeight
if(angleOfRotationX1<45.0){
biggestLineX1 = x1
biggestLineY1 = y1
biggestLineX2 = x2
biggestLineY2 = y2
}
if(angleOfRotationX1>270.0){
biggestLineX1 = x2
biggestLineY1 = y2
biggestLineX2 = x1
biggestLineY2 = y1
}
}
}
if(x==linesP.rows()-1){
Imgproc.line(
cdstP, org.opencv.core.Point(biggestLineX1, biggestLineY1),
org.opencv.core.Point(biggestLineX2, biggestLineY2),
Scalar(255.0, 0.0, 0.0), 3, Imgproc.LINE_AA, 0)
}
}
var angle = angleOf(PointF(biggestLineX1.toFloat(),biggestLineY1.toFloat()),PointF(biggestLineX2.toFloat(),biggestLineY2.toFloat()))
Log.e("angleOfRotationX2","$angle")
angle -= (angle * 2)
return deskew(mRGBA,angle)
}
fun angleOf(p1: PointF, p2: PointF): Double {
val deltaY = (p1.y - p2.y).toDouble()
val deltaX = (p2.x - p1.x).toDouble()
val result = Math.toDegrees(Math.atan2(deltaY, deltaX))
return if (result < 0) 360.0 + result else result
}
private fun deskew(src:Mat, angle:Double):Mat{
val center = org.opencv.core.Point((src.width() / 2).toDouble(), (src.height() / 2).toDouble())
val scaleBy = if(angle<0){
1.0+((0.5*angle)/45)//max scale down by 0.50(50%) based on angle
}else{
1.0-((0.3*angle)/45)//max scale down by 0.50(50%) based on angle
}
Log.e("scaleBy",""+scaleBy)
val rotImage = Imgproc.getRotationMatrix2D(center, angle, scaleBy)
val size = Size(src.width().toDouble(), src.height().toDouble())
Imgproc.warpAffine(src, src, rotImage, size, Imgproc.INTER_LINEAR + Imgproc.CV_WARP_FILL_OUTLIERS)
return src
}
确保在另一个线程上运行此"makeOrientationCorrection(("方法。 否则,UI 不会在 2-5 秒内更新。