【问题标题】:How to detect circlular region in images and centre it with Python?如何检测图像中的圆形区域并用 Python 将其居中?
【发布时间】:2015-10-20 17:21:58
【问题描述】:

我有一个如下图形式的火焰:

我正在尝试检测相机视图的外边缘并将图形居中,以便火焰的圆形视图恰好位于绘图的中心。因为圆圈的位置可能会随着图像捕获日期而变化。有时可能在上半部分,有时在下半部分,等等。

Python 中有没有可以检测视图并将其居中的模块?

可重现的代码

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

【问题讨论】:

  • 您可以尝试创建自己的解决方案。一个想法是像素行和列的嵌套for循环。找到像素不是黑色的最小列。然后找到最大值。这将是您的圆圈的左侧和右侧,为您提供直径(这样您就可以找到中心),然后您可以裁剪掉不必要的黑色像素。

标签: python image-processing matplotlib scikit-image


【解决方案1】:

我认为您有很多选择。我想到的两种简单方法是将您的输入图像设置为低强度值,这会给您一个白色圆圈。然后你可以对它的圆运行霍夫变换来找到中心。

或者你可以使用阈值白色像素的距离变换,并取这个距离变换的最大值:

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

只是为了让您了解这种方法的稳健性,如果我选择了一个非常糟糕的阈值(image = rgb2gray(img) > 0.001 - 太低而无法获得一个漂亮的圆圈),结果几乎相同:

【讨论】:

    【解决方案2】:

    改编自this answer,使用 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()
    

    这会产生:

    【讨论】:

    • 如果你想让这个过程更加健壮,获取所有非零像素的凸包并将圆拟合到它的边缘(或使用其他一些过程来过滤掉内部边缘)。
    猜你喜欢
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 2015-05-22
    • 2011-07-19
    • 2014-03-27
    • 1970-01-01
    • 2021-04-19
    相关资源
    最近更新 更多