从嘈杂的背景中区分相似的 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
----------------------------------------