2

在完成计算机视觉书籍的几章后,我决定应用这些方法为游戏创建一些原始机器人。我选择了几乎没有动力的Fling,我需要做的就是找到球。球可能有 5 种不同的颜色,也可以指向 4 个方向中的任何一个(取决于眼睛的位置)。我裁剪了场地中的每个方块,这样我就可以检查每个方块是否包含一个球。我的问题是我无法正确找到球。

在此处输入图像描述

我的第一次尝试是跟随。我对每个球的 RGB 颜色求和并得到 [R, G, B] 数组。然后我对字段中每个块的 RGB 颜色求和。如果块的数组具有与球的数组相似的 [R, G, B] 我建议这个块有一个球。问题是很难为“相似性”找到好的价值。甚至不同的空块在这样的总和上也有很大的不同。

其次,我尝试使用具有matchTemplate功能的openCV模块。此函数将图像与另一个源图像匹配,并且与minMaxLoc函数一起返回值maxLoc. 如果 maxLoc 接近 1,则图像可能在源图像中。我做了所有可能的球变化(总共20个),并在整个场地上传球。此功能运行良好,但不幸的是,它有时会错过球场上的一些球,或者为一个球分配两种不同类型的球(比如绿色和黄色)。我试图通过匹配球而不是整个场地而是每个街区来改进这个过程(这种方法的优点是它检查每个街区并且应该检测场地中正确数量的球,当与整个场地匹配时,每个场地只给出一个位置球的颜色。如果有两个相同颜色的球匹配模板丢失关于第 2 个球的信息)。令人惊讶的是,它仍然有误报\正数。

可能有更简单的方法来解决这个问题(也许是一个我还不知道的库),但现在我找不到。欢迎任何建议。

4

2 回答 2

7

这些球在颜色方面看起来非常不同。您最初描述的问题似乎与图像中存在的一些更精细、随机的细节有关——尤其是在背景和球的不同阴影/姿势中。

在此基础上,我想说您可以通过应用一组预处理步骤来“折叠”图像中的颜色范围,从而显着简化任务。

有许多更原则性的方法可以实现准确的颜色分割(更正式地说,这是您想要实现的) - 但从更务实的角度来看,这里有一些快速'n'dirty hacks。

因此,例如,我们可以最初平滑图像以减少高频分量......

在此处输入图像描述

然后,转换为标准化的 RGB表示...

在此处输入图像描述

之前,最后用均值偏移过滤步骤对其进行后移......

在此处输入图像描述

这是 Python 中的代码,使用 OpenCV 绑定,按顺序执行所有这些操作:

import cv 

# get orginal image
orig = cv.LoadImage('fling.png') 

# show original 
cv.ShowImage("orig", orig)

# blur a bit to remove higher frequency variation
cv.Smooth(orig,orig,cv.CV_GAUSSIAN,5,5)

# normalise RGB
norm = cv.CreateImage(cv.GetSize(orig), 8, 3) 
red = cv.CreateImage(cv.GetSize(orig), 8, 1) 
grn = cv.CreateImage(cv.GetSize(orig), 8, 1) 
blu = cv.CreateImage(cv.GetSize(orig), 8, 1) 
total = cv.CreateImage(cv.GetSize(orig), 8, 1) 
cv.Split(orig,red,grn,blu,None)
cv.Add(red,grn,total)
cv.Add(blu,total,total)
cv.Div(red,total,red,255.0)
cv.Div(grn,total,grn,255.0)
cv.Div(blu,total,blu,255.0)
cv.Merge(red,grn,blu,None,norm)
cv.ShowImage("norm", norm)

# posterize simply with mean shift filtering
post = cv.CreateImage(cv.GetSize(orig), 8, 3) 
cv.PyrMeanShiftFiltering(norm,post,20,30)
cv.ShowImage("post", post)
于 2013-03-29T17:07:45.383 回答
3

你的任务在几个方面比你会发现的通用计算机视觉算法的设计要简单:你确切地知道要寻找什么,并且确切地知道在哪里寻找它。因此,我认为涉及外部库是不必要的复杂化,除非您已经熟悉它并且可以有效地将其用作解决您自己问题的工具。在这篇文章中,我将只使用 PIL。

首先,将任务分为两个更简单的任务:

  • 给定一个瓷砖,确定那里是否有球。
  • 给定一个我们非常确定有球的瓷砖,识别球的颜色。

第二个任务应该很简单,我不会在这里花时间。基本上,在球的主要颜色可见的一些像素中取样,并将您找到的颜色与已知的球颜色进行比较。

