【java基于OpenCV进行图片纠正】

该文章详细介绍了如何使用Java结合OpenCV库进行图像纠正,特别是针对四边形的校正。首先,文章提供了计算角度、找到最大正方形轮廓、计算点间距离和两直线交点等辅助函数。接着,文章展示了主要的识别函数,包括边缘检测、轮廓查找、凸包计算、多边形拟合以及角度判断,用于识别和纠正图像中的四边形。最后,通过透视变换实现图像的纠正。
摘要由CSDN通过智能技术生成

java基于OpenCV进行图片纠正

只需要输入图片数据即可进行纠正,希望文章对你有所帮助

 // 根据三个点计算中间那个点的夹角   pt1 pt0 pt2
    private static double getAngle(Point pt1, Point pt2, Point pt0)
    {
        double dx1 = pt1.x - pt0.x;
        double dy1 = pt1.y - pt0.y;
        double dx2 = pt2.x - pt0.x;
        double dy2 = pt2.y - pt0.y;
        return (dx1*dx2 + dy1*dy2)/Math.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
    }

    // 找到最大的正方形轮廓
    private static int findLargestSquare(List<MatOfPoint> squares) {
        if (squares.size() == 0)
            return -1;
        int max_width = 0;
        int max_height = 0;
        int max_square_idx = 0;
        int currentIndex = 0;
        for (MatOfPoint square : squares) {
            Rect rectangle = Imgproc.boundingRect(square);
            if (rectangle.width >= max_width && rectangle.height >= max_height) {
                max_width = rectangle.width;
                max_height = rectangle.height;
                max_square_idx = currentIndex;
            }
            currentIndex++;
        }
        return max_square_idx;
    }

    // 点到点的距离
    private static double getSpacePointToPoint(Point p1, Point p2) {
        double a = p1.x - p2.x;
        double b = p1.y - p2.y;
        return Math.sqrt(a * a + b * b);
    }

    // 两直线的交点
    private static Point computeIntersect(double[] a, double[] b) {
        if (a.length != 4 || b.length != 4)
            {throw new ClassFormatError();}
        double x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3], x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];
        double d = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
        if (d != 0) {
            Point pt = new Point();
            pt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
            pt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
            return pt;
        }
        else{
            return new Point(-1, -1);
        }
    }

    // 对多个点按顺时针排序
    private static void sortCorners(List<Point> corners) {
        if (corners.size() == 0) return;
        Point p1 = corners.get(0);
        int index = 0;
        for (int i = 1; i < corners.size(); i++) {
            Point point = corners.get(i);
            if (p1.x > point.x) {
                p1 = point;
                index = i;
            }
        }

        corners.set(index, corners.get(0));
        corners.set(0, p1);

        Point lp = corners.get(0);
        for (int i = 1; i < corners.size(); i++) {
            for (int j = i + 1; j < corners.size(); j++) {
                Point point1 = corners.get(i);
                Point point2 = corners.get(j);
                if ((point1.y-lp.y*1.0)/(point1.x-lp.x)>(point2.y-lp.y*1.0)/(point2.x-lp.x)) {
                    Point temp = point1.clone();
                    corners.set(i, corners.get(j));
                    corners.set(j, temp);
                }
            }
        }
    }

上面是待会要用到的计算函数

接下来就是主要的识别函数


