首页 > 解决方案 > 如何使用 PathIterator 创建 Area 对象?

问题描述

我显然在这里错过了一个重要的概念。我已经编写了使用鼠标事件在现有 BufferedImage 上绘制边界(多边形)的代码。以下是相关部分:

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();
}

一切正常,我可以保存带有我的绘图的图像: 带有绘图 的图像 顶点(pts)列表和线段(segments)都是描述区域/形状/多边形的。我希望仅从原始图像中提取边界内的区域。也就是说,我计划通过移动所有像素来创建一个新的 BufferedImage,测试它们是否落入图中,如果有则保留它们。所以我想从我在绘制形状时收集的点和线段创建一个区域。一切都在说:创建一个 AREA 变量和“getPathIterator”。但在什么形状上?我的 AREA 变量将为空。路径迭代器如何访问我列表中的点?

我也浏览过文献和这个网站。我错过了一些东西。

标签: javabufferedimageshapesarea

解决方案


Thank you haraldK for your suggestion. Before I saw your post, I came to a similar conclusion: Using the Arraylist of vertices from the paint operation, I populated a "Path2D.Float" object called "contour" by looping through the points list that was created during the "painting" operation. Using this "contour" object, I instantiated an Area called "interferogram". Just to check my work, I created another PathIterator, "PI", from the Area and decomposed the Area, "interferogram" into "segments" sending the results to the console. I show the code below:

private void mnuitmKeepInsideActionPerformed(java.awt.event.ActionEvent evt)                                                 
{                                                     
    // Keeps the inner area of interest
    // Vertices is the "pts" list from Class MouseDrawing (mask)
    // It is already a closed path
    ArrayList<Point2D.Float> vertices = 
            new ArrayList<>(this.mask.getVertices()); 
    this.contour = new Path2D.Float(Path2D.WIND_NON_ZERO);

    // Read the vertices into the Path2D variable "contour"
    this.contour.moveTo((float)vertices.get(0).getX(), 
        (float)vertices.get(0).getY()); //Starting location

    for(int ivertex = 1; ivertex < vertices.size(); ivertex++)
    {
        this.contour.lineTo((float)vertices.get(ivertex).getX(), 
            (float)vertices.get(ivertex).getY());                           
    }      
    this.interferogram = new Area(this.contour);        
    PathIterator PI = this.interferogram.getPathIterator(null);

    //Test print out the segment types and vertices for debug
    float[] p = new float[6];
    int icount = 0;
    while( !PI.isDone())
    {
        int type = PI.currentSegment(p);
        System.out.print(icount);
        System.out.print(" Type " + type);
        System.out.print(" X " + p[0]);
        System.out.println(" Y " + p[1]);
        icount++;
        PI.next();
    }

    BufferedImage masked = Mask(this.image_out, this.interferogram);
    // Write image to file for debug
    String dir;
    dir = System.getProperty("user.dir");
    dir = dir + "\\00masked.png";
    writeImage(masked, dir, "PNG");

}                                                

Next, I applied the mask to the image testing each pixel for inclusion in the area using the code below:

public BufferedImage Mask(BufferedImage BIM, Area area)
{
    /** Loop through the pixels in the image and test each one for inclusion
    *   within the area.
    *   Change the colors of those outside
    **/

    Point2D p = new Point2D.Double(0,0);
    // rgb should be white
    int rgb = (255 << 24);
    for (int row = 0; row < BIM.getWidth(); row++)
    {
        for (int col = 0; col < BIM.getHeight(); col++)
        {
            p.setLocation(col, row);
            if(!area.contains(p))
            {
                BIM.setRGB(col, row, rgb);
            }
        }
    }
    return BIM;
}
public static BufferedImage deepCopy(BufferedImage B2M) 
{
    ColorModel cm = B2M.getColorModel();
    boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
    WritableRaster raster = B2M.copyData(B2M.getRaster()
            .createCompatibleWritableRaster());
    return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}

This worked beautifully (I was surprised!) except for one slight detail: the lines of the area appeared around the outside of the masked image. In order to remedy this, I copied the original (resized) image before the painting operation. Many thanks to user1050755 (Nov 2014) for the routine deepCopy that I found on this website. Applying my mask to the copied image resulted in the portion of the original image I wanted without the mask lines. The result is shown in the attached picture. I am stoked! masked image


推荐阅读