简体   繁体   English

如何将屏幕 x,y(笛卡尔坐标)转换为 3D 世界空间十字准线移动角度(screenToWorld)?

[英]How to convert screen x,y (cartesian coordinates) to 3D world space crosshair movement angles (screenToWorld)?

Recently I've been playing around with computer vision and neural networks.最近我一直在研究计算机视觉和神经网络。
And came across experimental object detection within a 3D application.并在 3D 应用程序中遇到了实验对象检测。
But, surprisingly to me - I've faced an issue of converting one coordinates system to another (AFAIK cartesian to polar/sphere) .但是,令我惊讶的是 - 我遇到了将一个坐标系转换为另一个坐标系的问题(AFAIK 笛卡尔坐标系到极坐标/球体)

Let me explain.让我解释。
For example, we have a screenshot of a 3D application window (some 3D game) :例如,我们有一个 3D 应用程序窗口的屏幕截图(一些 3D 游戏) 在此处输入图像描述

Now, using Open-CV or neural network I'm able to detect the round spheres (in-game targets) .现在,使用 Open-CV 或神经网络,我能够检测到圆形球体(游戏中的目标)
As well as their X, Y coordinates within the game window (x, y offsets) .以及它们在游戏窗口内的X, Y坐标(x、y 偏移量)
在此处输入图像描述

And if I will programmatically move a mouse cursor within the given X, Y coordinates in order to aim one of the targets.如果我将以编程方式在给定的X, Y坐标内移动鼠标光标以瞄准其中一个目标。
It will work only when I'm in desktop environment (moving the cursor in desktop) .只有当我在桌面环境中(在桌面上移动光标)时它才会起作用。
But when I switch to the 3D game and thus, my mouse cursor is now within 3D game world environment - it does not work and does not aim the target.但是,当我切换到 3D 游戏时,我的鼠标光标现在位于 3D 游戏世界环境中 - 它不起作用并且不瞄准目标。

So, I did a decent research on the topic.因此,我对该主题进行了不错的研究。
And what I came across, is that the mouse cursor is locked inside 3D game.我遇到的是鼠标光标在 3D 游戏中被锁定。
Because of this, we cannot move the cursor using MOUSEEVENTF_MOVE (0x0001) + MOUSEEVENTF_ABSOLUTE (0x8000) flags within the mouse_event win32 call.因此,我们无法在mouse_event win32 调用中使用MOUSEEVENTF_MOVE (0x0001) + MOUSEEVENTF_ABSOLUTE (0x8000)标志移动光标。

We are only able to move the mouse programmatically using relative movement.我们只能使用相对移动以编程方式移动鼠标。
And, theoretically, in order to get this relative mouse movement offsets, we can calculate the offset of detections from the middle of the 3D game window.而且,从理论上讲,为了获得此相对鼠标移动偏移量,我们可以计算检测到 3D 游戏窗口中间的偏移量。
In such case, relative movement vector would be something like (x=-100, y=0) if the target point is 100px left from the middle of the screen.在这种情况下,如果目标点在屏幕100px左侧 100 像素处,则相对运动矢量将类似于(x=-100, y=0)

The thing is, that the crosshair inside a 3D game will not move 100px to the left as expected.问题是,3D 游戏中的十字准线不会像预期的那样向左移动100px
And will not aim the given target.并且不会瞄准给定的目标。
But it will move a bit in a given direction.但它会朝着给定的方向移动一点。

After that, I've made more research on the topic.之后,我对该主题进行了更多研究。
And as I understand, the crosshair inside a 3D game is moving using angles in 3D space.据我了解,3D 游戏中的十字准线是使用 3D 空间中的角度移动的。
Specifically, there are only two of them: horizontal movement angles and vertical movement angles .具体来说,只有两个: horizontal movement anglesvertical movement angles

