yak*_*you 3 opencv object-detection computer-vision
下面的图片有很多圈子.单击并放大以查看圆圈.
https://drive.google.com/open?id=1ox3kiRX5hf2tHDptWfgcbMTAHKCDizSI
我想要的是使用任何免费语言计算圆圈,例如python.
有功能或想法吗?
编辑:我提出了一个更好的解决方案,部分受到以下答案的启发.我最初想到了这种方法(如OP评论中所述),但我决定反对它.原始图像质量不够好.然而,我改进了这种方法,它的效果非常出色,可以获得更高质量的图像.最初的方法是首先,然后是底层的新方法.
所以这是一种似乎运作良好的一般方法,但绝对只是给出了估计.这假设圆圈的大小大致相同.
首先,图像大多是蓝色的 - 所以在蓝色通道上进行分析似乎是合理的.在这种情况下,使用Otsu阈值处理(无需输入确定最佳阈值)对蓝色通道进行阈值处理似乎非常有效.这并不是太令人惊讶,因为颜色值的分布几乎是二进制的.检查由它产生的面具!
然后,对掩码进行连通分量分析,得到每个分量的面积(分量=掩码中的白色斑点).从connectedComponentsWithStats()
给予(除其他外)区域返回的统计数据,这正是我们所需要的.然后,我们可以通过估算给定组件中基于其面积的圆圈数来简单地计算圆圈.另请注意,除了第一个标签之外,我正在记录每个标签:这是背景标签0
,而不是任何白色斑点.
现在,单个圆的面积有多大?最好让数据告诉我们.因此,您可以计算所有区域的直方图,并且由于单个圆圈比其他任何区域都多,因此该区域的高浓度约为250-270像素左右.或者你可以只取50到350之间的所有区域的平均值,这也应该让你进入类似的球场.
真的在这个直方图中你可以很容易地看到单个圆圈,双圆圈,三重等之间的界限.只有较大的组件才会给出非常粗略的估计.事实上,该区域似乎没有完全线性扩展.两个圆圈的斑点略大于两个单个圆圈,三个斑点比三个单圆圈还要大,依此类推,所以这使得它很难估计得很好,但是四舍五入仍然应该让我们保持接近.如果你想要,你可以包括一个小的乘法参数,随着面积的增加而增加,但是如果没有分析地通过直方图,这很难量化...所以,我并不担心这一点.
单个圆形区域除以平均单个圆形区域应该接近1.并且5个圆圈组的面积除以平均圆形区域应该接近5.这也意味着小的无关紧要的组件,即1或者10或甚至100像素的面积,将不计入总数round(50/avg_circle_size) < 1/2
,所以那些将向下舍入到0的数量.因此我应该能够采取所有组成区域,将它们除以平均圆形大小,圆形通过将它们全部加起来得到一个合适的估计.
import cv2
import numpy as np
img = cv2.imread('circles.png')
mask = cv2.threshold(img[:, :, 0], 255, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
stats = cv2.connectedComponentsWithStats(mask, 8)[2]
label_area = stats[1:, cv2.CC_STAT_AREA]
min_area, max_area = 50, 350 # min/max for a single circle
singular_mask = (min_area < label_area) & (label_area <= max_area)
circle_area = np.mean(label_area[singular_mask])
n_circles = int(np.sum(np.round(label_area / circle_area)))
print('Total circles:', n_circles)
Run Code Online (Sandbox Code Playgroud)
此代码对粗略计数简单有效.
但是,对于圆形组与正常圆形尺寸相比,这里肯定存在一些假设,并且存在这样的问题:在边界处的圆圈将不会被正确计数(这些没有明确定义 - 两个圆形斑点那半截断将看起来更像一个圆圈---没有明确的方法来计算或不用这个方法计算这些数字).此外,我在这里通过Otsu使用自动阈值处理; 您可以通过更仔细的颜色过滤得到(可能更好)结果.此外,在Otsu生成的蒙版中,一些被遮罩的圆圈从其中心移除了一些像素.形态学可以将这些像素重新添加回来,这样可以为单个圆形组件提供(略大)更准确的区域.无论哪种方式,我只想提出一个大致的想法,即如何用最少的代码轻松估算这一点.
之前,目标是计算圈子.这种新方法改为计算圆圈的中心.一般的想法是你的阈值,然后从背景像素填充填充背景(洪水填充像照片编辑应用程序中的油漆桶工具),这样你只能看到中心,如下面的答案所示.
然而,这依赖于全局阈值处理,这对于局部照明变化不稳健.这意味着,由于某些中心比其他中心更亮/更暗,因此单一阈值并不总能获得良好的效果.
在这里,我创建了一个动画来显示不同阈值的循环; 观察一些中心在不同时间出现和消失,这意味着根据您选择的阈值得到不同的数量(这只是图像的一小部分,它随处可见):
请注意,出现在左上角的第一个blob实际上会随着阈值的增加而消失.但是,如果我们实际上将每个帧组合在一起,则每个检测到的像素仍然存在:
但是现在每一个斑点出现,所以我们应该清理每一帧的掩模,以便我们在它们到来时移除单个像素(否则它们可能会累积并且以后很难去除).使用小内核的简单形态开放将删除它们:
应用于整个图像,这种方法工作得非常好,几乎找到每个单元格.只有三个误报(检测到的blob不是中心)和两个我可以发现的未命中,代码非常简单.创建蒙版后要做的最后一件事就是对组件进行计数,减去背景的一个.此处所需的唯一用户输入是单个点到泛洪填充,这是在后台(seed_pt
在代码中).
img = cv2.imread('circles.png', 0)
seed_pt = (25, 25)
fill_color = 0
mask = np.zeros_like(img)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
for th in range(60, 120):
prev_mask = mask.copy()
mask = cv2.threshold(img, th, 255, cv2.THRESH_BINARY)[1]
mask = cv2.floodFill(mask, None, seed_pt, fill_color)[1]
mask = cv2.bitwise_or(mask, prev_mask)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
n_centers = cv2.connectedComponents(mask)[0] - 1
print('There are %d cells in the image.'%n_centers)
Run Code Online (Sandbox Code Playgroud)
图像中有874个单元格.
归档时间: |
|
查看次数: |
2634 次 |
最近记录: |