python - NumPy fast iteration trough image
问题描述
I'm trying to get the extreme points in a binary image that has a rectangular shape (more or less). Binary image in which I want to find some rectangular bound around my rectangular (black) object
I would like to get 4 points like this (image) in order to use cv2.minAreaRect(points) on them and obtain a bounding box. The issue is that my current algorithm is extremely slow in finding those bounds because I have to iterate through this binary image 4 times (2D NumPy array).
def findCornerOne(frame):
#threshold that handles the case when you have some noise around your object (as in the photo
#above)
global thresholdVal
x = frame.shape[0]
y = frame.shape[1]
point = None
firstFound = True
count = 0
for i in range(0, y):
for j in range(0, x):
if frame[j][i] != 0:
if firstFound:
point = [i, j]
firstFound = False
count += 1
else:
if count <= thresholdVal:
firstFound = True
count = 0
else:
return point
return None
Is there any way speeding this up just with NumPy and standard python (without any libraries)? I was thinking about using numpy.where but I don't know how to specify on what axis I'm going to search first and find the bounding points.
In the end, I would like to obtain some points like the red ones in the image:image
解决方案
我想出的解决方案可以总结如下:
- 通过Otsu的方法将原始图像转换为灰度并二值化。
- 计算二值图像的连通分量。
- 选择最大的区域。
- 确定该区域的极端坐标。
import numpy as np
from skimage import io
from skimage.measure import label, regionprops
from skimage.filters import threshold_otsu
from skimage.color import rgb2gray
img = io.imread('https://i.stack.imgur.com/FIQjh.png')[:, :, :3]
gray = rgb2gray(img)
thresholded = gray > threshold_otsu(gray)
labels = label(thresholded, background=1)
props = measure.regionprops(labels)
largest = sorted(props, key=lambda x: x.area, reverse=True)[0]
top = np.where(largest.coords[:, 0] == largest.coords[:, 0].min())
bottom = np.where(largest.coords[:, 0] == largest.coords[:, 0].max())
left = np.where(largest.coords[:, 1] == largest.coords[:, 1].min())
right = np.where(largest.coords[:, 1] == largest.coords[:, 1].max())
extremes = np.concatenate([top[0], bottom[0], left[0], right[0]])
corners = largest.coords[extremes]
上面的代码不仅依赖于 NumPy,还依赖于 scikit-image,并且相当高效。请注意,这种方法返回的点数超过 4 个(您可以很容易地对坐标进行聚类以获得 4 个点)。
In [419]: corners
Out[419]:
array([[ 69, 417],
[ 69, 418],
[ 69, 419],
[ 69, 420],
[ 69, 421],
[256, 211],
[256, 212],
[256, 213],
[256, 214],
[101, 187],
[102, 187],
[103, 187],
[104, 187],
[227, 460],
[228, 460],
[229, 460],
[230, 460],
[231, 460],
[232, 460],
[233, 460],
[234, 460],
[235, 460]], dtype=int64)
如果要获取边界框,则不需要手动计算它们,因为返回的属性regionprops
有一个bbox
包含边界框坐标的键:
In [420]: largest.bbox
Out[420]: (69, 187, 257, 461)
演示
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
patches = [Circle((col, row), radius=25, color='green')
for row, col in corners]
fig, ax = plt.subplots(1)
ax.imshow(img)
for p in patches:
ax.add_patch(p)
plt.show(fig)
推荐阅读
- qt - Qt程序单应用模式字体问题
- javascript - 如何在fireEvent期间使用jest和测试库测试onload?
- python - 为 binarysearch.com 中的问题编写的代码超出时间限制
- c++ - 如何处理输出:trim_with_solid
- r - 无法为我的数据集中的每个运动员创建 facetplot (R)
- mysql - MYSQL:从表中获取每个客户的倒数第二条记录
- c - 用 CGO 链接两个具有相同函数名的 C 库?
- python - 通过对梯度小的地方进行平均来聚类数据
- python - 在 df2 列中以逗号分隔值合并 df1 列中的值的两个数据框
- javascript - 可拖动的 jQuery 元素