2

コンピューター ビジョンの本のいくつかの章を読み終えた後、それらの方法を適用して、ゲーム用の原始的なボットを作成することにしました。ダイナミクスがほとんどないFlingを選択し、ボールを見つけるだけで済みました。ボールには 5 つの異なる色があり、4 つの方向のいずれかに向けることができます (目の位置によって異なります)。各ブロックにボールが含まれているかどうかを確認できるように、フィールド内の各ブロックをトリミングしました。私の問題は、ボールを正しく見つけることができないことです。

ここに画像の説明を入力

私の最初の試みは次のとおりでした。各ボールの RGB カラーを合計し、[R, G, B] 配列を取得します。次に、フィールド内の各ブロックの RGB カラーを合計します。ブロックの配列にボールの配列と同様の [R, G, B] がある場合、このブロックにボールがあることをお勧めします。問題は、「類似性」の適切な値を見つけるのが難しいことです。異なる空のブロックでさえ、その合計は大幅に異なります。

次に、matchTemplate機能を持つopenCVモジュールを使用してみました。この関数は、画像を別のソース画像と照合し、 minMaxLoc関数とともに値maxLocを返します。. maxLoc が 1 に近い場合、画像はおそらくソース画像にあります。可能な限りのバリエーションのボール (全体で 20 個) を作成し、フィールド全体でパスしました。この機能はうまく機能しましたが、残念ながら、フィールド内のいくつかのボールを見逃したり、1 つのボールに 2 種類のボール (たとえば緑と黄色) を割り当てたりすることがあります。フィールド全体ではなくブロックごとにボールを照合することで、プロセスを改善しようとしました(この方法には、各ブロックをチェックし、フィールド内のボールの正しい数を検出する必要があるという利点があります。ボールの色. 同じ色のボールが 2 つある場合、matchTemplate は 2 番目のボールに関する情報を失います) . 驚くべきことに、まだ偽陰性/陽性があります。

おそらく、この問題を解決するもっと簡単な方法があります (私がまだ知らないライブラリかもしれません) が、今のところ見つけることができません。どんな提案も歓迎します。

4

2 に答える 2

7

ボールは色の点でかなり異なっているように見えます。最初に説明した問題は、画像に存在するより細かくランダムな詳細の一部に関連しているようです-特に背景とボールのさまざまなシェーディング/ポーズ.

これに基づいて、一連の前処理手順を適用して画像内の色の範囲を「折りたたむ」ことで、タスクを大幅に簡素化できると思います。

正確な色のセグメンテーション(より形式的には、達成したいこと) を達成するためのより原理的な方法はいくらでもありますが、より実用的な観点から、いくつかの簡単なハックを次に示します。

したがって、たとえば、最初に画像を滑らかにして、高周波成分を減らすことができます...

ここに画像の説明を入力

次に、正規化された RGB表現に変換します...

ここに画像の説明を入力

前に、最後に平均シフト フィルタリング ステップでポスタライズします...

ここに画像の説明を入力

OpenCV バインディングを使用して、これらすべてを順番に実行する Python のコードを次に示します。

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 のみを使用します。

まず、タスクを 2 つの単純なタスクに区別します。

  • 与えられたタイルにボールがあるかどうかを判断します。
  • ボールがあると確信しているタイルが与えられた場合、ボールの色を特定します。

2 番目のタスクは単純なはずなので、ここでは時間を割きません。基本的に、ボールの主な色が見えるピクセルをいくつかサンプリングし、見つけた色を既知のボールの色と比較します。

それでは、最初のタスクを見てみましょう。

まず、ボールがタイルの端まで伸びていないことに注意してください。したがって、タイルの端に沿ってピクセルをサンプリングすることで、そこにボールがあるかどうかに関係なく、タイルの背景のかなり代表的なサンプルを見つけることができます。

処理を進める簡単な方法は、タイル内のすべてのピクセルをタイル背景のこのサンプルと比較し、一般的に類似している (ボールがない) か、類似していない (ボールがない) かの何らかの尺度を取得することです。

以下は、これを行う 1 つの方法です。ここで使用される基本的なアプローチは、背景ピクセルの平均と標準偏差を、赤、緑、青のチャネルごとに個別に計算することです。次に、すべてのピクセルについて、すべてのチャネルの平均からの標準偏差の数を計算します。非類似度の尺度として、最も類似度の低いチャネルのこの値を使用します。

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 に答える