Why is my shapely polygon generated from a mask invalid?












1














I am trying to make a shapely Polygon from a binary mask, but I always end up with an invalid Polygon. How can I make a valid polygon from an arbitrary binary mask? Below is an example using a circular mask. I suspect that it is because the points I get from the mask contour are out of order, which is apparent when I plot the points (see images below).



import matplotlib.pyplot as plt
import numpy as np
from shapely.geometry import Point, Polygon
from scipy.ndimage.morphology import binary_erosion
from skimage import draw

def get_circular_se(radius=2):

N = (radius * 2) + 1
se = np.zeros(shape=[N,N])
for i in range(N):
for j in range(N):
se[i,j] = (i - N / 2)**2 + (j - N / 2)**2 <= radius**2
se = np.array(se, dtype="uint8")
return se

return new_regions, np.asarray(new_vertices)

#generates a circular mask
side_len = 512
rad = 100
mask = np.zeros(shape=(side_len, side_len))
rr, cc = draw.circle(side_len/2, side_len/2, radius=rad, shape=mask.shape)
mask[rr, cc] = 1

#makes a polygon from the mask perimeter
se = get_circular_se(radius=1)
contour = mask - binary_erosion(mask, structure=se)
pixels_mask = np.array(np.where(contour==1)[::-1]).T
polygon = Polygon(pixels_mask)
print polygon.is_valid

>>False

#plots the results
fig, ax = plt.subplots()
ax.imshow(mask,cmap='Greys_r')
ax.plot(pixels_mask[:,0],pixels_mask[:,1],'b-',lw=0.5)
plt.tight_layout()
plt.show()


enter image description hereenter image description here










