web-dev-qa-db-ja.com

Pygameでのマスクと発射されたビームの重なり[AI車モデルビジョン]

Pygameで定義済みのトラックマスクを使用してビーム衝突検出を実装しようとしています。私の最終的な目標は、AI車のモデルに、乗っているトラックを見るビジョンを与えることです。

AI car model vision

これは私のビームをマスクしてオーバーラップを見つけようとする現在のコードです:

_import math
import sys

import pygame as pg

RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

pg.init()
beam_surface = pg.Surface((500, 500), pg.SRCALPHA)


def draw_beam(surface, angle, pos):
    # compute beam final point
    x_dest = 250 + 500 * math.cos(math.radians(angle))
    y_dest = 250 + 500 * math.sin(math.radians(angle))

    beam_surface.fill((0, 0, 0, 0))

    # draw a single beam to the beam surface based on computed final point
    pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))
    beam_mask = pg.mask.from_surface(beam_surface)

    # find overlap between "global mask" and current beam mask
    hit = mask.overlap(beam_mask, (pos[0] - 250, pos[1] - 250))
    if hit is not None:
        pg.draw.line(surface, BLUE, mouse_pos, hit)
        pg.draw.circle(surface, GREEN, hit, 3)


surface = pg.display.set_mode((500, 500))
mask_surface = pg.image.load("../assets/mask.png")
mask = pg.mask.from_surface(mask_surface)
clock = pg.time.Clock()

while True:
    for e in pg.event.get():
        if e.type == pg.QUIT:
            pg.quit()
            sys.exit()

    mouse_pos = pg.mouse.get_pos()

    surface.fill((0, 0, 0))
    surface.blit(mask_surface, mask_surface.get_rect())

    for angle in range(0, 120, 30):
        draw_beam(surface, angle, mouse_pos)

    pg.display.update()
    clock.tick(30)

_

コードスニペットで何が起こるかを説明しましょう。 1つずつ_beam_surface_にビームを描画し、そこからマスクを作成し、1つの長方形と円(gifの黒色)で定義された背景マスクとの重なりを見つけます。 「ヒットポイント」(両方のマスク間のオーバーラップポイント)がある場合は、ヒットポイントとマウスの位置を結ぶ線で描画します。

角度_<0,90>_の場合は正常に機能します

collision angles smaller than 90

しかし、範囲_<90,360>_の角度では機能しません

all collision angles

Pygameの overlap() ドキュメントはこれを伝えています:

左上隅から始めて、最初の行のビット0からW-1((0、0)から(W-1、0))をチェックし、次の行((0、1)から(W-1 、1))。この列ブロック全体がチェックされると、次の列ブロックに続きます(W to 2 * W-1)。

つまり、このアプローチは、ビームがほぼ左上隅からマスクに当たった場合にのみ機能します。それをすべての状況で機能させる方法について何かアドバイスはありますか?これは一般的にこの問題を解決するための良いアプローチですか?

6
skywall

光線軸のxおよびyコンポーネントが正の方向を指している場合、アプローチはうまく機能しますが、負の方向を指している場合は失敗します。ご指摘のとおり、これは pygame.mask.Mask.overlap の動作方法が原因です。

左上隅から始めて、最初の行のビット0からW-1((0、0)から(W-1、0))をチェックし、次の行((0、1)から(W-1 、1))。この列ブロック全体がチェックされると、次の列ブロックに続きます(W to 2 * W-1)。

アルゴリズムを機能させるには、光線が常に正の方向を指すようにする必要があります。したがって、レイが負のx方向を指している場合は、マスクとレイを垂直に反転し、レイが負のy方向を指している場合は、レイを水平に反転します。

pygame.transform.flip() topを使用して、4つのマスクを作成します。反転せず、水平反転、垂直反転、垂直および水平反転

mask = pg.mask.from_surface(mask_surface)
mask_fx = pg.mask.from_surface(pg.transform.flip(mask_surface, True, False))
mask_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, False, True))
mask_fx_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, True, True))
flipped_masks = [[mask, mask_fy], [mask_fx, mask_fx_fy]]

光線の方向を決定します。

c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))

光線の方向に応じてflippedマスクを取得します。

flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]

flippedターゲットポイントを計算します。

x_dest = 250 + 500 * abs(c)
y_dest = 250 + 500 * abs(s)

flippedオフセットを計算します。

offset_x = 250 - pos[0] if flip_x else pos[0] - 250
offset_y = 250 - pos[1] if flip_y else pos[1] - 250

