Hi all,
I'm still struggling with this. I'd really appreciate if someone could help me solve this problem and so, I'm recapping what I've done so far here.
I'm a biologist that studies animal movement and behaviour. As part of my work, I acquired UAV images which I used in Agisoft Metashape to reconstruct a georeferenced map (an orthomosaic) of my animals' habitat. As a next step, I have videos I collected within this habitat. I have used computer vision algorithms to track my animals within my video, thus giving me their location in pixel coordinates. I would now like to convert trajectories of these animals from pixel space to global coordinates. To test if this would work, I first marked specific points on my orthomosaic. I then converted these coordinates to image space (from 3D to 2D) using the following code (note the images are part of a separate chunk which I've aligned separately. I've also georeferenced this chunk using GCPs and the tiepoints align beautifully to the original chunk where I created the orthomosaic):
import Metashape
import numpy as np
import pandas as pd
def list_coordinates(file_path):
data = []
with open(file_path, 'r') as file:
for line in file:
values = line.strip().split(',')[1:-1]
# Filter out empty strings before converting to float
values = [float(val) if val else 0.0 for val in values]
data.append(tuple(values))
return data
def process_chunk(chunk, global_coordinates):
T = chunk.transform.matrix
data_list = []
for point_idx, global_coord in enumerate(global_coordinates):
p = T.inv().mulp(chunk.crs.unproject(Metashape.Vector(global_coord)))
print(f"Image pixel coordinates of point {point_idx + 1} with global coordinates ({chunk.crs.name}): {global_coord}")
for i, camera in enumerate(chunk.cameras):
project_point = camera.project(p)
if project_point:
u = project_point.x # u pixel coordinates in camera
v = project_point.y # v pixel coordinates in camera
if 0 <= u <= camera.sensor.width and 0 <= v <= camera.sensor.height:
# Extract video and frame_seq from the camera label
camera_label_parts = camera.label.split('_')
video = '_'.join(camera_label_parts[:-1])
frame_seq = camera_label_parts[-1]
data_list.append([point_idx + 1, camera.label, video, frame_seq, u, v])
columns = ['Point', 'Camera', 'video', 'frame_seq', 'u', 'v']
df = pd.DataFrame(data_list, columns=columns)
return df
# Define global coordinates for multiple points
points = list_coordinates('/Users/vivekhsridhar/Library/Mobile Documents/com~apple~CloudDocs/Documents/Metashape/TalChhapar/output/ninety_territory_points_xyz.txt')
# Use active Metashape document
doc = Metashape.app.document
# Iterate through all chunks in the document
df = pd.DataFrame()
for chunk in doc.chunks:
if chunk.label != 'Chunk 1' and chunk.label != 'Chunk 2':
# Set the current chunk to the one being processed
doc.chunk = chunk
# Process the current chunk
tmp = process_chunk(chunk, points)
df = pd.concat([df, tmp], ignore_index=True)
# Save the results to a CSV file
df.to_csv('/Users/vivekhsridhar/Library/Mobile Documents/com~apple~CloudDocs/Documents/Metashape/TalChhapar/output/p1_territory_points_uv.csv', index=False)
I know this code works because I plotted the 2D coordinates onto the respective images and they appear at the right location on the image (attached sample image). So next, I decided to convert these 2D coordinates back to 3D and check if I obtained coordinates of my original points. I used the following code to do this:
import Metashape
import datetime
import pandas as pd
doc = Metashape.app.document
chunk = doc.chunks[0]
surface = chunk.point_cloud
chunk = doc.chunks[2]
print(chunk.label)
camera = chunk.cameras[0]
print(camera)
df = pd.read_csv('/Users/vivekhsridhar/Library/Mobile Documents/com~apple~CloudDocs/Documents/Metashape/TalChhapar/output/p1_territory_points_uv.csv')
df = df[df['Camera'] == '20230310_SE_Lek1_P1D1_DJI_0190_0000']
for index, row in df.iterrows():
x = row['u']
y = row['v']
coords_2D = Metashape.Vector([x, y])
point_internal = surface.pickPoint(camera.center, camera.unproject(coords_2D))
point3D_world = chunk.crs.project(chunk.transform.matrix.mulp(point_internal))
print(point_internal)
print(point3D_world)
This however does not work and the newly obtained 3D coordinates do not match the location of the original point on the orthomosaic. Where have I gone wrong? Unlike my previous post, all my chunks now have an [R] flag next to them so all images are referenced as well. I'd appreciate any help in terms of how I can proceed with this.
Best,
Vivek