最近我一直在研究计算机视觉和神经网络。
并且发现了一个实验性的物体检测方法,可以在3D应用程序中使用。
但是,令我惊讶的是 - 我面临着将一个坐标系转换为另一个坐标系的问题 (据我所知是笛卡尔坐标系到极坐标/球坐标系)。
让我解释一下。
例如,我们有一个3D应用程序窗口的截图 (某个3D游戏):
现在,使用Open-CV或神经网络,我能够检测到圆形球体 (游戏目标)。
以及它们在游戏窗口中的X,Y
坐标 (x,y偏移量)。
如果我想要在给定的 X, Y
坐标内编程移动鼠标光标以瞄准其中一个目标,那么它只能在桌面环境下(移动桌面中的光标)工作。
但是当我切换到 3D 游戏并且我的鼠标光标现在位于 3D 游戏世界环境中时,它不起作用,也无法瞄准目标。
因此,我对这个问题进行了适当的研究。我发现,鼠标光标被锁定在 3D 游戏中。因此,我们不能使用 mouse_event
win32 调用中的 MOUSEEVENTF_MOVE (0x0001) + MOUSEEVENTF_ABSOLUTE (0x8000)
标志来移动光标。
我们只能使用相对移动来编程移动鼠标。理论上,为了获得这种相对鼠标移动偏移量,我们可以计算检测结果与 3D 游戏窗口中心的偏移量。在这种情况下,如果目标点距离屏幕中心左侧 100px
,则相对移动向量将是 (x=-100, y=0)
。
之后,我对这个主题进行了更多的研究。据我所知,在3D游戏中,准星使用3D空间中的角度移动。具体来说,只有两个角度:水平移动角度和垂直移动角度。
因此,游戏引擎将我们的鼠标移动转换为给定3D世界空间内的移动角度。这就是在3D游戏中完成准星移动的方法。但我们无法访问它,我们只能通过外部调用win32来移动鼠标。
我决定以某种方式计算每度像素数(win32
相对鼠标移动需要使用的像素数量,以在游戏内将准星移动 1 度)。
为了做到这一点,我写下了一个简单的计算算法。 它是这样的:
正如您所看到的,我们需要通过 win32
相对地水平移动鼠标 16400
像素,才能使游戏内的准星移动360度。
确实如此。 分别通过 16400/2
来移动准星 180 度。
接下来,我尝试将屏幕上的目标偏移坐标 X,Y
转换为百分比(从屏幕中央开始)。 然后再将它们转换为度数。
总体公式看起来是这样的:
(仅列出水平移动的示例):
w = 100 # screen width
x_offset = 10 # target x offset
hor_fov = 106.26
degs = (hor_fov/2) * (x_offset /w) # 5.313 degrees
实际上,它起作用了!
但并不完全如预期。
总体瞄准精度不同,取决于目标距离屏幕中央的距离。
我不太擅长三角学,但我可以说-极坐标/球面坐标肯定有关系。
因为我们只能水平和垂直地看到游戏世界的一部分。
这也被称为 FOV (视野)
。
因此,在给定的 3D 游戏中,我们仅能水平查看 106.26
度。
而纵向则是 73.74
度。
我的猜测是,我试图将坐标从线性系统转换为非线性系统。
结果导致整体精度不足。
我还尝试在 Python 中使用 math.atan
。
它可以工作,但精度仍然不够高。
下面是代码:
def point_get_difference(source_point, dest_point):
# 1000, 1000
# source_point = (960, 540)
# dest_point = (833, 645)
# result = (100, 100)
x = dest_point[0]-source_point[0]
y = dest_point[1]-source_point[1]
return x, y
def get_move_angle__new(aim_target, gwr, pixels_per_degree, fov):
game_window_rect__center = (gwr[2]/2, gwr[3]/2)
rel_diff = list(point_get_difference(game_window_rect__center, aim_target))
x_degs = degrees(atan(rel_diff[0]/game_window_rect__center[0])) * ((fov[0]/2)/45)
y_degs = degrees(atan(rel_diff[1] / game_window_rect__center[0])) * ((fov[1]/2)/45)
rel_diff[0] = pixels_per_degree * x_degs
rel_diff[1] = pixels_per_degree * y_degs
return rel_diff, (x_degs+y_degs)
get_move_angle__new((900, 540), (0, 0, 1920, 1080), 16364/360, (106.26, 73.74))
# Output will be: ([-191.93420990140876, 0.0], -4.222458785413539)
# But it's not accurate, overall x_degs must be more or less than -4.22...
有没有一种精确地将2D屏幕X,Y
坐标转换为3D游戏十字准心移动度数的方法?
一定有办法,我就是想不出来...
znear
值(或焦距)以及相机的FOVx,FOVy
角度来调整您的角度计算。如果它是2.5D射线投射,例如Wolfenstein,则通常需要考虑在x轴上使用投影,通常使用cos(x_angle)
或其反转而不是直接使用x
坐标。 - SpektreFOVx = 2.0*atan(0.5*screen_x_resolution/znear); angle_x = atan(screen_x_from_center/znear);
所以你可能需要测量/拟合一些你不知道的东西来获取 znear、FOVx ... 另外screen_x
可能需要缩放(通过屏幕分辨率和有时甚至是缩放比例)。如果你钩入游戏进程,你甚至可以从那里获取视频和深度缓冲区,从而可以获得目标的原始 3D 位置。 - SpektrePHI/Theta
)。 - Abraham Tugalov