Python - Find center of object in an image

2019-03-31 02:05发布

问题:

I have an image file that's has a white background with a non-white object. I want to find the center of the object using python (Pillow).

I have found a similar question in c++ but no acceptable answer - How can I find center of object?

Similar question, but with broken links in answer - What is the fastest way to find the center of an irregularly shaped polygon? (broken links in answer)

I also read this page but it doesn't give me a useful recipe - https://en.wikipedia.org/wiki/Smallest-circle_problem

Here's an example image:

Edit: The current solution I'm using is this:

def find_center(image_file):
    img = Image.open(image_file)
    img_mtx = img.load()
    top = bottom = 0
    first_row = True
    # First we find the top and bottom border of the object
    for row in range(img.size[0]):
        for col in range(img.size[1]):
            if img_mtx[row, col][0:3] != (255, 255, 255):
                bottom = row
                if first_row:
                    top = row
                    first_row = False
    middle_row = (top + bottom) / 2  # Calculate the middle row of the object

    left = right = 0
    first_col = True
    # Scan through the middle row and find the left and right border
    for col in range(img.size[1]):
        if img_mtx[middle_row, col][0:3] != (255, 255, 255):
            left = col
            if first_col:
                right = col
                first_col = False
    middle_col = (left + right) / 2  # Calculate the middle col of the object

    return (middle_row, middle_col)

回答1:

If you define center as Center of Mass, then it is not difficult, although the CoM can be outside of your shape. You can interpret your image as a 2D distribution, and you can find its expected value (CoM) using integration (summation).

If you have numpy it is quite simple. First create a numpy array containing 1 where your image is non-white, then to make it a probability distribution divide it by the total number of ones.

from PIL import Image
import numpy as np

im = Image.open('image.bmp')
immat = im.load()
(X, Y) = im.size
m = np.zeros((X, Y))

for x in range(X):
    for y in range(Y):
        m[x, y] = immat[(x, y)] != (255, 255, 255)
m = m / np.sum(np.sum(m))

From this point on it turns into basic probability theory. You find the marginal distributions, then you calculate the expected values as if it was a discrete probability distribution.

# marginal distributions
dx = np.sum(m, 1)
dy = np.sum(m, 0)

# expected values
cx = np.sum(dx * np.arange(X))
cy = np.sum(dy * np.arange(Y))

(cx, cy) is the CoM you are looking for.

Remarks:

  • If you do not have numpy, you can still do it. It is just a bit more tedious as you have to do the summations by loops / comprehensions.
  • This method can easily be extended if you want to assign a 'mass' based on color. You just have to change m[x, y] = immat[(x, y)] != (255, 255, 255) to m[x, y] = f(immat[(x, y)]) where f is an arbitary (non-negative valued) function.
  • If you want to avoid the double loop, you can us np.asarray(im), but then be careful with the indices

No loops:

m = np.sum(np.asarray(im), -1) < 255*3
m = m / np.sum(np.sum(m))

dx = np.sum(m, 0) # there is a 0 here instead of the 1
dy = np.sum(m, 1) # as np.asarray switches the axes, because
                  # in matrices the vertical axis is the main
                  # one, while in images the horizontal one is
                  # the first


回答2:

I would try and find a way to draw a triangle around it, with one point of the triangle at the farthest "points" on the object, and then find the center of that triangle.