Pygame中面具与发射光束之间的重叠[AI汽车模型视觉] [英] Overlap between mask and fired beams in Pygame [AI car model vision]

查看:81
本文介绍了Pygame中面具与发射光束之间的重叠[AI汽车模型视觉]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在Pygame中使用预定义的跟踪蒙版来实现光束碰撞检测.我的最终目标是给出一个AI汽车模型的视野,以查看其行驶的轨迹:

这是我当前的代码,我在其中发射光束以掩蔽并尝试查找重叠:

 导入数学导入系统将pggame导入为pg红色=(255,0,0)绿色=(0,255,0)蓝色=(0,0,255)pg.init()beam_surface = pg.Surface((500,500),pg.SRCALPHA)def draw_beam(表面,角度,位置):#计算光束终点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))#根据计算出的终点将单个光束绘制到光束表面pg.draw.line(beam_surface,BLUE,(250,250),(x_dest,y_dest))beam_mask = pg.mask.from_surface(beam_surface)#查找全局蒙版"和当前光束蒙版之间的重叠命中= mask.overlap(beam_mask,(pos [0]-250,pos [1]-250))如果hit不是None:pg.draw.line(surface,BLUE,mouse_pos,hit)pg.draw.circle(surface,GREEN,hit,3)表面= pg.display.set_mode((500,500))mask_surface = pg.image.load("../assets/mask.png")遮罩= pg.mask.from_surface(mask_surface)时钟= pg.time.Clock()而True:对于pg.event.get()中的e:如果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())对于范围在(0,120,30)中的角度:draw_beam(表面,角度,mouse_pos)pg.display.update()clock.tick(30) 

让我们描述一下代码片段中发生的事情.我一个接一个地将光束绘制到 beam_surface 上,从中进行遮罩,然后发现与由一个矩形和一个圆形(gif中为黑色)定义的背景遮罩重叠.如果存在击中点"(两个蒙版之间的重叠点),我会用一条连接击中点和鼠标位置的线来绘制它.

对于角度< 0,90> :

但是对于< 90,360> 范围内的角度,不起作用:

Pygame的

 导入数学导入系统将pggame导入为pg红色=(255,0,0)绿色=(0,255,0)蓝色=(0,0,255)pg.init()beam_surface = pg.Surface((500,500),pg.SRCALPHA)def draw_beam(表面,角度,位置):c = math.cos(math.radians(angle))s = math.sin(math.radians(angle))flip_x = c<0flip_y = s<0filpped_mask = flipped_masks [flip_x] [flip_y]#计算光束终点x_dest = 250 + 500 * abs(c)y_dest = 250 + 500 *绝对值beam_surface.fill((0,0,0,0))#根据计算出的终点将单个光束绘制到光束表面pg.draw.line(beam_surface,BLUE,(250,250),(x_dest,y_dest))beam_mask = pg.mask.from_surface(beam_surface)#找到全局掩码"之间的重叠.和当前的光束掩模offset_x = 250-pos [0],如果flip_x否则pos [0]-250offset_y = 250-pos [1],如果flip_y else pos [1]-250hit = filpped_mask.overlap(beam_mask,(offset_x,offset_y))如果hit不是None并且(hit [0]!= pos [0]或hit [1]!= pos [1]):hx = 499-如果flip_x则命中[0],否则命中[0]hy = 499-如果flip_y,则命中[1];否则,命中[1]hit_pos =(hx,hy)pg.draw.line(surface,BLUE,pos,hit_pos)pg.draw.circle(surface,GREEN,hit_pos,3)#pg.draw.circle(曲面,(255,255,0),mouse_pos,3)表面= 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))遮罩= 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]]时钟= pg.time.Clock()而True:对于pg.event.get()中的e:如果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())对于范围在(0,359,30)中的角度:draw_beam(表面,角度,mouse_pos)pg.display.update()clock.tick(30) 


