Segment the image using python and PIL to calculate the centroid and rotate multiple rectangular objects

I use python and PIL to find the centroid and rotation of various rectangles (and squares) in a 640x480 image, similar to this enter image description here

So far, my code works for one rectangle in the image.

import Image, math def find_centroid(im): width, height = im.size XX, YY, count = 0, 0, 0 for x in xrange(0, width, 1): for y in xrange(0, height, 1): if im.getpixel((x, y)) == 0: XX += x YY += y count += 1 return XX/count, YY/count #Top Left Vertex def find_vertex1(im): width, height = im.size for y in xrange(0, height, 1): for x in xrange (0, width, 1): if im.getpixel((x, y)) == 0: X1=x Y1=y return X1, Y1 #Bottom Left Vertex def find_vertex2(im): width, height = im.size for x in xrange(0, width, 1): for y in xrange (height-1, 0, -1): if im.getpixel((x, y)) == 0: X2=x Y2=y return X2, Y2 #Top Right Vertex def find_vertex3(im): width, height = im.size for x in xrange(width-1, 0, -1): for y in xrange (0, height, 1): if im.getpixel((x, y)) == 0: X3=x Y3=y return X3, Y3 #Bottom Right Vertex def find_vertex4 (im): width, height = im.size for y in xrange(height-1, 0, -1): for x in xrange (width-1, 0, -1): if im.getpixel((x, y)) == 0: X4=x Y4=y return X4, Y4 def find_angle (V1, V2, direction): side1=math.sqrt(((V1[0]-V2[0])**2)) side2=math.sqrt(((V1[1]-V2[1])**2)) if direction == 0: return math.degrees(math.atan(side2/side1)), 'Clockwise' return 90-math.degrees(math.atan(side2/side1)), 'Counter Clockwise' #Find direction of Rotation; 0 = CW, 1 = CCW def find_direction (vertices, C): high=480 for i in range (0,4): if vertices[i][1]<high: high = vertices[i][1] index = i if vertices[index][0]<C[0]: return 0 return 1 def main(): im = Image.open('hopperrotated2.png') im = im.convert('1') # convert image to black and white print 'Centroid ', find_centroid(im) print 'Top Left ', find_vertex1 (im) print 'Bottom Left ', find_vertex2 (im) print 'Top Right', find_vertex3 (im) print 'Bottom Right ', find_vertex4 (im) C = find_centroid (im) V1 = find_vertex1 (im) V2 = find_vertex3 (im) V3 = find_vertex2 (im) V4 = find_vertex4 (im) vertices = [V1,V2,V3,V4] direction = find_direction(vertices, C) print 'angle: ', find_angle(V1,V2,direction) if __name__ == '__main__': main() 

In cases where problems arise, it is when the image has more than one object.

I know that PIL has a find_edges method that gives an image only of the edges, but I have no idea how to use this new edge image to segment the image into separate objects.

 from PIL import Image, ImageFilter im = Image.open('hopperrotated2.png') im1 = im.filter(ImageFilter.FIND_EDGES) im1 = im1.convert('1') print im1 im1.save("EDGES.jpg") 

if I can use edges to segment the image into separate rectangles, then I can just run my first bit of code on each rectangle to get centroid and rotation.

But it would be better to use the edges to calculate the rotation and centroid of each rectangle without having to split the image up.

Any help is greatly appreciated!

+4
source share
2 answers

You need to identify each object before finding angles. You only need the border of the objects, so you can also reduce your initial input. Then you just need to follow each individual border to find your angles, the centroid is located immediately after you know each individual border.

Using the code below, here is what you get (a centroid is a red dot, white text is rotation in degrees):

enter image description here

Please note that your input is not binary, so I used a very simple threshold for this. In addition, the following code is the easiest way to achieve this; there are faster methods in any decent library.

 import sys import math from PIL import Image, ImageOps, ImageDraw orig = ImageOps.grayscale(Image.open(sys.argv[1])) orig_bin = orig.point(lambda x: 0 if x < 128 else 255) im = orig_bin.load() border = Image.new('1', orig.size, 'white') width, height = orig.size bim = border.load() # Keep only border points for x in xrange(width): for y in xrange(height): if im[x, y] == 255: continue if im[x+1, y] or im[x-1, y] or im[x, y+1] or im[x, y-1]: bim[x, y] = 0 else: bim[x, y] = 255 # Find each border (the trivial dummy way). def follow_border(im, x, y, used): work = [(x, y)] border = [] while work: x, y = work.pop() used.add((x, y)) border.append((x, y)) for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)): px, py = x + dx, y + dy if im[px, py] == 255 or (px, py) in used: continue work.append((px, py)) return border used = set() border = [] for x in xrange(width): for y in xrange(height): if bim[x, y] == 255 or (x, y) in used: continue b = follow_border(bim, x, y, used) border.append(b) # Find the corners and centroid of each rectangle. rectangle = [] for b in border: xmin, xmax, ymin, ymax = width, 0, height, 0 mean_x, mean_y = 0, 0 b = sorted(b) top_left, bottom_right = b[0], b[-1] for x, y in b: mean_x += x mean_y += y centroid = (mean_x / float(len(b)), mean_y / float(len(b))) b = sorted(b, key=lambda x: x[1]) curr = 0 while b[curr][1] == b[curr + 1][1]: curr += 1 top_right = b[curr] curr = len(b) - 1 while b[curr][1] == b[curr - 1][1]: curr -= 1 bottom_left = b[curr] rectangle.append([ [top_left, top_right, bottom_right, bottom_left], centroid]) result = orig.convert('RGB') draw = ImageDraw.Draw(result) for corner, centroid in rectangle: draw.line(corner + [corner[0]], fill='red', width=2) cx, cy = centroid draw.ellipse((cx - 2, cy - 2, cx + 2, cy + 2), fill='red') rotation = math.atan2(corner[0][1] - corner[1][1], corner[1][0] - corner[0][0]) rdeg = math.degrees(rotation) draw.text((cx + 10, cy), text='%.2f' % rdeg) result.save(sys.argv[2]) 
+5
source

Here is an example of how you can do this by labeling the image, and then taking a centroid for the centers, all of this is built into ndimage in scipy (along with a bunch of other interesting things). For corners, I used the corner captures of the rectangle with the edges of the bounding fragments.

 import numpy as np import scipy from scipy import ndimage im = scipy.misc.imread('6JYjd.png',flatten=1) im = np.where(im > 128, 0, 1) label_im, num = ndimage.label(im) slices = ndimage.find_objects(label_im) centroids = ndimage.measurements.center_of_mass(im, label_im, xrange(1,num+1)) angles = [] for s in slices: height, width = label_im[s].shape opp = height - np.where(im[s][:,-1]==1)[0][-1] - 1 adj = width - np.where(im[s][-1,:]==1)[0][0] - 1 angles.append(np.degrees(np.arctan2(opp,adj))) print 'centers:', centroids print 'angles:', angles 

Conclusion:

 centers: [(157.17299748926865, 214.20652790151453), (219.91948280928594, 442.7146635321775), (363.06183745583041, 288.57169725293517)] angles: [7.864024795499545, 26.306963825741803, 7.937188000622946] 
+2
source

All Articles