OpenCV Rotation (Deskewing) в Android - преобразование C++ в Java

Я пытаюсь определить угол наклона текста в ссылке на изображение. Проблема в том, что этот пост написан на C++, и у меня возникают проблемы с преобразованием некоторых вещей в Java.

Я сделал реализацию Хью Трансформация. Большую часть перехода на 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 не содержит переменных без знака, и именно так это работает в посте (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 просто заполняется каждым пикселем.

Так. Пожалуйста, дайте мне знать, что я делаю не так. Заранее спасибо.

4 ответа

Вот код 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;
}

Угол должен быть в радианах, а не в градусах.

This is possible using classes of tess4j ie 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() в другом потоке. в противном случае пользовательский интерфейс не будет обновляться в течение 2-5 секунд.

Другие вопросы по тегам