创新实训项目分析——第十二篇

本文详细介绍了图像抗扭曲算法的实现过程,包括寻找轮廓、点集划分、确定四个顶点坐标以及透视变换。通过Canny边缘检测、多边形拟合确定顶点,将点集分配到四个区域,并根据对角线最长原则找到矩形四个顶点。最后,使用透视变换矩阵对图像进行矫正。该方法适用于处理图像扭曲问题,确保清晰度。
摘要由CSDN通过智能技术生成

2021SC@SDUSC


前言

对于图片抗扭曲功能算法的实现,可以划分为以下任务:
1.采⽤寻找轮廓的⽅法,⽤approxPolyDP函数,对图像轮廓点进⾏多边形拟合
2.把图像的四个顶点处的点归类,划分出四个区域{左上,右上,右下,左下},利⽤opencv的寻找轮廓,得到最⼤轮廓,然后⽣成最⼩外接矩形,确定四个顶点的⼤致位置。设置⼀个阀值,与上图中的点集合求距离,⼤于阀值的舍弃,⼩于的保留。
3.所有的点集都落到了四个区域,利⽤矩形中,对⻆线距离最⼤,确定四个顶点的位置
4.根据输⼊和输出点获得图像透视变换的矩阵
5.透视变换。透视变换(Perspective Transformation)是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。
这一篇我们将继续分析寻找轮廓方法的代码

一、项目环境

android studio版本 4.1.2

sdk版本 Compile SDK version:30

Build Tools Version 30.0.3

gradle版本 6.8.3

二、代码分析

1.把点集划分到四个区域中

 // px1 左上,px2左下,py1右上,py2右下
List<Point> px1 = new ArrayList<Point>(), 
px2 = new ArrayList<Point>(), 
py1 = new ArrayList<Point>(),
py2 = new ArrayList<Point>();
   int thresold = 50;// 设置距离阀值
   double distance = 0;

把点集划分到四个区域中,即左上,右上,右下,左下
四个参照点集(通过寻找最大轮廓,进行minAreaRect得到四个点[左上,右上,右下,左下])

    for (int i = 0; i < referencePoints.length; i++) {
        for (int j = 0; j < points.length; j++) {
            distance = Math.pow(referencePoints[i].x - points[j].x, 2)
                    + Math.pow(referencePoints[i].y - points[j].y, 2);
            if (distance < Math.pow(thresold, 2)) {
                if (i == 0) {
                    px1.add(points[j]);
                } else if (i == 1) {
                    py1.add(points[j]);
                } else if (i == 2) {
                    py2.add(points[j]);
                } else if (i == 3) {
                    px2.add(points[j]);
                }
            } else {
                continue;
            }
        }
    }

双重for循环,一个循环四个顶点,一个循环点集,计算顶点[i]和点[j]的距离,如果小于距离阀值,我们就认为这个点归属于这个顶点的那个区域,并且加入所属区域

   Map<String, List> map = new HashMap<String, List>();
   map.put("px1", px1);
   map.put("px2", px2);
   map.put("py1", py1);
   map.put("py2", py2);
   return map;

把四个区域存入map中并且返回

2. 具体的寻找四个顶点的坐标

    Point[] result = new Point[4];// [左上,右上,右下,左下]
    List<Point> px1 = map.get("px1");// 左上
    List<Point> px2 = map.get("px2");// 左下
    List<Point> py1 = map.get("py1");// 右上
    List<Point> py2 = map.get("py2");// 右下
​
    if(px1.size()==0||px2.size()==0||py1.size()==0||py2.size()==0){
        Toast.makeText(getApplicationContext(),"请拍摄清晰一点的图片哦",Toast.LENGTH_LONG).show();
    }

当某个区域内没有任何的顶点,我们没办法进行下一步的操作,可以用消息提醒用户这一张无法进行图片抗扭曲修正,请重新拍摄

 double maxDistance = 0;
 double tempDistance;
    int i, j;
    int p1 = 0, p2 = 0;// 记录点的下标
    // 寻找左上,右下
​
    for (i = 0; i < px1.size(); i++) {
        for (j = 0; j < py2.size(); j++) {
            tempDistance = Math.pow(px1.get(i).x - py2.get(j).x, 2) + Math.pow(px1.get(i).y - py2.get(j).y, 2);
            if (tempDistance > maxDistance) {
                maxDistance = tempDistance;
                p1 = i;
                p2 = j;
            }
        }
    }
       // 寻找左下,右上
    maxDistance = 0;
    for (i = 0; i < px2.size(); i++) {
        for (j = 0; j < py1.size(); j++) {
            tempDistance = Math.pow(px2.get(i).x - py1.get(j).x, 2) + Math.pow(px2.get(i).y - py1.get(j).y, 2);
            if (tempDistance > maxDistance) {
                maxDistance = tempDistance;
                p1 = i;
                p2 = j;
            }
        }
    }