那么让我们看看第一个任务。

首先,请注意球不会延伸到瓷砖的边缘。因此,您可以通过沿图块边缘对像素进行采样,找到图块背景的相当有代表性的样本,无论那里是否有球。

一个简单的方法是将图块中的每个像素与该图块背景样本进行比较,并获得某种衡量它是否通常相似(无球)或不相似(球)的度量。

以下是执行此操作的一种方法。这里使用的基本方法是计算背景像素的平均值和标准差——分别计算红色、绿色和蓝色通道。对于每个像素,我们然后计算我们与每个通道的平均值的标准偏差数。我们将此值用于最不相似的渠道作为我们的不相似性度量。

import Image
import math

def fetch_pixels(col, row):
    img = Image.open( "image.png" )
    img = img.crop( (col*32,row*32,(col+1)*32,(row+1)*32) )
    return img.load()

def border_pixels( a ):
    rv = [ a[x,y] for x in range(32) for y in (0,31) ]
    rv.extend( [ a[x,y] for x in (0,31) for y in range(1,31) ] )
    return rv

def mean_and_stddev( xs ):
    mean = float(sum( xs )) / len(xs)
    dev = math.sqrt( float(sum( [ (x-mean)**2 for x in xs ] )) / len(xs) )
    return mean, dev

def calculate_deviations(cols = 7, rows = 8):
    outimg = Image.new( "L", (cols*32,rows*32) )
    pixels = outimg.load()
    for col in range(cols):
        for row in range(rows):
            rv = calculate_deviations_for( col, row, pixels )
            print rv
    outimg.save( "image_output.png" )

def calculate_deviations_for( col, row, opixels ):
    a = fetch_pixels( col, row )
    border = border_pixels( a )
    bru, brd = mean_and_stddev( map( lambda x : x[0], border ) )
    bgu, bgd = mean_and_stddev( map( lambda x : x[1], border ) )
    bbu, bbd = mean_and_stddev( map( lambda x : x[2], border ) )
    rv = []
    for y in range(32):
        for x in range(32):
            r, g, b = a[x,y]
            dr = (bru-r) / brd
            dg = (bgu-g) / bgd
            db = (bbu-b) / bbd
            t = max(abs(dr), abs(dg), abs(db))
            opixel = 0
            limit, span = 2.5, 8.0
            if t > limit:
                v = min(1.0, (t - limit) / span)
                print t,v
                opixel = 127 + int( 128 * v )
            opixels[col*32+x,row*32+y] = opixel
            rv.append( t )
    return (sum(rv) / float(len(rv)))

结果的可视化在这里:

临时球识别

请注意,大多数非球像素是纯黑色的。现在应该可以通过简单地计算黑色像素来确定是否存在球。(或者更可靠的是:计算最大的单个非黑色像素块的大小。)

现在,这是一种非常临时的方法,我当然不会声称它是最好的方法。“极限”值是通过实验确定的——本质上是通过反复试验确定的。它包含在这里是为了说明我认为您应该探索的那种方法,并为您提供一个调整的起点。(如果你想要一个开始试验的地方,你可以尝试让它为顶部的紫色球提供更好的结果。你能想到上面方法中的弱点可能会导致这样的结果吗?永远记住,但是,您不需要看起来完美的结果,只要一个足够好的结果即可。您想要的最终答案是“有球”或“没有球”,而您只想能够可靠地回答。)

注意:

  • 您需要确保在球完成滚动并且静止在其瓷砖中心时进行屏幕抓取。这极大地简化了问题。
  • 游戏的背景会影响问题——如果出现海洋主题或沙漠主题的关卡,您将需要测试并可能调整识别器以确保它仍然可靠地工作。
  • 覆盖比赛场地的特殊效果和/或 GUI 元素会使问题复杂化。(例如,考虑游戏是否有“云”或“烟雾”效果,有时会漂浮在比赛场地上。)如果不确定,您可能需要调整识别器以便能够返回“无结果” - 然后您可以稍后尝试另一个屏幕截图。您可能想要获取多个屏幕截图并对结果进行平均。
  • 我假设只有球和非球。如果更高级别有其他类型的对象,您将不得不进行更多实验以找出如何最好地识别这些对象。
  • 我没有使用“参考图片”的方法。但是,如果您的图像包含游戏中的所有对象,并且您可以将像素与图块精确对齐,那么这可能是最可靠的方法。不是将前景与采样背景进行比较,而是将前景与一组已知的前景图像进行比较。
于 2013-03-29T10:26:12.563 回答