Slice image in dynamic number of squares (grid) and save corner coordinates of those squares in list

Question:

I’m reading in an image with the Pillow library in Python. I want to "slice" it into squares, and save the corner coordinates of each of the squares in a list. For example in the image below, I would like to save the corner coordinates for square 15. (Top left corner is 0,0)

slices

The first think I do after reading in the image is calculate the modulus of the height and width in pixels by the number of slices, and crop the image so the resulting number of pixels per square is the same and an integer.

from PIL import Image, ImageDraw, ImageFont
fileName = 'eyeImg_86.png'
img = Image.open(fileName)

vertical_slices = 8
horizontal_slices = 4

height = img.height
width = img.width

new_height = height - (height % horizontal_slices)
new_width = width - (width %  vertical_slices)

img = img.crop((0, 0, new_width, new_height))

Then I calculate the size in pixels of each vertical and horizontal step.

horizontal_step = int(new_width / vertical_slices)
vertical_step = int(new_height / horizontal_slices)

And then I loop over the ranges between 0 to the total number of vertical and horizontal slices and append to a nested list (each inner list is a row)

points = []
for i in range(horizontal_slices+1):
    row = []
    for j in range(vertical_slices+1):
        row.append((horizontal_step*j, vertical_step*i))
    points.append(row)

Here’s where I’m struggling to draw and to calculate what I need inside each of these squares. If I try to loop over all those points and draw them on the image.

with Image.open(fileName) as im: 
    im = im.convert(mode='RGB')
    draw = ImageDraw.Draw(im)
    for i in range(horizontal_slices+1):
        if i < horizontal_slices:
            for j in range(vertical_slices+1):
                if j < vertical_slices:
                    draw.line([points[i][j], points[i+1][j-1]], fill=9999999)

slices2

Is there an easy way that I can dynamically give it the rows and columns and save each of the square coordinates to a list of tuples for example?

I’d like to both be able to draw them on top of the original image, and also calculate the number of black pixels inside each of the squares.

EDIT: To add some clarification, since the number of rows and columns of the grid is arbitrary, it will likely not be made of squares but rectangles. Furthermore, the numbering of these rectangles should be done row-wise from left to right, like reading.

Thank you

Asked By: ortunoa

||

Answers:

There were (from my understanding) inconsistencies in your use of "horizontal/vertical"; I also removed the points list, since you can easily convert the rectangle number to its upper-left corner coords (see the function in the end); I draw the grid directly by drawing all horizontal lines and all vertical lines).

from PIL import Image, ImageDraw, ImageFont
fileName = 'test.png'
img = Image.open(fileName)

vertical_slices = 8
horizontal_slices = 6

height = img.height
width = img.width

new_height = height - (height % vertical_slices)
new_width = width - (width %  horizontal_slices)

img = img.crop((0, 0, new_width, new_height))

horizontal_step = int(new_width / horizontal_slices)
vertical_step = int(new_height / vertical_slices)

# drawing the grid

img = img.convert(mode='RGB')
pix = img.load()

draw = ImageDraw.Draw(img)
for i in range(horizontal_slices+1):
    draw.line([(i*horizontal_step,0), (i*horizontal_step,new_height)], fill=9999999)
for j in range(vertical_slices+1):
      draw.line([(0,j*vertical_step), (new_width,j*vertical_step)], fill=9999999)
      
# with rectangles being numbered from 1 (upper left) to v_slices*h_slices (lower right) in reading order

def num_to_ul_corner_coords(num):
    i = (num-1)%horizontal_slices
    j = (num-1)//horizontal_slices
    return(i*horizontal_step,j*vertical_step)

This should do what you want, provided your picture is pure black and white:

def count_black_pixels(num) :
    cnt = 0
    x, y = num_to_ul_corner_coords(num)
    for i in range(horizontal_step):
        for j in range(vertical_step):
            if pix[x+i,y+j] == (0,0,0):
                cnt += 1

    perc = round(cnt/(horizontal_step*vertical_step)*100,2)

    return cnt, perc
Answered By: Swifty
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.