【问题标题】:Distinguish similar RGB pixels from noisey background?从嘈杂的背景中区分相似的 RGB 像素?
【发布时间】:2021-12-31 04:50:04
【问题描述】:

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

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

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

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

True Compass Values: 35.9901, 84.8366, 104.4101

These values are taken from the source code.

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

我已经使用 this tool 手动找到了提示的结尾,结果总是在 +/- 1 度(大多数情况下为 0.5)内结束,所以我希望这应该是可能的

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

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

【问题讨论】:

  • 图像中的指南针是否总是相同的大小......我的意思是它总是图像总宽度的 1/16 还是这样的?
  • 有趣。您能否提供这三张图像的未涂黑版本,用于测试目的?此外,除非我遗漏了什么,否则您的文字将角度描述为从北顺时针方向增加,但您标记的示例是逆时针方向。

标签: python python-imaging-library


【解决方案1】:

如果您没有严格的时间限制(例如从视频中进行实时检测),并且愿意切换到 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
----------------------------------------

【讨论】:

  • 干得好 - 如果@Wellwoah 感觉很敏锐,它可能可以很好地跨多个进程并行化 - 要么将连续图像发送到不同进程,要么在 4 个进程中每个进程执行 90 度。跨度>
  • 我想知道使用这两种方法是否会更快。根据事情需要多长时间,使用提问者的原始方法可能是有意义的,然后将此过程应用于结果的 +/- 几度范围内的每个角度,以获得更好的准确性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-03
  • 2013-04-18
  • 2021-06-29
  • 2017-09-04
  • 1970-01-01
相关资源
最近更新 更多