OpenCV:获取卡片的顶点

时间:2018-03-25 15:25:18

标签: opencv image-processing

我正在尝试检测卡片并对图像进行回顾。我已成功获得卡的轮廓。但是我在如何获得顶点(在图像中显示为蓝点)作为getPerspectiveTransform函数的输入时遇到了困难。实际上我在静态图像上用简单的算法完成了它。但是,它对于实时框架效果不佳,尤其是当卡片旋转时。是否有任何简单而强大的方法可以获得卡的四个顶点,而不管卡的方向如何?

MatOfPoint2f curve = new MatOfPoint2f(matched.toArray());
double perimeter = Imgproc.arcLength(curve, true);
MatOfPoint2f approxCurve2f = new MatOfPoint2f();
Imgproc.approxPolyDP(curve, approxCurve2f, 0.003 * perimeter, true);

Mat temp = new Mat(src.rows(),src.cols(),CvType.CV_8UC1);
Imgproc.drawContours(temp, cnt, -1, new Scalar(255,255,255));
Moments moment = Imgproc.moments(matched);
Point centroid = new Point(moment.m10/moment.m00, moment.m01/moment.m00);

MatOfPoint approxCurve = new MatOfPoint();
approxCurve2f.convertTo(approxCurve, CvType.CV_32S);
List<Point> corners = Util.toPointList(approxCurve);
//group points that near each other
List<List<Point>> grouping = Util.cornerPointGrouping(corners, 50);
if(grouping.size()!=4)
{
    return temp;
}

Mat before = new Mat(1,4, CvType.CV_32FC2);
Mat after = new Mat(1,4, CvType.CV_32FC2);
Util.generateTransformationParams(grouping, centroid, before, after);
Mat transformMat = Imgproc.getPerspectiveTransform(before, after);
Mat out = new Mat();
Size dsize = new Size(src.width(), src.height());
Imgproc.warpPerspective(src, out, transformMat, dsize);


public static void generateTransformationParams(List<List<Point>> grouping, 
        Point centroid, Mat before, Mat after)
{

    Point topLeft=new Point(), topRight=new Point(), 
            btmLeft=new Point(), btmRight=new Point();
    for(List<Point> list: grouping)
    {
        List<Point> ySorted = new ArrayList<>(list);
        Collections.sort(list, new xComparator());
        Collections.sort(ySorted, new yComparator());
        if(list.get(0).y<centroid.y) //top
        {
            if(list.get(0).x<centroid.x) //left
            {
                topLeft = new Point(list.get(0).x,ySorted.get(0).y);
            }
            else //right
            {
                topRight = new Point(list.get(list.size()-1).x,ySorted.get(0).y);
            }
        }
        else //bottom
        {
            if(list.get(0).x<centroid.x) //left
            {
                btmLeft = new Point(list.get(0).x,ySorted.get(list.size()-1).y);
            }
            else //right
            {
                btmRight = new Point(list.get(list.size()-1).x,ySorted.get(list.size()-1).y);
            }
        }

    }
    //btmLeft as reference
    before.put(0, 0, topLeft.x, topLeft.y, topRight.x, topRight.y,
            btmRight.x, btmRight.y, btmLeft.x, btmLeft.y);
    double width = Util.getLength(btmLeft, btmRight);
    double height = width/creditCardRatio;
    after.put(0, 0, btmLeft.x, btmLeft.y-height, btmLeft.x+width, btmLeft.y-height,
            btmLeft.x+width, btmLeft.y, btmLeft.x, btmLeft.y);
}

enter image description here

enter image description here

我尝试了minAreaRect,当卡片是矩形时它很有效。当透视改变时,它无法给我准确的顶点。 enter image description here

1 个答案:

答案 0 :(得分:0)

一旦定义了卡片的轮廓(从轮廓方法或强度图像),OpenCV提供了这些附加功能来检测旋转的矩形。旋转的矩形可以由其顶点定义。

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)

https://docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html

如果转换是高度透视的,或者您需要高精度的点,只需调用cv2.findContours()并发送标志cv2.CHAIN_APPROXIMATION_SIMPLE就足够了(假设您的卡被检测为四边形轮廓)轮廓点将是顶点。

编辑:上面的语法是在Python中,我意识到问题出在Android java