不是,该算法可以进一步改进.射线总是绘制在 beam_surface 的右下象限上.因此,不再需要其他3个象限,并且 beam_surface 的大小可以减小到250x250.射线的起点是(0,0)而不是(250,250),偏移的计算必须稍作调整:

  beam_surface = pg.Surface((250,250),pg.SRCALPHA)def draw_beam(表面,角度,位置):c = math.cos(math.radians(angle))s = math.sin(math.radians(angle))flip_x = c<0flip_y = s<0filpped_mask = flipped_masks [flip_x] [flip_y]#计算光束终点x_dest = 500 * abs(c)y_dest = 500 *绝对值beam_surface.fill((0,0,0,0))#根据计算出的终点将单个光束绘制到光束表面pg.draw.line(beam_surface,BLUE,(0,0),(x_dest,y_dest))beam_mask = pg.mask.from_surface(beam_surface)#找到全局掩码"之间的重叠.和当前的光束掩模如果flip_x否则pos [0],则offset_x = 499-pos [0]如果flip_y else pos [1],则offset_y = 499-pos [1]hit = filpped_mask.overlap(beam_mask,(offset_x,offset_y))如果hit不是None并且(hit [0]!= pos [0]或hit [1]!= pos [1]):hx = 499-如果flip_x则命中[0],否则命中[0]hy = 499-如果flip_y,则命中[1];否则,命中[1]hit_pos =(hx,hy)pg.draw.line(surface,BLUE,pos,hit_pos)pg.draw.circle(surface,GREEN,hit_pos,3) 

I try to implement beam collision detection with a predefined track mask in Pygame. My final goal is to give an AI car model vision to see a track it's riding on:

This is my current code where I fire beams to mask and try to find an overlap:

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)

Let's describe what happens in the code snippet. One by one, I draw beams to beam_surface, make masks from them, and find overlap with background mask defined by one rectangle and a circle (black color in gifs). If there is a "hit point" (overlap point between both masks), I draw it with a line connecting hit point and mouse position.

It works fine for angles <0,90>:

But it's not working for angles in range <90,360>:

Pygame's overlap() documentation tells this:

Starting at the top left corner it checks bits 0 to W - 1 of the first row ((0, 0) to (W - 1, 0)) then continues to the next row ((0, 1) to (W - 1, 1)). Once this entire column block is checked, it continues to the next one (W to 2 * W - 1).

This means that this approach will work only if the beam hits the mask approximately from the top left corner. Do you have any advice on how to make it work for all of the situations? Is this generally a good approach to solve this problem?

解决方案

Your approach works fine, if the x and y component of the ray axis points in the positive direction, but it fails if it points in the negative direction. As you pointed out, that is caused by the way pygame.mask.Mask.overlap works:

Starting at the top left corner it checks bits 0 to W - 1 of the first row ((0, 0) to (W - 1, 0)) then continues to the next row ((0, 1) to (W - 1, 1)). Once this entire column block is checked, it continues to the next one (W to 2 * W - 1).

To make the algorithm work, you have to ensure that the rays point always in the positive direction. Hence if the ray points in the negative x direction, then flip the mask and the ray vertical and if the ray points in the negative y direction than flip the ray horizontal.

Use pygame.transform.flip() top create 4 masks. Not flipped, flipped horizontal, flipped vertical and flipped vertical and horizontal:

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]]

Determine if the direction of the ray:

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

Get the flipped mask dependent on the direction of the ray:

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

Compute the flipped target point:

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

Compute the flipped offset:

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

Get the nearest intersection point of the flipped ray and mask and unflip the intersection point:

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)

See the example:

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)


Not,the algorithm can be further improved. The ray is always drawn on the bottom right quadrant of the beam_surface. Hence the other 3 quadrants are no longer needed and the size of beam_surface can be reduced to 250x250. The start of the ray is at (0, 0) rather than (250, 250) and the computation of the offsets hast to be slightly adapted:

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)

这篇关于Pygame中面具与发射光束之间的重叠[AI汽车模型视觉]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