So the game engine takes our mouse movement and converts it to the movement angles within a given 3D world space.因此,游戏引擎获取我们的鼠标移动并将其转换为给定 3D 世界空间内的移动角度。
And that's how the crosshair movement is done inside a 3D game.这就是 3D 游戏中十字准线移动的方式。
But we don't have access to that, all we can is move the mouse with win32 calls externally.但是我们无权访问它,我们只能通过外部win32调用移动鼠标。

Then I've decided to somehow calculate pixels per degree (amount of pixels we need to use with win32 relative mouse movement in order to move the crosshair by 1 degrees inside the game) .然后我决定以某种方式计算pixels per degree像素(我们需要使用win32相对鼠标移动的像素数量,以便在游戏中将十字准线移动 1 度)
In order to do this, I've wrote down a simple calculation algorithm.为了做到这一点,我写下了一个简单的计算算法。
Here it is:这里是: 在此处输入图像描述

As you can see, we need to move our mouse relatively with win32 by 16400 pixels horizontally, in order to move the crosshair inside our game by 360 degrees.如您所见,我们需要将鼠标相对于win32水平移动16400像素,以便将游戏中的十字准线移动 360 度。
And indeed, it works.事实上,它有效。
16400/2 will move the crosshair by 180 degrees respectively. 16400/2将十字准线分别移动 180 度。

What I did next, is I tried to convert our screen X, Y target offset coordinates to percentages (from the middle of the screen).我接下来要做的是尝试将我们的屏幕X, Y目标偏移坐标转换为百分比(从屏幕中间开始)。
And then convert them to degrees.然后将它们转换为度数。

The overall formula looked like (example for horizontal movement only) :整体公式看起来像(仅水平移动的示例)

w = 100  # screen width
x_offset = 10  # target x offset 
hor_fov = 106.26

degs = (hor_fov/2) * (x_offset /w)  # 5.313 degrees

And indeed, it worked!事实上,它奏效了!
But not quite as expected.但并不完全像预期的那样。
The overall aiming precision was different, depending on how far the target is from the middle of the screen.总体瞄准精度不同,具体取决于目标距离屏幕中间的距离。

I'm not that great with trigonometry, but as I can say - there's something to do with polar/sphere coordinates.我不太擅长三角学,但正如我可以说的那样——这与极坐标/球坐标有关。
Because we can see only some part of the game world both horizontally & vertically.因为我们只能在水平和垂直方向上看到游戏世界的一部分。
It's also called the FOV (Field of view) .它也被称为FOV (Field of view)

Because of this, in the given 3D game we are only able to view 106.26 degrees horizontally.因此,在给定的 3D 游戏中,我们只能水平查看106.26度。
And 73.74 degrees vertically.垂直73.74度。

My guess, is that I'm trying to convert coordinates from linear system to something non-linear.我的猜测是,我正在尝试将坐标从线性系统转换为非线性系统。
As a result, the overall accuracy is not good enough.结果,整体精度不够好。

I've also tried to use math.atan in Python.我也试过在 Python 中使用math.atan
And it works, but still - not accurate.它有效,但仍然 - 不准确。

Here is the code:这是代码:

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

Is there a way to precisely convert 2D screen X, Y coordinates into 3D game crosshair movement degrees?有没有办法将2D画面的X, Y坐标精确转换成3D游戏的十字准线移动度数?
There must be a way, I just can't figure it out...一定有办法,我只是想不通...

The half-way point between the center and the edge of the screen is not equal to the field of view divided by four.屏幕中心和边缘之间的中点不等于视野除以四。 As you noticed, the relationship is nonlinear.正如您所注意到的,这种关系是非线性的。

The angle between a fractional position on the screen (0-1) and the middle of the screen can be calculated as follows.屏幕上的分数位置 (0-1) 与屏幕中间之间的角度可以计算如下。 This is for the horizontal rotation (ie, around the vertical axis), so we're only considering the X position on the screen.这是针对水平旋转(即围绕垂直轴),所以我们只考虑屏幕上的 X 位置。

# angle is the angle in radians that the camera needs to
# rotate to aim at the point

