Skip to content

Commit

Permalink
fix: calculate bbox for the drone image
Browse files Browse the repository at this point in the history
  • Loading branch information
nrjadkry committed Dec 11, 2024
1 parent 6516c2b commit e0ce06c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 11 deletions.
101 changes: 91 additions & 10 deletions src/backend/app/gcp/gcp_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from app.s3 import get_presigned_url
from app.waypoints import waypoint_schemas
from app.config import settings
from pyproj import Transformer


async def calculate_bounding_box(
Expand Down Expand Up @@ -103,7 +104,9 @@ async def find_matching_images_that_contains_point(bounding_boxes, gps_coordinat
return matching_images


async def calculate_bbox_from_images_file(images_json_url: str):
async def calculate_bbox_from_images_file(
images_json_url: str, fov_degree: float, altitude: float
):
"""
Create bounding boxes for all images from a presigned JSON file URL.
"""
Expand All @@ -116,16 +119,12 @@ async def calculate_bbox_from_images_file(images_json_url: str):
filename = image["filename"]
lat = image["latitude"]
lon = image["longitude"]
altitude = image["altitude"]
width = image["width"]
height = image["height"]
focal_ratio = image["focal_ratio"]
fnumber = image["fnumber"]

# Calculate the bounding box
bbox = await calculate_bounding_box(
lat, lon, width, height, focal_ratio, fnumber, altitude
)
aspect_ratio = width / height

bbox = await calc_bbox(lat, lon, altitude, fov_degree, aspect_ratio)
bounding_boxes[filename] = bbox

return bounding_boxes
Expand Down Expand Up @@ -158,6 +157,82 @@ async def calculate_image_footprint(
return width, height


async def calc_bbox(lat, long, altitude, fov_degree, aspect_ratio):
# Define the bounding box coordinates in EPSG:3857
# Update offset function to work with EPSG:3857 coordinates
async def offset_coordinates_3857(x, y, dx, dy):
"""
Calculate new coordinates in EPSG:3857 given distance offsets.
"""
new_x = x + dx
new_y = y + dy
return new_x, new_y

# Initialize transformer for WGS84 to EPSG:3857 and vice versa
wgs84_to_3857 = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
epsg_3857_to_wgs84 = Transformer.from_crs("EPSG:3857", "EPSG:4326", always_xy=True)

# Convert centroid coordinates to EPSG:3857
centroid_3857 = wgs84_to_3857.transform(long, lat)

# Calculate the width and height of the image footprint
footprint_height, footprint_width = await calculate_image_footprint(
altitude, fov_degree, aspect_ratio
)

# Calculate half-width and half-height in meters (same as before)
half_width = footprint_width / 2
half_height = footprint_height / 2

# Calculate the four corners in EPSG:3857
top_left_3857 = await offset_coordinates_3857(
centroid_3857[0], centroid_3857[1], -half_width, half_height
)
top_right_3857 = await offset_coordinates_3857(
centroid_3857[0], centroid_3857[1], half_width, half_height
)
bottom_right_3857 = await offset_coordinates_3857(
centroid_3857[0], centroid_3857[1], half_width, -half_height
)
bottom_left_3857 = await offset_coordinates_3857(
centroid_3857[0], centroid_3857[1], -half_width, -half_height
)

# Convert corners back to WGS84
top_left_wgs84 = epsg_3857_to_wgs84.transform(top_left_3857[0], top_left_3857[1])
top_right_wgs84 = epsg_3857_to_wgs84.transform(top_right_3857[0], top_right_3857[1])
bottom_right_wgs84 = epsg_3857_to_wgs84.transform(
bottom_right_3857[0], bottom_right_3857[1]
)
bottom_left_wgs84 = epsg_3857_to_wgs84.transform(
bottom_left_3857[0], bottom_left_3857[1]
)

# Extract longitude and latitude values
longitudes = [
top_left_wgs84[0],
top_right_wgs84[0],
bottom_right_wgs84[0],
bottom_left_wgs84[0],
]
latitudes = [
top_left_wgs84[1],
top_right_wgs84[1],
bottom_right_wgs84[1],
bottom_left_wgs84[1],
]

# Calculate the bounding box: [min_longitude, min_latitude, max_longitude, max_latitude]
bbox = [
min(longitudes), # min_longitude
min(latitudes), # min_latitude
max(longitudes), # max_longitude
max(latitudes), # max_latitude
]

return bbox


def find_images_with_coordinate(
bounding_boxes: Dict[str, Tuple[float, float, float, float]],
gps_coordinate: Tuple[float, float],
Expand Down Expand Up @@ -200,7 +275,11 @@ def find_images_with_coordinate(


async def process_images_for_point(
project_id: uuid.UUID, task_id: uuid.UUID, point: waypoint_schemas.PointField
project_id: uuid.UUID,
task_id: uuid.UUID,
point: waypoint_schemas.PointField,
fov_degree: float,
altitude: float,
) -> List[str]:
"""
Process images to find those containing a specific point and return their pre-signed URLs.
Expand All @@ -221,7 +300,9 @@ async def process_images_for_point(
s3_images_json_url = get_presigned_url(settings.S3_BUCKET_NAME, s3_images_json_path)

# Fetch bounding boxes from the `images.json` file
bbox_list = await calculate_bbox_from_images_file(s3_images_json_url)
bbox_list = await calculate_bbox_from_images_file(
s3_images_json_url, fov_degree, altitude
)

# Extract the longitude and latitude of the point
point_tuple = (point.longitude, point.latitude)
Expand Down
6 changes: 5 additions & 1 deletion src/backend/app/gcp/gcp_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ async def find_images(
point: waypoint_schemas.PointField = None,
) -> List[str]:
"""Find images that contain a specified point."""
return await gcp_crud.process_images_for_point(project_id, task_id, point)
fov_degree = 82.1 # For DJI Mini 4 Pro
altitude = 100 # TODO: Get this from db
return await gcp_crud.process_images_for_point(
project_id, task_id, point, fov_degree, altitude
)

0 comments on commit e0ce06c

Please sign in to comment.