首页 > 解决方案 > 从嘈杂的背景中区分相似的 RGB 像素?

问题描述

背景:我正在尝试从指南针的小图像中找到方向。定向航向表示如果红色(北)点从顶部逆时针旋转 90 度,则观察者面向东,180 度为南,270 度为西,0 为北。等等。我知道这么小的模糊图像存在局限性,但我希望尽可能准确。指南针覆盖在街景图像上,这意味着背景嘈杂且不可预测。

在此处输入图像描述

我想到的第一个策略是找到离中心最远的红色像素,并从中计算方向航向。数学很简单。

对我来说,困难的部分是将红色像素与其他像素区分开来。特别是因为几乎任何颜色都可以在背景中。

我的第一个想法是将完全透明的部分涂黑,以消除除白色透明环和指南针尖端之外的所有内容。

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 True Compass Values: 35.9901, 84.8366, 104.4101

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 These values are taken from the source code.

然后我使用这个解决方案来找到最接近用户给定颜色列表的 RGB 值。校准颜色列表后,我能够创建一个列表,找到一些指南针最内部的像素。这产生了 +/- 3 度内的正确结果。但是,当我尝试更改列表以包含红色指南针尖端的每个像素时,会有一些背景像素被注册为“红色”,因此会打乱计算。

我已经使用这个工具手动找到了尖端的末端,结果总是在 +/- 1 度(大多数情况下是 0.5)内结束,所以我希望这应该是可能的

指南针中红色的原始 RGB 值是 (184, 42, 42) 和 (204, 47, 48),但图像来自视频的屏幕截图,导致尖端/边缘像素模糊且呈黑色/灰色。

在此处输入图像描述

有没有比最接近的颜色()方法更好的方法?如果是这样,如果不是,我该如何校准可以使用的颜色列表?

标签: pythonpython-imaging-library

解决方案


如果您没有严格的时间限制(例如从视频中进行实时检测),并且愿意切换到 NumPy、OpenCV 和 scikit-image,则可以使用模板匹配。您可以从您提供的针的图像中获得相当不错的模板(和蒙版)。在某个循环中,您将以所需的分辨率迭代从 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
----------------------------------------

推荐阅读