Spaces:
Running
Running
Andy Lee
commited on
Commit
·
b79fff8
1
Parent(s):
95c127a
revert: geoguessur_bot
Browse files- geoguesser_bot.py +211 -0
geoguesser_bot.py
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from io import BytesIO
|
2 |
+
import os
|
3 |
+
import dotenv
|
4 |
+
import base64
|
5 |
+
import pyautogui
|
6 |
+
import matplotlib.pyplot as plt
|
7 |
+
import math
|
8 |
+
from time import time, sleep
|
9 |
+
from typing import Tuple, List
|
10 |
+
from PIL import Image
|
11 |
+
|
12 |
+
from langchain_core.messages import HumanMessage, BaseMessage
|
13 |
+
from langchain_openai import ChatOpenAI
|
14 |
+
from langchain_anthropic import ChatAnthropic
|
15 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
16 |
+
|
17 |
+
dotenv.load_dotenv()
|
18 |
+
|
19 |
+
|
20 |
+
PROMPT_INSTRUCTIONS = """
|
21 |
+
Try to predict where the image was taken.
|
22 |
+
First describe the relevant details in the image to do it.
|
23 |
+
List some regions and places where it could be.
|
24 |
+
Chose the most likely Country and City or Specific Location.
|
25 |
+
At the end, in the last line a part from the previous reasoning, write the Latitude and Longitude from that guessed location
|
26 |
+
using the following format, making sure that the coords are valid floats, without anything else and making sure to be consistent with the format:
|
27 |
+
Lat: XX.XXXX, Lon: XX.XXXX
|
28 |
+
"""
|
29 |
+
|
30 |
+
|
31 |
+
class GeoBot:
|
32 |
+
prompt_instructions: str = PROMPT_INSTRUCTIONS
|
33 |
+
|
34 |
+
def __init__(self, screen_regions, player=1, model=ChatOpenAI, model_name="gpt-4o"):
|
35 |
+
self.player = player
|
36 |
+
self.screen_regions = screen_regions
|
37 |
+
self.screen_x, self.screen_y = screen_regions["screen_top_left"]
|
38 |
+
self.screen_w = screen_regions["screen_bot_right"][0] - self.screen_x
|
39 |
+
self.screen_h = screen_regions["screen_bot_right"][1] - self.screen_y
|
40 |
+
self.screen_xywh = (self.screen_x, self.screen_y, self.screen_w, self.screen_h)
|
41 |
+
|
42 |
+
self.map_x, self.map_y = screen_regions[f"map_top_left_{player}"]
|
43 |
+
self.map_w = screen_regions[f"map_bot_right_{player}"][0] - self.map_x
|
44 |
+
self.map_h = screen_regions[f"map_bot_right_{player}"][1] - self.map_y
|
45 |
+
self.minimap_xywh = (self.map_x, self.map_y, self.map_w, self.map_h)
|
46 |
+
|
47 |
+
self.next_round_button = (
|
48 |
+
screen_regions["next_round_button"] if player == 1 else None
|
49 |
+
)
|
50 |
+
self.confirm_button = screen_regions[f"confirm_button_{player}"]
|
51 |
+
|
52 |
+
self.kodiak_x, self.kodiak_y = screen_regions[f"kodiak_{player}"]
|
53 |
+
self.hobart_x, self.hobart_y = screen_regions[f"hobart_{player}"]
|
54 |
+
|
55 |
+
# Refernece points to calibrate the minimap everytime
|
56 |
+
self.kodiak_lat, self.kodiak_lon = (57.7916, -152.4083)
|
57 |
+
self.hobart_lat, self.hobart_lon = (-42.8833, 147.3355)
|
58 |
+
|
59 |
+
self.model = model(model=model_name)
|
60 |
+
|
61 |
+
@staticmethod
|
62 |
+
def pil_to_base64(image: Image) -> str:
|
63 |
+
buffered = BytesIO()
|
64 |
+
image.save(buffered, format="PNG")
|
65 |
+
img_base64_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
66 |
+
|
67 |
+
return img_base64_str
|
68 |
+
|
69 |
+
@classmethod
|
70 |
+
def create_message(cls, images_data: List[str]) -> HumanMessage:
|
71 |
+
message = HumanMessage(
|
72 |
+
content=[
|
73 |
+
{
|
74 |
+
"type": "text",
|
75 |
+
"text": cls.prompt_instructions,
|
76 |
+
},
|
77 |
+
]
|
78 |
+
+ [
|
79 |
+
{
|
80 |
+
"type": "image_url",
|
81 |
+
"image_url": {"url": f"data:image/png;base64,{img_data}"},
|
82 |
+
}
|
83 |
+
for img_data in images_data
|
84 |
+
],
|
85 |
+
)
|
86 |
+
|
87 |
+
return message
|
88 |
+
|
89 |
+
def extract_location_from_response(
|
90 |
+
self, response: BaseMessage
|
91 |
+
) -> Tuple[float, float]:
|
92 |
+
try:
|
93 |
+
response = response.content.split("\n")
|
94 |
+
while (
|
95 |
+
response
|
96 |
+
and len(response[-1]) == 0
|
97 |
+
and "lat" not in response[-1].lower()
|
98 |
+
):
|
99 |
+
response.pop()
|
100 |
+
if response:
|
101 |
+
prediction = response[-1]
|
102 |
+
else:
|
103 |
+
return None
|
104 |
+
print(f"\n-------\n{self.model} Prediction:\n", prediction)
|
105 |
+
|
106 |
+
# Lat: 57.7916, Lon: -152.4083
|
107 |
+
lat = float(prediction.split(",")[0].split(":")[1])
|
108 |
+
lon = float(prediction.split(",")[1].split(":")[1])
|
109 |
+
|
110 |
+
x, y = self.lat_lon_to_mercator_map_pixels(lat, lon)
|
111 |
+
print(f"Normalized pixel coordinates: ({x}, {y})")
|
112 |
+
|
113 |
+
if x < self.map_x:
|
114 |
+
x = self.map_x
|
115 |
+
print("x out of bounds")
|
116 |
+
elif x > self.map_x + self.map_w:
|
117 |
+
x = self.map_x + self.map_w
|
118 |
+
print("x out of bounds")
|
119 |
+
if y < self.map_y:
|
120 |
+
y = self.map_y
|
121 |
+
print("y out of bounds")
|
122 |
+
elif y > self.map_y + self.map_h:
|
123 |
+
y = self.map_y + self.map_h
|
124 |
+
print("y out of bounds")
|
125 |
+
|
126 |
+
return x, y
|
127 |
+
|
128 |
+
except Exception as e:
|
129 |
+
print("Error:", e)
|
130 |
+
return None
|
131 |
+
|
132 |
+
@staticmethod
|
133 |
+
def lat_to_mercator_y(lat: float) -> float:
|
134 |
+
return math.log(math.tan(math.pi / 4 + math.radians(lat) / 2))
|
135 |
+
|
136 |
+
def lat_lon_to_mercator_map_pixels(self, lat: float, lon: float) -> Tuple[int, int]:
|
137 |
+
"""
|
138 |
+
Convert latitude and longitude to pixel coordinates on the mercator projection minimap,
|
139 |
+
taking two known points 1 and 2 as a reference.
|
140 |
+
|
141 |
+
Args:
|
142 |
+
lat (float): Latitude (Decimal Degrees) of the point to convert.
|
143 |
+
lon (float): Longitude (Decimal Degrees) of the point to convert.
|
144 |
+
|
145 |
+
Returns:
|
146 |
+
tuple: x, y pixel coordinates of the point.
|
147 |
+
"""
|
148 |
+
|
149 |
+
# Calculate the x pixel coordinate
|
150 |
+
lon_diff_ref = self.kodiak_lon - self.hobart_lon
|
151 |
+
lon_diff = self.kodiak_lon - lon
|
152 |
+
|
153 |
+
x = (
|
154 |
+
abs(self.kodiak_x - self.hobart_x) * (lon_diff / lon_diff_ref)
|
155 |
+
+ self.kodiak_x
|
156 |
+
)
|
157 |
+
|
158 |
+
# Convert latitude and longitude to mercator projection y coordinates
|
159 |
+
mercator_y1 = self.lat_to_mercator_y(self.kodiak_lat)
|
160 |
+
mercator_y2 = self.lat_to_mercator_y(self.hobart_lat)
|
161 |
+
mercator_y = self.lat_to_mercator_y(lat)
|
162 |
+
|
163 |
+
# Calculate the y pixel coordinate
|
164 |
+
lat_diff_ref = mercator_y1 - mercator_y2
|
165 |
+
lat_diff = mercator_y1 - mercator_y
|
166 |
+
|
167 |
+
y = (
|
168 |
+
abs(self.kodiak_y - self.hobart_y) * (lat_diff / lat_diff_ref)
|
169 |
+
+ self.kodiak_y
|
170 |
+
)
|
171 |
+
|
172 |
+
return round(x), round(y)
|
173 |
+
|
174 |
+
def select_map_location(self, x: int, y: int, plot: bool = False) -> None:
|
175 |
+
# Hovering over the minimap to expand it
|
176 |
+
pyautogui.moveTo(
|
177 |
+
self.map_x + self.map_w - 15, self.map_y + self.map_h - 15, duration=0.5
|
178 |
+
)
|
179 |
+
# bot.screen_w-50, bot.screen_h-80
|
180 |
+
# pyautogui.moveTo(self.screen_w-50, self.screen_h-80, duration=1.5)
|
181 |
+
# print(self.screen_w-50, self.screen_h-80)
|
182 |
+
print("finish moving")
|
183 |
+
sleep(0.5)
|
184 |
+
|
185 |
+
# Clicking on the predicted location
|
186 |
+
pyautogui.click(x, y, duration=0.5)
|
187 |
+
print("finish clicking")
|
188 |
+
sleep(0.5)
|
189 |
+
|
190 |
+
if plot:
|
191 |
+
self.plot_minimap(x, y)
|
192 |
+
|
193 |
+
# Confirming the guessed location
|
194 |
+
pyautogui.click(self.confirm_button, duration=0.2)
|
195 |
+
sleep(2)
|
196 |
+
|
197 |
+
def plot_minimap(self, x: int = None, y: int = None) -> None:
|
198 |
+
minimap = pyautogui.screenshot(region=self.minimap_xywh)
|
199 |
+
plot_kodiak_x = self.kodiak_x - self.map_x
|
200 |
+
plot_kodiak_y = self.kodiak_y - self.map_y
|
201 |
+
plot_hobart_x = self.hobart_x - self.map_x
|
202 |
+
plot_hobart_y = self.hobart_y - self.map_y
|
203 |
+
plt.imshow(minimap)
|
204 |
+
plt.plot(plot_hobart_x, plot_hobart_y, "ro")
|
205 |
+
plt.plot(plot_kodiak_x, plot_kodiak_y, "ro")
|
206 |
+
if x and y:
|
207 |
+
plt.plot(x - self.map_x, y - self.map_y, "bo")
|
208 |
+
|
209 |
+
os.makedirs("plots", exist_ok=True)
|
210 |
+
plt.savefig("plots/minimap.png")
|
211 |
+
# plt.show()
|