private void Correct(Mat src) {
		Mat[] mat_list = new Mat[9];
		int img_w = src.cols();
		int img_h = src.rows();
		Mat gary = new Mat();
		Mat edges = new Mat();
		Mat hierarchy =new Mat();
		Mat rotated_image = new Mat();
		Mat crop_image = new Mat();
		List<MatOfPoint> contours = new ArrayList<>();
		Imgproc.cvtColor(src, gary, Imgproc.COLOR_RGB2GRAY);
		Imgproc.threshold(gary, edges, 70,255,Imgproc.THRESH_BINARY_INV);
		HighGui.imshow("edges", edges);
	    HighGui.waitKey(0);
		Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
// 找出轮廓对应凸包的四边形拟合
        List<MatOfPoint> squares = new ArrayList<>();
        List<MatOfPoint> hulls = new ArrayList<>();
        MatOfInt hull = new MatOfInt();
        MatOfPoint2f approx = new MatOfPoint2f();
        approx.convertTo(approx, CvType.CV_32F);

        for (MatOfPoint contour: contours) {
            // 边框的凸包
            Imgproc.convexHull(contour, hull);
//            Imgproc.minAreaRect(approx);
            // 用凸包计算出新的轮廓点
            Point[] contourPoints = contour.toArray();
            int[] indices = hull.toArray();
            List<Point> newPoints = new ArrayList<>();
            for (int index : indices) {
                newPoints.add(contourPoints[index]);
            }
            
            MatOfPoint2f contourHull = new MatOfPoint2f();
            contourHull.fromList(newPoints);

            // 多边形拟合凸包边框(此时的拟合的精度较低)
            Imgproc.approxPolyDP(contourHull, approx, Imgproc.arcLength(contourHull, true)*0.02, true);

            // 筛选出面积大于某一阈值的,且四边形的各个角度都接近直角的凸四边形
            MatOfPoint approxf1 = new MatOfPoint();
            approx.convertTo(approxf1, CvType.CV_32S);
            System.out.println("---------------------------------------");
            System.out.println(approx.rows());
            System.out.println(Math.abs(Imgproc.contourArea(approx)));
            System.out.println(Imgproc.isContourConvex(approxf1));
            System.out.println("---------------------------------------");
            if (approx.rows() == 4 && Math.abs(Imgproc.contourArea(approx)) > 2000 &&
                    Imgproc.isContourConvex(approxf1)) {
                double maxCosine = 0;
                for (int j = 2; j < 5; j++) {
                    double cosine = Math.abs(getAngle(approxf1.toArray()[j%4], approxf1.toArray()[j-2], approxf1.toArray()[j-1]));
                    maxCosine = Math.max(maxCosine, cosine);
                    System.out.println("maxCosine" + maxCosine);
                }
                // 角度大概72度
                if (maxCosine < 0.3) {
                    MatOfPoint tmp = new MatOfPoint();
                    contourHull.convertTo(tmp, CvType.CV_32S);
                    squares.add(approxf1);
                    hulls.add(tmp);
                }
            }
        }
        Imgproc.polylines(src, contours, true, new Scalar(255, 0, 0));
//        Imgproc.drawContours(src, contours, -1, new Scalar(255, 0, 0));
        HighGui.imshow("src", src);
	    HighGui.waitKey(0);
        // 找出外接矩形最大的四边形
        int index = findLargestSquare(squares);
        if(index == -1) {
        	System.out.println("外接矩阵最大四边形没找到");
        	return mat_list;
        }
        MatOfPoint largest_square = squares.get(index);
        if (largest_square.rows() == 0 || largest_square.cols() == 0)
            return mat_list;

        // 找到这个最大的四边形对应的凸边框,再次进行多边形拟合,此次精度较高,拟合的结果可能是大于4条边的多边形
        MatOfPoint contourHull1 = hulls.get(index);
        MatOfPoint2f tmp = new MatOfPoint2f();
        contourHull1.convertTo(tmp, CvType.CV_32F);
        Imgproc.approxPolyDP(tmp, approx, 3, true);
        List<Point> newPointList = new ArrayList<>();
        double maxL = Imgproc.arcLength(approx, true) * 0.02;

        
        // 找到高精度拟合时得到的顶点中 距离小于低精度拟合得到的四个顶点maxL的顶点,排除部分顶点的干扰
        for (Point p : approx.toArray()) {
            if (!(getSpacePointToPoint(p, largest_square.toList().get(0)) > maxL &&
                    getSpacePointToPoint(p, largest_square.toList().get(1)) > maxL &&
                    getSpacePointToPoint(p, largest_square.toList().get(2)) > maxL &&
                    getSpacePointToPoint(p, largest_square.toList().get(3)) > maxL)) {
                newPointList.add(p);
            }
        }

        // 找到剩余顶点连线中,边长大于 2 * maxL的四条边作为四边形物体的四条边
        List<double[]> lines = new ArrayList<>();
        for (int i = 0; i < newPointList.size(); i++) {
            Point p1 = newPointList.get(i);
            Point p2 = newPointList.get((i+1) % newPointList.size());
            if (getSpacePointToPoint(p1, p2) > 2 * maxL) {
                lines.add(new double[]{p1.x, p1.y, p2.x, p2.y});
            }
        }

        // 计算出这四条边中 相邻两条边的交点,即物体的四个顶点
        List<Point> corners = new ArrayList<>();
        for (int i = 0; i < lines.size(); i++) {
            Point corner = computeIntersect(lines.get(i),lines.get((i+1) % lines.size()));
            corners.add(corner);
        }

        // 对顶点顺时针排序

        sortCorners(corners);

        // 计算目标图像的尺寸
        Point p0 = corners.get(0);
        Point p1 = corners.get(1);
        Point p2 = corners.get(2);
        Point p3 = corners.get(3);
        Point[] pointArr = new Point[4];
        pointArr[0] = p0;
        pointArr[1] = p1;
        pointArr[2] = p2;
        pointArr[3] = p3;
        MatOfPoint2f matOfPoint2f = new MatOfPoint2f(pointArr);
        System.out.println(Imgproc.minAreaRect(matOfPoint2f));
        double space0 = getSpacePointToPoint(p0, p1);
        double space1 = getSpacePointToPoint(p1, p2);
        double space2 = getSpacePointToPoint(p2, p3);
        double space3 = getSpacePointToPoint(p3, p0);
//        System.out.println(corners +"-" +space0 +"-"+ space1 +"-"+space2 +"-"+space3);
        double imgWidth = space1 > space3 ? space1 : space3;
        double imgHeight = space0 > space2 ? space0 : space2;
//        System.out.println("变化前四个点"+ p0+"-"+ p1+"-"+ p2+"-"+ p3);
        System.out.println( space0+"-"+ space1+"-"+ space2+"-"+ space3);
        // 如果提取出的图片宽小于高,则旋转90度
        if (imgWidth < imgHeight) {
            double temp = imgWidth;
            imgWidth = imgHeight;
            imgHeight = temp;
            Point tempPoint = p0.clone();
            p0 = p3.clone();
            p3 = p2.clone();
            p2 = p1.clone();
            p1 = tempPoint.clone();
        }
//        double x_ = p0.x - p2.x;
//        double y_ = p0.y - p2.y;
//        if() {
//        	
//        }
        System.out.println(p0 + "-" + p2);
        Imgproc.putText(src, "P0", p0, Imgproc.FONT_HERSHEY_COMPLEX, 0.8, new Scalar(0,0,0));
        Imgproc.putText(src, "P1", p1, Imgproc.FONT_HERSHEY_COMPLEX, 0.8, new Scalar(0,0,0));
        Imgproc.putText(src, "P2", p2, Imgproc.FONT_HERSHEY_COMPLEX, 0.8, new Scalar(0,0,0));
        Imgproc.putText(src, "P3", p3, Imgproc.FONT_HERSHEY_COMPLEX, 0.8, new Scalar(0,0,0));
        
        HighGui.imshow("src image", src);
	    HighGui.waitKey(0);
//        System.out.println("变化后四个点"+ p0+"-"+ p1+"-"+ p2+"-"+ p3);
        Mat quad = Mat.zeros((int)img_h, (int)img_w, CvType.CV_8UC3);

        MatOfPoint2f cornerMat = new MatOfPoint2f(p0, p1, p2, p3);
        MatOfPoint2f quadMat = new MatOfPoint2f(new Point(imgWidth*0.4, imgHeight*1.6),
                new Point(imgWidth*0.4, imgHeight*0.4),
                new Point(imgWidth*1.6, imgHeight*0.4),
                new Point(imgWidth*1.6, imgHeight*1.6));

        // 提取图像
        Mat transmtx = Imgproc.getPerspectiveTransform(cornerMat, quadMat);
        Imgproc.warpPerspective(src, rotated_image, transmtx, quad.size());
        // return quad;
		HighGui.imshow("Rotated image", rotated_image);
	    HighGui.waitKey(0);

如果此文章对您有所帮助而您又愿意赞助鼓励作者更好地去创作可以点击此处打赏作者

Java中使用OpenCV进行图片定位,可以通过以下步骤实现: 1. 加载OpenCV库 在Java代码中,需要先加载OpenCV库。可以使用以下代码: ```java System.loadLibrary(Core.NATIVE_LIBRARY_NAME); ``` 2. 加载图像 使用`Imgcodecs.imread()`函数可以加载图像,例如: ```java Mat image = Imgcodecs.imread("path/to/image.jpg"); ``` 3. 定位目标 使用OpenCV的定位算法,可以找到图像中的目标。例如,在图像中查找一个矩形,可以使用以下代码: ```java MatOfRect rectangles = new MatOfRect(); CascadeClassifier classifier = new CascadeClassifier("path/to/classifier.xml"); classifier.detectMultiScale(image, rectangles); ``` 这里,`CascadeClassifier`是一个级联分类器,用于检测目标。`detectMultiScale`函数返回一个矩阵,其中包含检测到的所有矩形的坐标。 4. 绘制矩形 最后,可以使用OpenCV的绘图函数,在图像中绘制矩形。例如: ```java for (Rect rect : rectangles.toArray()) { Imgproc.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 0, 255), 2); } ``` 这里,`rectangle`函数在图像中绘制矩形,`toArray`函数将矩阵转换为数组,`Scalar`表示绘制颜色,`2`表示绘制线条的宽度。 5. 保存结果 最后,可以使用`Imgcodecs.imwrite()`函数将结果保存到磁盘上。例如: ```java Imgcodecs.imwrite("path/to/result.jpg", image); ``` 这里,`imwrite`函数将处理后的图像保存到磁盘上。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值