OpenCV:获取卡的顶点

OpenCV:获取卡的顶点,opencv,image-processing,Opencv,Image Processing,我试图检测一张卡片,并在图像上做透视。我已经成功地得到了卡片的轮廓。但是在如何获取顶点点(在图像中显示为蓝色点)作为getPerspectiveTransform函数的输入方面,我面临着困难。实际上,我已经用简单的算法在静态图像上完成了。然而,它并不适用于活帧,尤其是当卡旋转时。有没有一种简单而健壮的方法可以在不考虑卡的方向的情况下获得卡的四个顶点点 MatOfPoint2f curve = new MatOfPoint2f(matched.toArray()); double perimete

我试图检测一张卡片,并在图像上做透视。我已经成功地得到了卡片的轮廓。但是在如何获取顶点点(在图像中显示为蓝色点)作为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);
}
MatOfPoint2f曲线=新的MatOfPoint2f(matched.toArray());
双周长=Imgproc.弧长(曲线,真);
MatOfPoint2f approxCurve2f=新的MatOfPoint2f();
Imgproc.approxPolyDP(曲线,approxCurve2f,0.003*周长,真);
Mat temp=新Mat(src.rows()、src.cols()、CvType.CV_8UC1);
Imgproc.绘制等高线(温度,cnt,-1,新标量(255255));
力矩=Imgproc.力矩(匹配);
点质心=新点(力矩.m10/力矩.m00,力矩.m01/力矩.m00);
MatOfPoint approxCurve=新的MatOfPoint();
approxCurve2f.convertTo(approxCurve,CvType.cv32s);
列表角点=Util.toPointList(approxCurve);
//将相互靠近的点分组
列表分组=Util.cornerPointGrouping(corners,50);
if(grouping.size()!=4)
{
返回温度;
}
前垫=新垫(1,4,CvType.CV_32FC2);
后垫=新垫(1,4,CvType.CV_32FC2);
Util.generateTransformationParams(分组、质心、前后);
Mat transformMat=Imgproc.getPerspectiveTransform(之前、之后);
Mat out=新Mat();
Size dsize=新尺寸(src.width(),src.height());
Imgproc.warpPerspective(src、out、transformMat、dsize);
公共静态void generateTransformationParams(列表分组,
点质心、前垫、后垫)
{
点topLeft=新点(),topRight=新点(),
btmLeft=新点(),btmRight=新点();
用于(列表:分组)
{
List ySorted=新阵列列表(List);
Collections.sort(list,new xComparator());
Collections.sort(ySorted,new yComparator());

如果(list.get(0).y一旦定义了卡的轮廓(从轮廓方法或强度图像),OpenCV将提供这些附加函数来检测旋转的矩形。旋转的矩形可以通过其顶点定义

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

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

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

编辑:上面的语法是Python,我意识到问题是在Android java中,语言并不重要。事实上,我之前尝试过MinareRect,我已经更新了我的问题。如果你按照回答进行,这可能会很有帮助,在近似co后,你将得到卡片的顶点@Gopiraj我不理解步骤5和6ntour,您将在轮廓上以4个或更多点结束,使用这些点形成一条线。例如line1=(点[0],点[1]),…lineN=(点[N],点[0])。查找线的长度(标准值(点[0]-点[1]))。根据长度对线进行排序。获取这些线中最大的4条线和。这些线的交点将是顶点