Forum

Author Topic: Rotate 2D image as it appears in Orthomosiac  (Read 6653 times)

Jordan Pierce

  • Newbie
  • *
  • Posts: 24
    • View Profile
Rotate 2D image as it appears in Orthomosiac
« on: February 02, 2024, 12:43:47 AM »
Hi, just curious if anyone is familiar with using the python api to rotate a 2D image by N degrees to match the view it appears in the orthomosaic?

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 15710
    • View Profile
Re: Rotate 2D image as it appears in Orthomosiac
« Reply #1 on: February 02, 2024, 01:04:05 AM »
Hello Jordan,

Do you mean to apply a yaw angle rotation to the original photo?

How you are planning to use it? If you need to open images in GIS application with the yaw rotation applied, then using additional World file may be a better solution.
Best regards,
Alexey Pasumansky,
Agisoft LLC

Jordan Pierce

  • Newbie
  • *
  • Posts: 24
    • View Profile
Re: Rotate 2D image as it appears in Orthomosiac
« Reply #2 on: February 02, 2024, 06:28:27 PM »
Hello Jordan,

Do you mean to apply a yaw angle rotation to the original photo?

How you are planning to use it? If you need to open images in GIS application with the yaw rotation applied, then using additional World file may be a better solution.

Hi Alexey, I ended up figuring it out. The issue was not with calculating the yaw angle from the camera.transform (see below, for others in the future), but rather the function used to perform the rotation (I think their docs are incorrect):

Code: [Select]
# Your transformation matrix
transformation_matrix = np.array(camera.transform)

# Extract the yaw angle in radians
yaw = np.arctan2(transformation_matrix[1, 0], transformation_matrix[0, 0])

# Convert radians to degrees
yaw_degrees = np.degrees(yaw)

The objective was to show the users the image rotated so they align with what can be viewed in the orthomosaic. I have access to the project file and the API so everything I need was there. Thank you for your response.

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 15710
    • View Profile
Re: Rotate 2D image as it appears in Orthomosiac
« Reply #3 on: February 07, 2024, 04:02:17 PM »
Hello Jordan,

There is an example of calculating estimated coordinates and orientation angles in the following script (which also considers antenna offsets, if present):
https://github.com/agisoft-llc/metashape-scripts/blob/master/src/save_estimated_reference.py

Just in case the use of the World file could be helpful, I am adding the script (developed for 1.6 version, however) that saves the World files for the aligned cameras, so that they can be used for proper overlay of the original images over some basemap in external GIS applications in the correct place and with the proper 2D orientation:
Code: [Select]
import os
import Metashape
from PySide2 import QtWidgets

global app
app = QtWidgets.QApplication.instance()
doc = Metashape.app.document

chunk = doc.chunk
crs = chunk.crs
T = chunk.transform.matrix
print('Processing started...')
nprocessed = 0

if chunk.model:
surface = chunk.model
elif chunk.dense_cloud:
surface = chunk.dense_cloud
else:
surface = chunk.point_cloud

for photo in chunk.cameras:
if not photo.type == Metashape.Camera.Type.Regular: #skip camera track, if any
continue
if not photo.transform: #skip not aligned cameras
continue

A = D = B = E = C = F = 0.0
x0 = Metashape.Vector((0.0,0.0,0.0))
x1 = Metashape.Vector((0.0,0.0,0.0))
x2 = Metashape.Vector((0.0,0.0,0.0))

if (photo.photo.path.find("/") == -1):
name = photo.photo.path.rsplit("\\", 1)[1]
else:
name = photo.photo.path.rsplit("/", 1)[1]
path = photo.photo.path.rsplit(".", 1)[0]
photo_ext = photo.photo.path.rsplit(".",1)[1]

file_ext = ".jgw"
if (photo_ext.upper() in ["TIF", "TIFF"]):
file_ext = ".tfw"
elif (photo_ext.upper() in ["JPG", "JPEG"]):
file_ext = ".jgw"
file = open(path + file_ext, "wt")

# vectors corresponding to photo corners

sensor = photo.sensor
width = photo.sensor.width
height = photo.sensor.height
calib = photo.sensor.calibration
corners = list()
for i in [[0, 0], [sensor.width - 1, 0], [sensor.width - 1, sensor.height - 1], [0, sensor.height - 1]]:
corners.append(surface.pickPoint(photo.center, photo.transform.mulp(calib.unproject(Metashape.Vector(i)))))
if not corners[-1]:
corners[-1] = chunk.point_cloud.pickPoint(photo.center, photo.transform.mulp(calib.unproject(Metashape.Vector(i))))
if not corners[-1]:
break
corners[-1] = crs.project(T.mulp(corners[-1]))

if not all(corners):
print("Skipping camera " + photo.label + "...")
continue

x0 = corners[0]
x1 = corners[1]
x2 = corners[3]

# solution
C = x0[0]
F = x0[1]
A = (x1[0] - x0[0]) / width
D = (x1[1] - x0[1]) / width
B = (x2[0] - x0[0]) / height
E = (x2[1] - x0[1]) / height

n = "\n"
output_str = ("{0:.10f}".format(A) + n + "{0:.10f}".format(D) + n + "{0:.10f}".format(B) + n + "{0:.10f}".format(E) + n + "{0:.10f}".format(C) + n + "{0:.10f}".format(F) + n)
file.write(output_str)
file.close()
print(photo.label + " written...")
app.processEvents()
nprocessed += 1

print('Processing finished, generated ' + str(nprocessed) + ' world file(s)')

Information about World file format can be found here:
https://en.wikipedia.org/wiki/World_file
Best regards,
Alexey Pasumansky,
Agisoft LLC