ImageIO实现提取图片轮廓

前言

  在网上找了很多提取图片轮廓的,结果不是很理想,之前使用opencv实现了提取湖泊轮廓,但是在打包到linux环境的时候出现一堆坑,感觉很麻烦并且占用服务器资源太大了,于是便使用gpt及自己的理解写出了一些使用ImageIO实现提取图片轮廓的代码,在此做一个笔记,存在一些瑕疵,希望有大佬能帮我解决

高德api获取地图图片

 /**
     * 图片高度
     */
    public static final int HEIGHT = 1024;
    /**
     * 图片宽度
     */
    public static final int WIDTH = 1024;

    /**
     * 高德apikey
     */
    public static final String MAP_KEY = "your_key";

    private static void saveLakeImage(double latitude, double longitude, int zoom, String savePath) {
        String apiUrl = "https://restapi.amap.com/v3/staticmap?" +
                "location=" + longitude + "," + latitude +
                "&zoom=" + zoom +
                "&size=" + HEIGHT + "*" + WIDTH +
                "&key=" + MAP_KEY;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(apiUrl);
            connection = (HttpURLConnection) url.openConnection();
            InputStream in = connection.getInputStream();
            FileOutputStream out = new FileOutputStream(savePath);
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

图片转base64

 /**
     * 获取文件base64编码
     *
     * @param path      文件路径
     * @param urlEncode 如果Content-Type是application/x-www-form-urlencoded时,传true
     * @return base64编码信息,不带文件头
     * @throws IOException IO异常
     */
    static String getFileContentAsBase64(String path, boolean urlEncode) throws IOException {
        byte[] b = Files.readAllBytes(Paths.get(path));
        String base64 = Base64.getEncoder().encodeToString(b);
        if (urlEncode) {
            base64 = URLEncoder.encode(base64, "utf-8");
        }
        return base64;
    }

灰度转换

// 灰度转换
    private static void getGrayImage(BufferedImage image, String grayPath) throws IOException {
        int width = image.getWidth();
        int height = image.getHeight();
        // 遍历图像像素,查找边缘像素
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // 获取像素的RGB值
                int rgb = image.getRGB(x, y);
                // 如果像素为黑色,则将其作为轮廓像素
                if (isWithinColorRange(rgb)) {
                    image.setRGB(x, y, Color.BLACK.getRGB());
                } else {
                    image.setRGB(x, y, Color.white.getRGB());
                }
            }
        }
        File outputFile = new File(grayPath);
        ImageIO.write(image, "jpg", outputFile);
    }

    private static boolean isWithinColorRange(int rgb) {
        Color color1 = new Color(162, 203, 254); // 第一个颜色范围的RGB值
        Color color2 = new Color(172, 209, 254); // 第二个颜色范围的RGB值
        Color pixelColor = new Color(rgb);
        return (pixelColor.getRed() >= color1.getRed() && pixelColor.getRed() <= color2.getRed()) &&
                (pixelColor.getGreen() >= color1.getGreen() && pixelColor.getGreen() <= color2.getGreen()) &&
                (pixelColor.getBlue() >= color1.getBlue() && pixelColor.getBlue() <= color2.getBlue());
    }

获取轮廓坐标

public static void getLake(String grayPath, String lakePath,double lat,double lng,int zoom) throws IOException {
        String base64 = getFileContentAsBase64(grayPath, false);
        // 解码图像数据
        byte[] imageBytes = java.util.Base64.getDecoder().decode(base64);
        ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
        BufferedImage sourceImage = ImageIO.read(bis);
        // 边缘跟踪
        BufferedImage edgeImage = traceEdges(sourceImage);
        ImageIO.write(edgeImage, "jpg", new File(lakePath));
        // 将结果图像写入文件
        List<Point> contours = findContours(edgeImage);
        List<double[]> contourCoordinates = new ArrayList<>();
        List<double[]> lake = new ArrayList<>();
        Point center = new Point(edgeImage.getWidth() / 2, edgeImage.getHeight() / 2);
        if (contours.size() > 0) {
            for (Point point : contours) {
                contourCoordinates.add(new double[]{point.x, point.y});
                lake.add(toLocation(lat,lng, zoom, center, point.x, point.y));
            }
        }
        System.out.println(JSON.toJSONString(contourCoordinates));
        System.out.println(JSON.toJSONString(lake));
    }


//边缘跟踪
private static BufferedImage traceEdges(BufferedImage sourceImage) {
        int width = sourceImage.getWidth();
        int height = sourceImage.getHeight();
        BufferedImage edgeImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);

        // 使用Sobel算子进行边缘检测
        int[][] sobelX = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
        int[][] sobelY = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};

        for (int y = 1; y < height - 1; y++) {
            for (int x = 1; x < width - 1; x++) {
                int gx = 0;
                int gy = 0;
                for (int i = -1; i <= 1; i++) {
                    for (int j = -1; j <= 1; j++) {
                        int pixel = sourceImage.getRGB(x + i, y + j) & 0xFF; // 获取灰度值
                        gx += sobelX[i + 1][j + 1] * pixel;
                        gy += sobelY[i + 1][j + 1] * pixel;
                    }
                }
                int magnitude = (int) Math.sqrt(gx * gx + gy * gy);
                if (magnitude > 100) { // 使用阈值进行二值化
                    edgeImage.setRGB(x, y, Color.BLACK.getRGB());
                } else {
                    edgeImage.setRGB(x, y, Color.WHITE.getRGB());
                }
            }
        }
        return edgeImage;
    }

//获取中心点最近的轮廓像素坐标
private static List<Point> findContours(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        boolean[][] visited = new boolean[width][height]; // 用于记录每个像素是否已被访问
        double centerX = width / 2.0;
        double centerY = height / 2.0;
        double minDistance = Double.MAX_VALUE;
        List<Point> nearestContour = null;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                if (image.getRGB(x, y) == Color.BLACK.getRGB() && !visited[x][y]) {
                    List<Point> contour = floodFill(image, x, y, visited, width, height);

                    // 计算轮廓到中心点的距离
                    double distance = calculateContourDistance(contour, centerX, centerY);
                    if (distance < minDistance) {
                        minDistance = distance;
                        nearestContour = contour;
                    }
                }
            }
        }
        return nearestContour;
    }

//获取轮廓坐标的算法
 private static List<Point> floodFill(BufferedImage image, int x, int y, boolean[][] visited, int width, int height) {
        List<Point> contour = new ArrayList<>();
        Stack<Point> stack = new Stack<>();
        stack.push(new Point(x, y));
        while (!stack.isEmpty()) {
            Point point = stack.pop();
            int px = point.x;
            int py = point.y;
            if (px >= 0 && px < width && py >= 0 && py < height && image.getRGB(px, py) == Color.BLACK.getRGB() && !visited[px][py]) {
                contour.add(point);
                image.setRGB(px, py, 0); // 将像素点设置为0,表示已访问
                visited[px][py] = true; // 标记为已访问
                stack.push(new Point(px + 1, py));
                stack.push(new Point(px - 1, py));
                stack.push(new Point(px, py + 1));
                stack.push(new Point(px, py - 1));
            }
        }
        visited[x][y] = true; // 标记为已访问

        return contour;
    }

问题

因为无法将轮廓坐标像素抽取成一个像素点连成的坐标系,导致坐标存在大量冗余,页面图形展示不好,并且上述方法对轮廓超出图像的处理会报错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值