# px is the point x position on the screen, normalised by
# the resolution (so 0.0 for the left-most pixel, 0.5 for
# the centre and 1.0 for the right-most

# FOV is the field of view in the x dimension in radians
angle = math.atan((x-0.5)*2*math.tan(FOV/2))

For a field of view of 100 degrees and an x of zero, that gives us -50 degrees of rotation (exactly half the field of view).对于 100 度的视野和零 x,这给了我们 -50 度的旋转(正好是视野的一半)。 For an x of 0.25 (half-way between the edge and middle), we get a rotation of around -31 degrees.对于 0.25 的 x(边缘和中间之间的中间位置),我们得到大约 -31 度的旋转。

Note that the 2*math.tan(FOV/2) part is constant for any given field of view, so you can calculate it in advance and store it.请注意,对于任何给定的视野, 2*math.tan(FOV/2)部分都是常数,因此您可以提前计算并存储它。 Then it just becomes (assuming we named it z ):然后它就变成了(假设我们将其命名为z ):

angle = math.atan((x-0.5)*z)

Just do that for both x and y and it should work.只需对 x 和 y 执行此操作,它就可以工作。

Edit / update:编辑/更新:

Here is a complete function.这是一个完整的功能。 I've tested it, and it seems to work.我已经测试过了,它似乎有效。

import math

def get_angles(aim_target, window_size, fov):
"""
    Get (x, y) angles from center of image to aim_target.

    Args:
        aim_target: pair of numbers (x, y) where to aim
        window_size: size of area (x, y)
        fov: field of view in degrees, (horizontal, vertical)

    Returns:
       Pair of floating point angles (x, y) in degrees
    """
    fov = (math.radians(fov[0]), math.radians(fov[1]))
    
    x_pos = aim_target[0]/(window_size[0]-1)
    y_pos = aim_target[1]/(window_size[1]-1)


    x_angle = math.atan((x_pos-0.5)*2*math.tan(fov[0]/2))
    y_angle = math.atan((y_pos-0.5)*2*math.tan(fov[1]/2))

    return (math.degrees(x_angle), math.degrees(y_angle))


print(get_angles(
    (0, 0), (1920, 1080), (100, 67.67)
), "should be around -50, -33.835")

print(get_angles(
    (1919, 1079), (1920, 1080), (100, 67.67)
), "should be around 50, 33.835")

print(get_angles(
    (959.5, 539.5), (1920, 1080), (100, 67.67)
), "should be around 0, 0")

print(get_angles(
    (479.75, 269.75), (1920, 1080), (100, 67.67)
), "should be around 30.79, 18.53")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将3D世界坐标转换为3D相机坐标 - How convert 3d world coordinates to 3d camera coordinates 如何将n,y,z坐标数组转换为numpy中的3D路径 - How to convert arrays of x,y,z coordinates to 3D path in numpy 如何操作 3D 数组以将笛卡尔坐标转换为球坐标 - How to manipulate a 3D array to convert from cartesian coordinates to spherical coordinates 如何将道路上某个点在其图像坐标中的 (x,y) 坐标转换为其在世界坐标中的 (x,y) 坐标? - How do I convert the (x,y) coordinates of a point on the road in its image coordinates, to its (x,y) coordinates in the world coordinates? 将3d钻孔轨迹转换为笛卡尔坐标并使用matplotlib绘制它 - Convert a 3d drillhole trace to cartesian coordinates and plot it with matplotlib 3d将x,y,z坐标转换为3d numpy数组 - 3d coordinates x,y,z to 3d numpy array 如何提取 svg 图像的笛卡尔坐标 (x,y)? - How to extract the cartesian coordinates (x,y) of an svg image? 3D通过分类(x,y)坐标散布颜色? - 3D scatter color by categorical (x,y) coordinates? 获取 3D 中平面给定 x 和 z 的 y 坐标 - Get y coordinates given x and z for a plane in 3D 在OpenCV中获取具有X,Y和到对象的距离的3D坐标 - Get 3D coordinates in OpenCV having X,Y and distance to object
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM