透视变换OpenCV

时间:2018-03-16 08:59:06

标签: android opencv image-processing

我是Android上的OpenCV新手并尝试做Perspective Transform但我不知道如何使用getperspectivetransform()和warperspective()函数。我可以从图像中检测矩形,但不知道如何翘曲。

这是检测矩形函数:

 Mat tempMat = new Mat();
        Mat src = new Mat();
        Utils.bitmapToMat(image, tempMat);

        Imgproc.cvtColor(tempMat, src, Imgproc.COLOR_BGR2RGB);

        Mat blurred = src.clone();
        Imgproc.medianBlur(src, blurred, 9);

        Mat gray0 = new Mat(blurred.size(), CvType.CV_8U), gray = new Mat();

        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();

        List<Mat> blurredChannel = new ArrayList<Mat>();
        blurredChannel.add(blurred);
        List<Mat> gray0Channel = new ArrayList<Mat>();
        gray0Channel.add(gray0);

        MatOfPoint2f approxCurve = new MatOfPoint2f();

        double maxArea = 0;
        int maxId = -1;

        for (int c = 0; c < 3; c++) {
            int ch[] = { c, 0 };
            Core.mixChannels(blurredChannel, gray0Channel, new MatOfInt(ch));

            int thresholdLevel = 1;
            for (int t = 0; t < thresholdLevel; t++) {
                if (t == 0) {
                    Imgproc.Canny(gray0, gray, 50, 50, 3, true); // true ?
                    Imgproc.dilate(gray, gray, new Mat(), new Point(-1, -1), 1); // 1
                    // ?
                } else {
                    Imgproc.adaptiveThreshold(gray0, gray, thresholdLevel,
                            Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
                            Imgproc.THRESH_BINARY,
                            (src.width() + src.height()) / 200, t);
                }

                Imgproc.findContours(gray, contours, new Mat(),
                        Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

                for (MatOfPoint contour : contours) {
                    MatOfPoint2f temp = new MatOfPoint2f(contour.toArray());

                    double area = Imgproc.contourArea(contour);
                    approxCurve = new MatOfPoint2f();
                    Imgproc.approxPolyDP(temp, approxCurve,
                            Imgproc.arcLength(temp, true) * 0.02, true);

                    if (approxCurve.total() == 4 && area >= maxArea) {
                        double maxCosine = 0;

                        List<Point> curves = approxCurve.toList();
                        for (int j = 2; j < 5; j++) {

                            double cosine = Math.abs(angle(curves.get(j % 4),
                                    curves.get(j - 2), curves.get(j - 1)));
                            maxCosine = Math.max(maxCosine, cosine);
                        }

                        if (maxCosine < 0.45) {
                            maxArea = area;
                            maxId = contours.indexOf(contour);
                        }
                    }
                }
            }
        }

我用这个语句绘制矩形。

if (maxId >= 0) {
            Rect rect = Imgproc.boundingRect(contours.get(maxId));

            Imgproc.rectangle(src, rect.tl(), rect.br(), new Scalar(255, 0, 0,
                    .8), 4);

        }

之后我将mat转换为位图并在imageview上显示。

Here is the screenshot

所以我的问题是翘曲,如何扭曲矩形并旋转呢?

如果有可能,我该如何改进检测矩形?任何提示?

(OpenCV Android SDK版本:3.41,Android Studio版本:3.01)

2 个答案:

答案 0 :(得分:0)

如果您想将检测到的轮廓扭曲成矩形,

  1. 获取矩形的轮廓
  2. 找到轮廓的凸包
  3. 使用approxPolyDP将凸包点减少到4个点
  4. 拟合连续点的线(例如,如果pts是数组,则线条拟合如下l1 = line Between(pts [0],pts [1]),l2 = line between(pts [1],pts [ 2]),l3 =行之间(pts [2],pts [3]),l4 = lineBetween(pts [3],pts [0])
  5. 找到这些行之间的交叉点,你最终得到四个点
  6. 按顺时针顺序排列点数(inputCorners = TopLeft,TopRight,BottomRight,BottomLeft)
  7. 创建一个具有所需分辨率的输出图像,并使角点按顺时针顺序排列((0,0),(0,cols),(行,列),(行,0))
  8. 使用函数

    查找单应性
    Mat homography = Calib3d.findHomography(inputCorners, imageCorners, Calib3d.RANSAC, 10);
    
  9. 使用输出单应矩阵,使用函数

    扭曲输入图像
    Imgproc.warpPerspective(image, outputMat, homography, new Size(image.cols(), image.rows()));
    
  10. 您可以参考以下link

答案 1 :(得分:0)

这是我的kotlin extensin版本,您可以在项目中使用它。

fun Bitmap.perspectiveTransform(srcPoints: List<org.opencv.core.Point>) : 
    Bitmap{
     val dstWidth = max(
        srcPoints[0].distanceFrom(srcPoints[1]),
        srcPoints[2].distanceFrom(srcPoints[3])
     )
     val dstHeight = max(
         srcPoints[0].distanceFrom(srcPoints[2]),
         srcPoints[1].distanceFrom(srcPoints[3])
      )

    val dstPoints: List<org.opencv.core.Point> = listOf(
       org.opencv.core.Point(0.0, 0.0),
       org.opencv.core.Point(dstWidth, 0.0),
       org.opencv.core.Point(0.0, dstHeight),
       org.opencv.core.Point(dstWidth, dstHeight)
    )
    return try {
         val srcMat = Converters.vector_Point2d_to_Mat(srcPoints)
         val dstMat = Converters.vector_Point2d_to_Mat(dstPoints)
         val perspectiveTransformation = 
    Imgproc.getPerspectiveTransform(srcMat, dstMat)
    val inputMat = Mat(this.height, this.width, CvType.CV_8UC1)
    Utils.bitmapToMat(this, inputMat)
    val outPutMat = Mat(dstHeight.toInt(), dstWidth.toInt(), CvType.CV_8UC1)
    Imgproc.warpPerspective(
        inputMat,
        outPutMat,
        perspectiveTransformation,
        Size(dstWidth, dstHeight)
    )
    val outPut = Bitmap.createBitmap(
        dstWidth.toInt(),
        dstHeight.toInt(), Bitmap.Config.RGB_565
    )
    //Imgproc.cvtColor(outPutMat , outPutMat , Imgproc.COLOR_GRAY2BGR)
    Utils.matToBitmap(outPutMat , outPut)
    outPut
  }
  catch ( e : Exception){
    e.printStackTrace()
    this
  }
}

要使用距离,我要编写另一个扩展功能

fun org.opencv.core.Point.distanceFrom(srcPoint: org.opencv.core.Point): 
     Double {
     val w1 = this.x - srcPoint.x
     val h1 = this.y - srcPoint.y
     val distance = w1.pow(2) + h1.pow(2)
     return sqrt(distance)
   }

在此答案中,正确的src点索引也为:
0:左上方

1:topRight

2:bottomLeft

3:bottomRight

祝你好运