Pygame中蒙版和发射光束的重叠[AI汽车模型视觉] [英] Overlap between mask and fired beams in Pygame [AI car model vision]

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

问题描述

我尝试在 Pygame 中使用预定义的轨道掩码来实现光束碰撞检测.我的最终目标是让 AI 汽车模型能够看到它正在行驶的轨道:

这是我当前的代码,我用光束来掩盖并尝试找到重叠:

导入数学导入系统将 pygame 导入为 pg红色 = (255, 0, 0)绿色 = (0, 255, 0)蓝色 = (0, 0, 255)pg.init()光束表面 = 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))光束表面.填充((0, 0, 0, 0))# 根据计算的最终点将单个梁绘制到梁表面pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))光束掩码 = pg.mask.from_surface(beam_surface)# 找到全局掩码"和当前光束掩码之间的重叠hit = mask.overlap(beam_mask, (pos[0] - 250, pos[1] - 250))如果命中不是无:pg.draw.line(表面,蓝色,鼠标位置,命中)pg.draw.circle(表面,绿色,命中,3)表面 = pg.display.set_mode((500, 500))mask_surface = pg.image.load("../assets/mask.png")掩码 = pg.mask.from_surface(mask_surface)时钟 = pg.time.Clock()而真:对于 pg.event.get() 中的 e:如果 e.type == pg.QUIT:pg.quit()系统退出()mouse_pos = pg.mouse.get_pos()表面填充((0, 0, 0))surface.blit(mask_surface, mask_surface.get_rect())对于范围内的角度 (0, 120, 30):draw_beam(表面,角度,鼠标位置)pg.display.update()时钟滴答(30)

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

工作正常对于角度<0,90>:

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

Pygame 的

导入数学导入系统将 pygame 导入为 pg红色 = (255, 0, 0)绿色 = (0, 255, 0)蓝色 = (0, 0, 255)pg.init()光束表面 = pg.Surface((500, 500), pg.SRCALPHA)def draw_beam(表面,角度,位置):c = math.cos(math.radians(angle))s = math.sin(math.radians(angle))翻转_x = c <0翻转_y = s <0filpped_mask = flipped_masks[flip_x][flip_y]# 计算光束终点x_dest = 250 + 500 * abs(c)y_dest = 250 + 500 * 绝对值光束表面.填充((0, 0, 0, 0))# 根据计算的最终点将单个梁绘制到梁表面pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))光束掩码 = pg.mask.from_surface(beam_surface)# 找到全局掩码"之间的重叠;和当前光束掩模offset_x = 250 - pos[0] if flip_x else pos[0] - 250offset_y = 250 - pos[1] if flip_y else pos[1] - 250命中 = filpped_mask.overlap(beam_mask, (offset_x, offset_y))如果 hit 不是 None 并且(hit[0] != pos[0] 或 hit[1] != pos[1]):hx = 499 - 命中[0] 如果flip_x 否则命中[0]hy = 499 - hit[1] if flip_y else hit[1]hit_pos = (hx, hy)pg.draw.line(表面,蓝色,位置,hit_pos)pg.draw.circle(表面,绿色,hit_pos,3)#pg.draw.circle(surface, (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()而真:对于 pg.event.get() 中的 e:如果 e.type == pg.QUIT:pg.quit()系统退出()mouse_pos = pg.mouse.get_pos()表面填充((0, 0, 0))surface.blit(mask_surface, mask_surface.get_rect())对于范围 (0, 359, 30) 中的角度:draw_beam(表面,角度,鼠标位置)pg.display.update()时钟滴答(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))翻转_x = c <0翻转_y = s <0filpped_mask = flipped_masks[flip_x][flip_y]# 计算光束终点x_dest = 500 * abs(c)y_dest = 500 * 绝对值光束表面.填充((0, 0, 0, 0))# 根据计算的最终点将单个梁绘制到梁表面pg.draw.line(beam_surface, BLUE, (0, 0), (x_dest, y_dest))光束掩码 = pg.mask.from_surface(beam_surface)# 找到全局掩码"之间的重叠;和当前光束掩模offset_x = 499-pos[0] if flip_x else pos[0]offset_y = 499-pos[1] if flip_y else pos[1]命中 = filpped_mask.overlap(beam_mask, (offset_x, offset_y))如果 hit 不是 None 并且(hit[0] != pos[0] 或 hit[1] != pos[1]):hx = 499 - 命中[0] 如果flip_x 否则命中[0]hy = 499 - hit[1] if flip_y else hit[1]hit_pos = (hx, hy)pg.draw.line(表面,蓝色,位置,hit_pos)pg.draw.circle(表面,绿色,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天全站免登陆