找出左上和右下两个区域点集中距离最大的两个点,并且分别得出两个点的索引号,这一步应该是用来求对角线的距离,然后根据矩形中,对角线最长,找到矩形的四个顶点坐标,我们获取到这四个索引值,即知道了四个顶点的坐标

3.整合寻找四个顶点坐标函数

寻找四个顶点的坐标
思路:

  • 1、canny描边
  • 2、寻找最大轮廓
  • 3、对最大轮廓点集合逼近,得到轮廓的大致点集合
  • 4、把点集划分到四个区域中,即左上,右上,左下,右下
  • 5、根据矩形中,对角线最长,找到矩形的四个顶点坐标
 // 1、canny描边
    Mat cannyMat = canny(src);
    // 2、寻找最大轮廓;
    //3、对最大轮廓点集合逼近,得到轮廓的大致点集合
    Point[] points = useApproxPolyDPFindPoints(cannyMat);
​ //在图像上画出逼近的点
    Mat approxPolyMat = src.clone();
    //获取参照点集
    Point[] referencePoints = findReferencePoint(cannyMat);
​   // 4、把点击划分到四个区域中,即左上,右上,左下,右下(效果还可以)
    Map<String, List> map = pointsDivideArea(points, referencePoints);
​  // 画出标记四个区域中的点集
    Mat areaMat = src.clone();
    List<Point> px1 = map.get("px1");// 左上
    List<Point> px2 = map.get("px2");// 左下
    List<Point> py1 = map.get("py1");// 右上
    List<Point> py2 = map.get("py2");// 右下
  // 5、根据矩形中,对角线最长,找到矩形的四个顶点坐标
    Point[] result = specificFindFourPoint(map);
​   return result;
}

将我们前几步的方法都整合到这一个函数中:
我们利用canny进行边缘检测后,将处理后的图像存储在cannyMat之中,利用 useApproxPolyDPFindPoints()来对cannyMat进行处理,得到最大轮廓之后将最大轮廓的点集存储在Point[]里,并且获得到顶点的参照点,通过参照点将点集划分为左上、左下、右上、右下四个区域,并且通过矩形中,对角线距离最长来计算出四个顶点的坐标。

4.透视变换,矫正图像

        // 找到四个顶点
        Point[] points = findFourPoint(src);
       // Canny,得到边缘检测后的图像
        Mat cannyMat = canny(src);
        // 寻找最大矩形
        RotatedRect rect =findMaxRect(cannyMat);
       // 点的顺序[左上 ,右上 ,右下 ,左下]
        List<Point> listSrcs = java.util.Arrays.asList(points[0], points[1], points[2], points[3]);
        Mat srcPoints = Converters.vector_Point_to_Mat(listSrcs, CvType.CV_32F);

Converters.vector_Point_to_Mat()方法是opencv提供的将list转换为Mat的方法

     Rect r = rect.boundingRect();
        r.x = Math.abs(r.x);
        r.y = Math.abs(r.y);
        List<Point> listDsts = java.util.Arrays.asList(new Point(r.x, r.y), new Point(r.x + r.width, r.y),
       new Point(r.x + r.width, r.y + r.height), new Point(r.x, r.y + r.height));

​//得到r的四个顶点

        Mat dstPoints = Converters.vector_Point_to_Mat(listDsts, CvType.CV_32F);
​        Mat perspectiveMmat = Imgproc.getPerspectiveTransform(srcPoints, dstPoints);

opencv2和opencv3中用于计算透视变换矩阵的函数是cv::getPerspectiveTransform(),C++接口其调用形式如下:
cv::Mat cv::getPerspectiveTransform( // 返回3x3透视变换矩阵
const cv::Point2f* src, // 源图像四个顶点坐标(点数组)
const cv::Point2f* dst // 目标图像上四个顶点的坐标(点数组)
);
输入srcPoints和dstPoints,得到透视变换的矩阵

三、总结

开始了解整个算法中非常重要的一部分:进行透视变换,首先我们要给出源顶点和转换后顶点的位置,从而得到透视变换的矩阵,得到矩阵之后,我们方便对整个图像进行透视变换,我们下一篇将会继续分析透视变换,并且为该项目收尾

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值