如何利用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
相关产品推荐
相关产品推荐

