从嘈杂的背景中区分相似的 RGB 像素?
Distinguish similar RGB pixels from noisey background?
上下文:我正在尝试从罗盘的小图像中找到方向。定向航向意味着如果红色(北)点从顶部逆时针旋转 90 度,观察者面向东方,180 度为南,270 度为西,0 度为北。等。我知道这么小的模糊图像存在局限性,但我希望尽可能准确。指南针覆盖在街景图像上,这意味着背景嘈杂且不可预测。
我想到的第一个策略是找到离中心最远的红色像素,并以此计算方向航向。数学很简单。
对我来说最困难的部分是将红色像素与其他所有像素区分开来。特别是因为几乎任何颜色都可以在背景中。
我的第一个想法是将完全透明的部分涂黑,以消除除了白色透明环和指南针尖端以外的所有部分。
True Compass Values: 35.9901, 84.8366, 104.4101
These values are taken from the source code.
然后我使用 找到最接近用户给定颜色列表的 RGB 值。校准颜色列表后,我能够创建一个列表,找到罗盘最内层的一些像素。这在 +/- 3 度内产生了正确的结果。但是,当我尝试更改列表以包含红色罗盘尖端的每个像素时,会有背景像素被注册为“红色”,因此会搞乱计算。
我使用 this tool 手动找到了尖端的末端,结果总是在 +/- 1 度(大多数情况下为 .5)以内结束,所以我希望这应该是可能的
罗盘中红色的原始 RGB 值为 (184, 42, 42) 和 (204, 47, 48) 但图像来自视频截图,导致 tip/edge 像素变得模糊并且 blackish/greyish.
有没有比 closest_color() 方法更好的方法?如果是,怎么办?如果不是,我该如何校准可用的颜色列表?
如果您没有严格的时间限制(例如视频的实时检测),并且愿意切换到 NumPy、OpenCV 和 scikit-image,您可以使用 template matching。您可以从您提供的针头图像中得出一个很好的模板(和面具)。在某些循环中,您将以所需的分辨率迭代从 0° 到 360° 的角度 - 越精细,整个过程所需的时间越长 - 并执行模板匹配。对于每个角度,您保存最佳匹配值,最后搜索所有角度的最佳分数。
那是我的代码:
import cv2
import numpy as np
from skimage.transform import rotate
# Set up template (and mask) for template matching
templ = cv2.resize(cv2.imread('templ_compass.png')[2:-2, :], (23, 69))
templ = cv2.cvtColor(templ, cv2.COLOR_BGR2BGRA)
templ[..., 3] = cv2.cvtColor(
cv2.addWeighted(templ[..., :3], 0.5,
cv2.flip(templ[..., :3], 0), 0.5, 0), cv2.COLOR_BGR2GRAY)
templ[..., 3] = cv2.threshold(templ[..., 3], 254, 255, cv2.THRESH_BINARY_INV)[1]
# Collect image file names
images = ['compass_36.png', 'compass_85.png', 'compass_104.png']
# Initialize angles and minimum values
angles = np.arange(0, 360, 1)
min_vals = np.zeros_like(angles)
# Iterate image file names
for image in images:
# Read image
img = cv2.imread(image).astype(np.float32) / 255
# Iterate angles
for i_a, angle in enumerate(angles):
# Rotate template and mask
templ_rot = rotate(templ.copy(), angle, resize=True).astype(np.float32)
# Actual template matching
result = cv2.matchTemplate(img, templ_rot[..., :3], cv2.TM_SQDIFF,
mask=templ_rot[..., 3])
# Save minimum value
min_vals[i_a] = cv2.minMaxLoc(result)[0]
# Find best match angle
best_match_idx = np.argmin(min_vals)
print('{}: {}'.format(image, angles[best_match_idx]))
结果如下:
compass_36.png: 37
compass_85.png: 85
compass_104.png: 104
如果将角度分辨率切换为 angles = np.arange(0, 360, 0.5)
,您将得到:
compass_36.png: 36.5
compass_85.png: 85.0
compass_104.png: 104.5
设置模板涉及一些手动工作,例如正确裁剪针头,获得合适的尺寸,并获得好的面罩。
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.19041-SP0
Python: 3.9.1
PyCharm: 2021.1.1
NumPy: 1.20.3
OpenCV: 4.5.2
scikit-image: 0.18.1
----------------------------------------
上下文:我正在尝试从罗盘的小图像中找到方向。定向航向意味着如果红色(北)点从顶部逆时针旋转 90 度,观察者面向东方,180 度为南,270 度为西,0 度为北。等。我知道这么小的模糊图像存在局限性,但我希望尽可能准确。指南针覆盖在街景图像上,这意味着背景嘈杂且不可预测。
我想到的第一个策略是找到离中心最远的红色像素,并以此计算方向航向。数学很简单。
对我来说最困难的部分是将红色像素与其他所有像素区分开来。特别是因为几乎任何颜色都可以在背景中。
我的第一个想法是将完全透明的部分涂黑,以消除除了白色透明环和指南针尖端以外的所有部分。
True Compass Values: 35.9901, 84.8366, 104.4101
These values are taken from the source code.
然后我使用
我使用 this tool 手动找到了尖端的末端,结果总是在 +/- 1 度(大多数情况下为 .5)以内结束,所以我希望这应该是可能的
罗盘中红色的原始 RGB 值为 (184, 42, 42) 和 (204, 47, 48) 但图像来自视频截图,导致 tip/edge 像素变得模糊并且 blackish/greyish.
有没有比 closest_color() 方法更好的方法?如果是,怎么办?如果不是,我该如何校准可用的颜色列表?
如果您没有严格的时间限制(例如视频的实时检测),并且愿意切换到 NumPy、OpenCV 和 scikit-image,您可以使用 template matching。您可以从您提供的针头图像中得出一个很好的模板(和面具)。在某些循环中,您将以所需的分辨率迭代从 0° 到 360° 的角度 - 越精细,整个过程所需的时间越长 - 并执行模板匹配。对于每个角度,您保存最佳匹配值,最后搜索所有角度的最佳分数。
那是我的代码:
import cv2
import numpy as np
from skimage.transform import rotate
# Set up template (and mask) for template matching
templ = cv2.resize(cv2.imread('templ_compass.png')[2:-2, :], (23, 69))
templ = cv2.cvtColor(templ, cv2.COLOR_BGR2BGRA)
templ[..., 3] = cv2.cvtColor(
cv2.addWeighted(templ[..., :3], 0.5,
cv2.flip(templ[..., :3], 0), 0.5, 0), cv2.COLOR_BGR2GRAY)
templ[..., 3] = cv2.threshold(templ[..., 3], 254, 255, cv2.THRESH_BINARY_INV)[1]
# Collect image file names
images = ['compass_36.png', 'compass_85.png', 'compass_104.png']
# Initialize angles and minimum values
angles = np.arange(0, 360, 1)
min_vals = np.zeros_like(angles)
# Iterate image file names
for image in images:
# Read image
img = cv2.imread(image).astype(np.float32) / 255
# Iterate angles
for i_a, angle in enumerate(angles):
# Rotate template and mask
templ_rot = rotate(templ.copy(), angle, resize=True).astype(np.float32)
# Actual template matching
result = cv2.matchTemplate(img, templ_rot[..., :3], cv2.TM_SQDIFF,
mask=templ_rot[..., 3])
# Save minimum value
min_vals[i_a] = cv2.minMaxLoc(result)[0]
# Find best match angle
best_match_idx = np.argmin(min_vals)
print('{}: {}'.format(image, angles[best_match_idx]))
结果如下:
compass_36.png: 37
compass_85.png: 85
compass_104.png: 104
如果将角度分辨率切换为 angles = np.arange(0, 360, 0.5)
,您将得到:
compass_36.png: 36.5
compass_85.png: 85.0
compass_104.png: 104.5
设置模板涉及一些手动工作,例如正确裁剪针头,获得合适的尺寸,并获得好的面罩。
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.19041-SP0
Python: 3.9.1
PyCharm: 2021.1.1
NumPy: 1.20.3
OpenCV: 4.5.2
scikit-image: 0.18.1
----------------------------------------