flippedレイとマスクの最も近い交点を取得し、unflipthe交点:

hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))
if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]):
    hx = 500 - hit[0] if flip_x else hit[0]
    hy = 500 - hit[1] if flip_y else hit[1]
    hit_pos = (hx, hy)

    pg.draw.line(surface, BLUE, mouse_pos, hit_pos)
    pg.draw.circle(surface, GREEN, hit_pos, 3)

例を参照してください。

import math
import sys
import pygame as pg

RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

pg.init()
beam_surface = pg.Surface((500, 500), pg.SRCALPHA)


def draw_beam(surface, angle, pos):
    c = math.cos(math.radians(angle))
    s = math.sin(math.radians(angle))

    flip_x = c < 0
    flip_y = s < 0
    filpped_mask = flipped_masks[flip_x][flip_y]

    # compute beam final point
    x_dest = 250 + 500 * abs(c)
    y_dest = 250 + 500 * abs(s)

    beam_surface.fill((0, 0, 0, 0))

    # draw a single beam to the beam surface based on computed final point
    pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))
    beam_mask = pg.mask.from_surface(beam_surface)

    # find overlap between "global mask" and current beam mask
    offset_x = 250 - pos[0] if flip_x else pos[0] - 250
    offset_y = 250 - pos[1] if flip_y else pos[1] - 250
    hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))
    if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]):
        hx = 499 - hit[0] if flip_x else hit[0]
        hy = 499 - hit[1] if flip_y else hit[1]
        hit_pos = (hx, hy)

        pg.draw.line(surface, BLUE, pos, hit_pos)
        pg.draw.circle(surface, GREEN, hit_pos, 3)
        #pg.draw.circle(surface, (255, 255, 0), mouse_pos, 3)


surface = pg.display.set_mode((500, 500))
#mask_surface = pg.image.load("../assets/mask.png")
mask_surface = pg.Surface((500, 500), pg.SRCALPHA)
mask_surface.fill((255, 0, 0))
pg.draw.circle(mask_surface, (0, 0, 0, 0), (250, 250), 100)
pg.draw.rect(mask_surface, (0, 0, 0, 0), (170, 170, 160, 160))

mask = pg.mask.from_surface(mask_surface)
mask_fx = pg.mask.from_surface(pg.transform.flip(mask_surface, True, False))
mask_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, False, True))
mask_fx_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, True, True))
flipped_masks = [[mask, mask_fy], [mask_fx, mask_fx_fy]]

clock = pg.time.Clock()

while True:
    for e in pg.event.get():
        if e.type == pg.QUIT:
            pg.quit()
            sys.exit()

    mouse_pos = pg.mouse.get_pos()

    surface.fill((0, 0, 0))
    surface.blit(mask_surface, mask_surface.get_rect())

    for angle in range(0, 359, 30):
        draw_beam(surface, angle, mouse_pos)

    pg.display.update()
    clock.tick(30)

いいえ、アルゴリズムはさらに改善できます。光線は常にbeam_surfaceの右下の象限に描画されます。したがって、他の3つの象限は不要になり、beam_surfaceのサイズを250x250に縮小できます。光線の開始は(250、250)ではなく(0、0)にあり、オフセットの計算はわずかに調整する必要があります。

beam_surface = pg.Surface((250, 250), pg.SRCALPHA)

def draw_beam(surface, angle, pos):
    c = math.cos(math.radians(angle))
    s = math.sin(math.radians(angle))

    flip_x = c < 0
    flip_y = s < 0
    filpped_mask = flipped_masks[flip_x][flip_y]

    # compute beam final point
    x_dest = 500 * abs(c)
    y_dest = 500 * abs(s)

    beam_surface.fill((0, 0, 0, 0))

    # draw a single beam to the beam surface based on computed final point
    pg.draw.line(beam_surface, BLUE, (0, 0), (x_dest, y_dest))
    beam_mask = pg.mask.from_surface(beam_surface)

    # find overlap between "global mask" and current beam mask
    offset_x = 499-pos[0] if flip_x else pos[0]
    offset_y = 499-pos[1] if flip_y else pos[1]
    hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))
    if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]):
        hx = 499 - hit[0] if flip_x else hit[0]
        hy = 499 - hit[1] if flip_y else hit[1]
        hit_pos = (hx, hy)

        pg.draw.line(surface, BLUE, pos, hit_pos)
        pg.draw.circle(surface, GREEN, hit_pos, 3)
6
Rabbid76