You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用PathIterator基于自定义多边形点集创建Area对象?

如何基于自定义顶点列表创建Area对象提取多边形区域

你已经实现了通过鼠标绘制多边形边界的功能,能收集顶点列表pts和线段列表segments,现在想要提取原始图像中该多边形包围的区域,计划通过创建新的BufferedImage并判断像素是否在多边形内来实现,但不清楚怎么基于自己的点列表构建Area对象——毕竟资料里提到的getPathIterator总是和特定形状绑定,而你的Area目前是空的,不知道怎么关联自己的顶点数据。

以下是你已有的核心代码:

public void paintComponent(Graphics g) { 
    super.paintComponent(g); //Paint parent's background 
    //G3 displays the BufferedImage "Drawing" with each paint 
    Graphics2D G3 = (Graphics2D)g; 
    G3.drawImage(this.Drawing, 0, 0, null); 
    G3.dispose(); 
} 

public void updateDrawing() { 
    int x0, y0, x1, y1; // Vertex coordinates 
    Line2D.Float seg; // grafix is painting the mouse drawing to the BufferedImage "Drawing" 
    if(this.pts.size() > 0) { 
        for(int ip = 0; ip < pts.size(); ip++) { 
            x0 = (int)this.pts.get(ip).x; 
            y0 = (int)this.pts.get(ip).y; 
            this.grafix.drawRect(x0 - this.sqw/2, y0 - this.sqh/2, + this.sqw, this.sqh); 
            if (ip > 0) { 
                x1 = (int)this.pts.get(ip-1).x; 
                y1 = (int)this.pts.get(ip-1).y; 
                this.grafix.drawLine(x1, y1, x0, y0); 
                seg = new Line2D.Float(x1, y1, x0, y0); 
                this.segments.add(seg); 
            } 
        } 
    } 
    repaint(); 
}

// 鼠标事件调用的方法
public void getNextPoint(Point2D p) { 
    this.isDrawing = true; 
    Point2D.Float next = new Point2D.Float(); 
    next.x = (float) p.getX(); 
    next.y = (float) p.getY(); 
    this.pts.add(next); 
    updateDrawing(); 
} 

public void closeBoundary() { 
    //Connects the last point to the first point to close the loop 
    Point2D.Float next = new Point2D.Float(this.pts.get(0).x, this.pts.get(0).y); 
    this.pts.add(next); 
    this.isDrawing = false; 
    updateDrawing(); 
}

解决方案

其实核心是先把你的顶点列表转换成GeneralPath对象——这是Java2D中用来描述任意复杂路径的类,然后用它来初始化Area,之后就能轻松用Area.contains()方法判断像素是否在多边形内了。

步骤1:从顶点列表构建GeneralPath

你可以在closeBoundary()方法完成后(或者专门写一个方法),把pts里的点转换成GeneralPath:

private Area createPolygonArea() {
    // 确保顶点列表至少有3个有效点(闭合后会多一个重复的起点,所以先判断原始点数)
    if (this.pts.size() < 3) {
        return new Area(); // 返回空区域
    }

    GeneralPath path = new GeneralPath();
    // 移动到第一个点
    Point2D.Float firstPoint = this.pts.get(0);
    path.moveTo(firstPoint.x, firstPoint.y);

    // 连接后续的点(注意closeBoundary已经加了重复的起点,所以遍历到倒数第二个点即可)
    for (int i = 1; i < this.pts.size() - 1; i++) {
        Point2D.Float point = this.pts.get(i);
        path.lineTo(point.x, point.y);
    }

    // 闭合路径(其实closeBoundary已经加了起点,不过这里调用closePath更规范)
    path.closePath();

    // 用GeneralPath创建Area
    return new Area(path);
}

步骤2:用Area提取图像区域

有了Area对象后,你就可以遍历原始图像的像素,判断每个像素是否在区域内,然后复制到新的BufferedImage中:

public BufferedImage extractPolygonArea(BufferedImage originalImage) {
    Area polygonArea = createPolygonArea();
    if (polygonArea.isEmpty()) {
        return null; // 没有有效区域
    }

    BufferedImage extractedImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), originalImage.getType());
    Graphics2D g2d = extractedImage.createGraphics();

    // 先填充透明背景(如果需要)
    g2d.setComposite(AlphaComposite.Clear);
    g2d.fillRect(0, 0, extractedImage.getWidth(), extractedImage.getHeight());
    g2d.setComposite(AlphaComposite.SrcOver);

    // 只绘制Area内的像素
    g2d.setClip(polygonArea);
    g2d.drawImage(originalImage, 0, 0, null);

    g2d.dispose();
    return extractedImage;
}

为什么不用PathIterator?

PathIterator其实是GeneralPath(以及其他Shape实现类)提供的迭代器,用来遍历路径上的线段、曲线等元素。你不需要直接操作它,因为Area会自动处理GeneralPath的路径信息——当你用GeneralPath创建Area时,内部已经通过getPathIterator解析了路径的所有顶点和线段。

注意事项

  • 确保你的顶点列表是有序的(按绘制顺序排列),否则构建的路径会混乱,Area也会出错。
  • 在closeBoundary()中你已经添加了重复的起点来闭合线段,所以在构建GeneralPath时,遍历到倒数第二个点就可以了,避免重复绘制最后一条线(path.closePath()会自动连接终点到起点)。

内容的提问来源于stack exchange,提问作者Desertgeek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:49:59