share|improve this question



























    1














    I am trying to make a shapely Polygon from a binary mask, but I always end up with an invalid Polygon. How can I make a valid polygon from an arbitrary binary mask? Below is an example using a circular mask. I suspect that it is because the points I get from the mask contour are out of order, which is apparent when I plot the points (see images below).



    import matplotlib.pyplot as plt
    import numpy as np
    from shapely.geometry import Point, Polygon
    from scipy.ndimage.morphology import binary_erosion
    from skimage import draw

    def get_circular_se(radius=2):

    N = (radius * 2) + 1
    se = np.zeros(shape=[N,N])
    for i in range(N):
    for j in range(N):
    se[i,j] = (i - N / 2)**2 + (j - N / 2)**2 <= radius**2
    se = np.array(se, dtype="uint8")
    return se

    return new_regions, np.asarray(new_vertices)

    #generates a circular mask
    side_len = 512
    rad = 100
    mask = np.zeros(shape=(side_len, side_len))
    rr, cc = draw.circle(side_len/2, side_len/2, radius=rad, shape=mask.shape)
    mask[rr, cc] = 1

    #makes a polygon from the mask perimeter
    se = get_circular_se(radius=1)
    contour = mask - binary_erosion(mask, structure=se)
    pixels_mask = np.array(np.where(contour==1)[::-1]).T
    polygon = Polygon(pixels_mask)
    print polygon.is_valid

    >>False

    #plots the results
    fig, ax = plt.subplots()
    ax.imshow(mask,cmap='Greys_r')
    ax.plot(pixels_mask[:,0],pixels_mask[:,1],'b-',lw=0.5)
    plt.tight_layout()
    plt.show()


    enter image description hereenter image description here










    share|improve this question

























      1












      1








      1


      1





      I am trying to make a shapely Polygon from a binary mask, but I always end up with an invalid Polygon. How can I make a valid polygon from an arbitrary binary mask? Below is an example using a circular mask. I suspect that it is because the points I get from the mask contour are out of order, which is apparent when I plot the points (see images below).



      import matplotlib.pyplot as plt
      import numpy as np
      from shapely.geometry import Point, Polygon
      from scipy.ndimage.morphology import binary_erosion
      from skimage import draw

      def get_circular_se(radius=2):

      N = (radius * 2) + 1
      se = np.zeros(shape=[N,N])
      for i in range(N):
      for j in range(N):
      se[i,j] = (i - N / 2)**2 + (j - N / 2)**2 <= radius**2
      se = np.array(se, dtype="uint8")
      return se

      return new_regions, np.asarray(new_vertices)

      #generates a circular mask
      side_len = 512
      rad = 100
      mask = np.zeros(shape=(side_len, side_len))
      rr, cc = draw.circle(side_len/2, side_len/2, radius=rad, shape=mask.shape)
      mask[rr, cc] = 1

      #makes a polygon from the mask perimeter
      se = get_circular_se(radius=1)
      contour = mask - binary_erosion(mask, structure=se)
      pixels_mask = np.array(np.where(contour==1)[::-1]).T
      polygon = Polygon(pixels_mask)
      print polygon.is_valid

      >>False

      #plots the results
      fig, ax = plt.subplots()
      ax.imshow(mask,cmap='Greys_r')
      ax.plot(pixels_mask[:,0],pixels_mask[:,1],'b-',lw=0.5)
      plt.tight_layout()
      plt.show()


      enter image description hereenter image description here










      share|improve this question













      I am trying to make a shapely Polygon from a binary mask, but I always end up with an invalid Polygon. How can I make a valid polygon from an arbitrary binary mask? Below is an example using a circular mask. I suspect that it is because the points I get from the mask contour are out of order, which is apparent when I plot the points (see images below).



      import matplotlib.pyplot as plt
      import numpy as np
      from shapely.geometry import Point, Polygon
      from scipy.ndimage.morphology import binary_erosion
      from skimage import draw

      def get_circular_se(radius=2):

      N = (radius * 2) + 1
      se = np.zeros(shape=[N,N])
      for i in range(N):
      for j in range(N):
      se[i,j] = (i - N / 2)**2 + (j - N / 2)**2 <= radius**2
      se = np.array(se, dtype="uint8")
      return se

      return new_regions, np.asarray(new_vertices)

      #generates a circular mask
      side_len = 512
      rad = 100
      mask = np.zeros(shape=(side_len, side_len))
      rr, cc = draw.circle(side_len/2, side_len/2, radius=rad, shape=mask.shape)
      mask[rr, cc] = 1

      #makes a polygon from the mask perimeter
      se = get_circular_se(radius=1)
      contour = mask - binary_erosion(mask, structure=se)
      pixels_mask = np.array(np.where(contour==1)[::-1]).T
      polygon = Polygon(pixels_mask)
      print polygon.is_valid

      >>False

      #plots the results
      fig, ax = plt.subplots()
      ax.imshow(mask,cmap='Greys_r')
      ax.plot(pixels_mask[:,0],pixels_mask[:,1],'b-',lw=0.5)
      plt.tight_layout()
      plt.show()


      enter image description hereenter image description here







      python numpy matplotlib shapely






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 21 '18 at 10:25









      Andrew

      1,2272109




      1,2272109
























          1 Answer
          1






          active

          oldest

          votes


















          0














          In fact I already found a solution that worked for me, but maybe someone has a better one. The problem was indeed that my points were out of order. Input coordinate order is crucial for making valid polygons. So, one just has to put the points in the right order first. Below is an example solution using a nearest neighbor approach with a KDTree, which I've already posted elsewhere for related problems.



          from sklearn.neighbors import KDTree

          def polygonize_by_nearest_neighbor(pp):
          """Takes a set of xy coordinates pp Numpy array(n,2) and reorders the array to make
          a polygon using a nearest neighbor approach.

          """

          # start with first index
          pp_new = np.zeros_like(pp)
          pp_new[0] = pp[0]
          p_current_idx = 0

          tree = KDTree(pp)

          for i in range(len(pp) - 1):

          nearest_dist, nearest_idx = tree.query([pp[p_current_idx]], k=4) # k1 = identity
          nearest_idx = nearest_idx[0]

          # finds next nearest point along the contour and adds it
          for min_idx in nearest_idx[1:]: # skip the first point (will be zero for same pixel)
          if not pp[min_idx].tolist() in pp_new.tolist(): # make sure it's not already in the list
          pp_new[i + 1] = pp[min_idx]
          p_current_idx = min_idx
          break

          pp_new[-1] = pp[0]
          return pp_new

          pixels_mask_ordered = polygonize_by_nearest_neighbor(pixels_mask)
          polygon = Polygon(pixels_mask_ordered)
          print polygon.is_valid

          >>True

          #plots the results
          fig, ax = plt.subplots()
          ax.imshow(mask,cmap='Greys_r')
          ax.plot(pixels_mask_ordered[:,0],pixels_mask_ordered[:,1],'b-',lw=2)
          plt.tight_layout()
          plt.show()


          enter image description hereenter image description here






          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53409981%2fwhy-is-my-shapely-polygon-generated-from-a-mask-invalid%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            In fact I already found a solution that worked for me, but maybe someone has a better one. The problem was indeed that my points were out of order. Input coordinate order is crucial for making valid polygons. So, one just has to put the points in the right order first. Below is an example solution using a nearest neighbor approach with a KDTree, which I've already posted elsewhere for related problems.



            from sklearn.neighbors import KDTree

            def polygonize_by_nearest_neighbor(pp):
            """Takes a set of xy coordinates pp Numpy array(n,2) and reorders the array to make
            a polygon using a nearest neighbor approach.

            """

            # start with first index
            pp_new = np.zeros_like(pp)
            pp_new[0] = pp[0]
            p_current_idx = 0

            tree = KDTree(pp)

            for i in range(len(pp) - 1):

            nearest_dist, nearest_idx = tree.query([pp[p_current_idx]], k=4) # k1 = identity
            nearest_idx = nearest_idx[0]

            # finds next nearest point along the contour and adds it
            for min_idx in nearest_idx[1:]: # skip the first point (will be zero for same pixel)
            if not pp[min_idx].tolist() in pp_new.tolist(): # make sure it's not already in the list
            pp_new[i + 1] = pp[min_idx]
            p_current_idx = min_idx
            break

            pp_new[-1] = pp[0]
            return pp_new

            pixels_mask_ordered = polygonize_by_nearest_neighbor(pixels_mask)
            polygon = Polygon(pixels_mask_ordered)
            print polygon.is_valid

            >>True

            #plots the results
            fig, ax = plt.subplots()
            ax.imshow(mask,cmap='Greys_r')
            ax.plot(pixels_mask_ordered[:,0],pixels_mask_ordered[:,1],'b-',lw=2)
            plt.tight_layout()
            plt.show()


            enter image description hereenter image description here






            share|improve this answer


























              0














              In fact I already found a solution that worked for me, but maybe someone has a better one. The problem was indeed that my points were out of order. Input coordinate order is crucial for making valid polygons. So, one just has to put the points in the right order first. Below is an example solution using a nearest neighbor approach with a KDTree, which I've already posted elsewhere for related problems.



              from sklearn.neighbors import KDTree

              def polygonize_by_nearest_neighbor(pp):
              """Takes a set of xy coordinates pp Numpy array(n,2) and reorders the array to make
              a polygon using a nearest neighbor approach.

              """

              # start with first index
              pp_new = np.zeros_like(pp)
              pp_new[0] = pp[0]
              p_current_idx = 0

              tree = KDTree(pp)

              for i in range(len(pp) - 1):

              nearest_dist, nearest_idx = tree.query([pp[p_current_idx]], k=4) # k1 = identity
              nearest_idx = nearest_idx[0]

              # finds next nearest point along the contour and adds it
              for min_idx in nearest_idx[1:]: # skip the first point (will be zero for same pixel)
              if not pp[min_idx].tolist() in pp_new.tolist(): # make sure it's not already in the list
              pp_new[i + 1] = pp[min_idx]
              p_current_idx = min_idx
              break

              pp_new[-1] = pp[0]
              return pp_new

              pixels_mask_ordered = polygonize_by_nearest_neighbor(pixels_mask)
              polygon = Polygon(pixels_mask_ordered)
              print polygon.is_valid

              >>True

              #plots the results
              fig, ax = plt.subplots()
              ax.imshow(mask,cmap='Greys_r')
              ax.plot(pixels_mask_ordered[:,0],pixels_mask_ordered[:,1],'b-',lw=2)
              plt.tight_layout()
              plt.show()


              enter image description hereenter image description here






              share|improve this answer
























                0












                0








                0






                In fact I already found a solution that worked for me, but maybe someone has a better one. The problem was indeed that my points were out of order. Input coordinate order is crucial for making valid polygons. So, one just has to put the points in the right order first. Below is an example solution using a nearest neighbor approach with a KDTree, which I've already posted elsewhere for related problems.



                from sklearn.neighbors import KDTree

                def polygonize_by_nearest_neighbor(pp):
                """Takes a set of xy coordinates pp Numpy array(n,2) and reorders the array to make
                a polygon using a nearest neighbor approach.

                """

                # start with first index
                pp_new = np.zeros_like(pp)
                pp_new[0] = pp[0]
                p_current_idx = 0

                tree = KDTree(pp)

                for i in range(len(pp) - 1):

                nearest_dist, nearest_idx = tree.query([pp[p_current_idx]], k=4) # k1 = identity
                nearest_idx = nearest_idx[0]

                # finds next nearest point along the contour and adds it
                for min_idx in nearest_idx[1:]: # skip the first point (will be zero for same pixel)
                if not pp[min_idx].tolist() in pp_new.tolist(): # make sure it's not already in the list
                pp_new[i + 1] = pp[min_idx]
                p_current_idx = min_idx
                break

                pp_new[-1] = pp[0]
                return pp_new

                pixels_mask_ordered = polygonize_by_nearest_neighbor(pixels_mask)
                polygon = Polygon(pixels_mask_ordered)
                print polygon.is_valid

                >>True

                #plots the results
                fig, ax = plt.subplots()
                ax.imshow(mask,cmap='Greys_r')
                ax.plot(pixels_mask_ordered[:,0],pixels_mask_ordered[:,1],'b-',lw=2)
                plt.tight_layout()
                plt.show()


                enter image description hereenter image description here






                share|improve this answer












                In fact I already found a solution that worked for me, but maybe someone has a better one. The problem was indeed that my points were out of order. Input coordinate order is crucial for making valid polygons. So, one just has to put the points in the right order first. Below is an example solution using a nearest neighbor approach with a KDTree, which I've already posted elsewhere for related problems.



                from sklearn.neighbors import KDTree

                def polygonize_by_nearest_neighbor(pp):
                """Takes a set of xy coordinates pp Numpy array(n,2) and reorders the array to make
                a polygon using a nearest neighbor approach.

                """

                # start with first index
                pp_new = np.zeros_like(pp)
                pp_new[0] = pp[0]
                p_current_idx = 0

                tree = KDTree(pp)

                for i in range(len(pp) - 1):

                nearest_dist, nearest_idx = tree.query([pp[p_current_idx]], k=4) # k1 = identity
                nearest_idx = nearest_idx[0]

                # finds next nearest point along the contour and adds it
                for min_idx in nearest_idx[1:]: # skip the first point (will be zero for same pixel)
                if not pp[min_idx].tolist() in pp_new.tolist(): # make sure it's not already in the list
                pp_new[i + 1] = pp[min_idx]
                p_current_idx = min_idx
                break

                pp_new[-1] = pp[0]
                return pp_new

                pixels_mask_ordered = polygonize_by_nearest_neighbor(pixels_mask)
                polygon = Polygon(pixels_mask_ordered)
                print polygon.is_valid

                >>True

                #plots the results
                fig, ax = plt.subplots()
                ax.imshow(mask,cmap='Greys_r')
                ax.plot(pixels_mask_ordered[:,0],pixels_mask_ordered[:,1],'b-',lw=2)
                plt.tight_layout()
                plt.show()


                enter image description hereenter image description here







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 21 '18 at 10:30









                Andrew

                1,2272109




                1,2272109






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53409981%2fwhy-is-my-shapely-polygon-generated-from-a-mask-invalid%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Wiesbaden

                    Marschland

                    Dieringhausen