Search code examples
python-imaging-library

Python PIL how cut area inside diagonal line?


I would like to know the area inside the spaces between one line and another and then cut them into separate images.

def grid(width, height, wq, hq):
    img = Image.new('RGB', (width, height), color='white')
    pic = ImageDraw.Draw(img) 
    for i in range(0, width, wq):
        for j in range(0, height, hq):
            pic.line([(i, j), (i + wq, j + hq)], fill="red", width=10)
    img.show()
grid(600,600,300,300)

Find the areas, and save the two triangles and the two trapezoids in separate images

https://i.sstatic.net/suEgl.jpg


Solution

  • This isn't going to be easy with PIL, though you probably could do it using ImageDraw.floodfill() to fill in the trapezoids one at a time and then find their locations using Numpy.

    I would suggest you use OpenCV and its findContours(). I would also suggest you ditch full-colour processing if you just want to find an area of one thing and not another thing which reduces processing time and memory pressure. Specifically also, if using OpenCV you need to look for white objects on a black background, so I adapted your code to look like this:

    #!/usr/bin/env python3
    
    from PIL import Image, ImageDraw
    import numpy as np
    import cv2
    
    def grid(width, height, wq, hq):
       img = Image.new('L', (width, height), color='white')
       pic = ImageDraw.Draw(img) 
       for i in range(0, width, wq):
          for j in range(0, height, hq):
             pic.line([(i, j), (i + wq, j + hq)], fill="black", width=10)
       return img
    
    im = grid(600,600,300,200)
    im.save('result.png')
    
    # Make into Numpy array for OpenCV
    na = np.array(im)
    
    # Find the contours
    contours, _ = cv2.findContours(na,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    for i, c in enumerate(contours):
        # Calculate area
        area = cv2.contourArea(c)
        print(f'Contour: {i}, area: {area} pixels')
        # Make new, empty output image
        out = np.zeros((na.shape[0], na.shape[1]), np.uint8)
        # Draw in the contour
        cv2.drawContours(out,[c],0,255,thickness=cv2.FILLED)
        cv2.imwrite(f'contour-{i}.png', out)
    

    Here are the output images, one per contour, all horizontally merged onto a grey background so you can determine the extent of each:

    enter image description here

    Here are the output areas:

    Contour: 0, area: 27936.5 pixels
    Contour: 1, area: 84432.0 pixels
    Contour: 2, area: 112819.5 pixels
    Contour: 3, area: 28130.0 pixels
    Contour: 4, area: 84626.5 pixels
    Contour: 5, area: 0.0 pixels
    

    There are many more features you can test for such as arc length, centroids, moments, circularity, minimum area but I'll leave that up to you.

    You can also use the images generated above as masks and bitwise_and() them with your image to extract just the white areas.

    Remember that you can do your processing in greyscale but still use the resulting masks and areas and apply them to your original full-colour input image.


    If you have some objection to using OpenCV, note that you could use scikit-image and its label and regionprops.