How to detect circlular region in images and centre it with Python?

时光怂恿深爱的人放手 提交于 2019-12-10 12:06:47

问题


I have a figure flame of the form shown below:

I am trying to detect the outer edge of the camera's view and centre the figure so that circular view of the flame is exactly at the centre of the plot. As the position of the circle might change with the image capture date. Sometimes it might be at the upper half, sometimes lower half, etc.

Are there any modules in Python that can detect the view and centre it?

Reproducible code

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img=mpimg.imread('flame.png')
lum_img = img[:,:,0]
img_plot = plt.imshow(lum_img)
img_plot.set_cmap('jet')
plt.axis('Off')
plt.show()

回答1:


Adapted from this answer, do an edge detection and robustly fit a circle to the outline using RANSAC:

from __future__ import print_function
from skimage import io, feature, color, measure, draw, img_as_float
import numpy as np

image = img_as_float(color.rgb2gray(io.imread('flame.png')))
edges = feature.canny(image)
coords = np.column_stack(np.nonzero(edges))

model, inliers = measure.ransac(coords, measure.CircleModel,
                                min_samples=3, residual_threshold=1,
                                max_trials=1000)

print(model.params)

rr, cc = draw.circle_perimeter(int(model.params[0]),
                               int(model.params[1]),
                               int(model.params[2]),
                               shape=image.shape)

image[rr, cc] = 1

import matplotlib.pyplot as plt
plt.imshow(image, cmap='gray')
plt.scatter(model.params[1], model.params[0], s=50, c='red')
plt.axis('off')
plt.savefig('/tmp/flame_center.png', bbox_inches='tight')
plt.show()

This yields:




回答2:


I think you have plenty of options. Two easy approaches that come to my mind would be to threshold your input image on a low intensity value which will give you a white circle. Then you could run the Hough transform for circles on it to find the center.

Or you can use the distance transform of the thresholded white pixels and take the maximum of this distance transform:

# code derived from watershed example of scikit-image
# http://scikit-image.org/docs/dev/auto_examples/plot_watershed.html

import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage as ndi

from skimage.morphology import watershed
from skimage.feature import peak_local_max
from skimage.color import rgb2gray
from skimage.io import imread

img = imread('flame.png')
image = rgb2gray(img) > 0.01

# Now we want to separate the two objects in image
# Generate the markers as local maxima of the distance to the background
distance = ndi.distance_transform_edt(image)

# get global maximum like described in 
# http://stackoverflow.com/a/3584260/2156909
max_loc = unravel_index(distance.argmax(), distance.shape)

fig, axes = plt.subplots(ncols=4, figsize=(10, 2.7))
ax0, ax1, ax2, ax3 = axes

ax0.imshow(img,interpolation='nearest')
ax0.set_title('Image')
ax1.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
ax1.set_title('Thresholded')
ax2.imshow(-distance, cmap=plt.cm.jet, interpolation='nearest')
ax2.set_title('Distances')
ax3.imshow(rgb2gray(img), cmap=plt.cm.gray, interpolation='nearest')
ax3.set_title('Detected centre')
ax3.scatter(max_loc[1], max_loc[0], color='red')

for ax in axes:
    ax.axis('off')

fig.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0,
                    right=1)
plt.show()

Just to give you an idea how robust this method is, if I pick a very bad threshold (image = rgb2gray(img) > 0.001 -- far too low to get a nice circle), the result is almost the same:



来源:https://stackoverflow.com/questions/31705355/how-to-detect-circlular-region-in-images-and-centre-it-with-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!