Search code examples
pythonimagemasking

Custom Polygon Image Boolean Mask


I am having trouble formulating a solution to a problem and I don't really know the right keywords to search for so I come here for some help.

I have an astronomy image that has stars mapped to the image, which is essentially saying I have some image with a set of points randomly spread out over the it with x and y pixel coordinates. The only problem is some of these "stars" are just bad detections and our attempts to filter them out while also keeping good points as yielded lack luster results. These points come from bad pixel columns or foreground stars that are saturating the imager.

I want to then be able to go into the image by hand an specify shapes bound by coordinates that will exclude any points from these areas. For example, I have some big blown up diffraction spiking star that I want to put some kind of boxy star around it. Another is some slanted bad column that I want to exclude so I want to put a rhombus around this and exclude all the bad points within it.

This is tantamount to me wanting to be able to form some kind of polygon mask given a set of connecting points, and be able to apply that to the image where it will then exclude those points within.

Just doing some brain storming and trying to find something on the internet, I had a thought of making some kind of custom polygon object that can be applied to all points which thus widdling down the bad points one shape at a time.

The only problem is I am having a hard time imagining how I would do this. Is there any good resources or advice for implementing such a mask? Elsewise, is there a better technique for what I am trying to achieve?


Solution

  • This problem called perfectly for something call point-in-polygon. Some research I got a hold of an algorithm that essentially takes a simple polygon and finds the intersection with any edges to the right of the point. If there is an even number of collisions then the point is outside of the polygon and vice-versa for odd number of collisions; this is called the even-odd rule. There are some limitations but with a high degree of accuracy in point positions it is very successful.

    Here is a link to a useful source I found on the two most used algorithms in this problem space: point-in-polygon.

    Here is the solution I ended up coming up with to apply the algorithm in Python (hope it might help others):

    class Polygon():
    """
    Make some polygon with edges and vertices.
    """
    def __init__(self, points):
        """
        Takes in list of tuple points specifying each vertex.
        """
    
        ## Build polygon rith a list of edges
        self.edges = []
        for i in range(len(points)):
            if(i < (len(points) - 1)):
                # construct with the vertices of i to len - 2
                vi = Vertex(points[i][0], points[i][1])
                vj = Vertex(points[i+1][0], points[i+1][1])
                e_current = Edge(vi, vj)
                self.edges.append(e_current)
            else:
                # construct last vertex that connects back to beginning
                vi = Vertex(points[i][0], points[i][1])
                vj = Vertex(points[0][0], points[0][1])
                e_current = Edge(vi, vj)
                self.edges.append(e_current)
    
    
    def isInside(self, P):
        """
        Takes in a tuple P and sees if this point is inside the instance of polygon.
        """
        P = Vertex(P[0], P[1])
        collisions = 0
        for e in self.edges:
            if(((e.getStartPoint().y <= P.y) and (e.getEndPoint().y > P.y)) or ((e.getStartPoint().y > P.y) and (e.getEndPoint().y <= P.y))):
                vt = 1.0 * (P.y - e.getStartPoint().y) / (e.getEndPoint().y - e.getStartPoint().y)
                if(P.x < e.getStartPoint().x + vt * (e.getEndPoint().x - e.getStartPoint().x)):
                    collisions += 1
    
        if collisions % 2 == 1:
            return True
        else:
            return False
    
    
    def getSize(self):
        return len(self.edges)
    
    
    def toString(self):
        string = ""
        for e in self.edges:
            string += (e.toString() + "\n")
        return string
    
    class Edge():
    """
    Consruct an edge from two vertices vi, vj.
    """
    def __init__(self, vi, vj):
        self.vi = vi
        self.vj = vj
    
    def getStartPoint(self):
        return self.vi
    
    
    def getEndPoint(self):
        return self.vj
    
    def toString(self):
        return "Edge from " + self.vi.toString() + " to " + self.vj.toString() + "."
    
    class Vertex():
    """
    Construct a vertex out of x and y coordinates
    """
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def toString(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"