0

I have a figure flame of the form shown below:

enter image description here

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()
Tom Kurushingal
  • 6,086
  • 20
  • 54
  • 86
  • 2
    You could try to create your own solution. One idea would be a nested for loop for rows and columns of pixels. Find the minimum column for which a pixel is not black. Then find the maximum. This would be the left and right sides of your circle, giving you the diameter (so you could find the center), and you could then crop off the unnecessary black pixels. – Andrew Jul 29 '15 at 16:03

2 Answers2

5

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:

center of the flame

Community
  • 1
  • 1
Stefan van der Walt
  • 7,165
  • 1
  • 32
  • 41
  • If you want to make the procedure even more robust, obtain the convex hull of all non-zero pixels and do the circle fit to the edge of that (or use some other procedure to filter out the inside edges). – Stefan van der Walt Jul 29 '15 at 18:11
4

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()

plot output

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: bad result

P.R.
  • 3,785
  • 1
  • 27
  • 47