Segment an image using python and PIL to calculate centroid and rotations of multiple rectangular objects

醉酒当歌 提交于 2019-12-02 21:25:53

You need to identify each object before finding the corners. You only need the border of the objects, so you could also reduce your initial input to that. Then it is only a matter of following each distinct border to find your corners, the centroid is directly found after you know each distinct border.

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

Note that your input is not binary, so I used a really simple threshold for that. Also, the following code is the simplest 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])

Here is an example of how you can do this by labelling the image, and then taking the centroid for the centers, this is all built in to ndimage in scipy (along with a bunch of other cool image things). For the angles, I've used the rectangle corner intercepts with the edges of the bounding slices.

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

Output:

centers: [(157.17299748926865, 214.20652790151453), (219.91948280928594, 442.7146635321775), (363.06183745583041, 288.57169725293517)]
angles: [7.864024795499545, 26.306963825741803, 7.937188000622946]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!