Forum

Author Topic: Rescaling BBox after Alignement  (Read 280 times)

Lennart Hildebrandt

  • Newbie
  • *
  • Posts: 2
    • View Profile
Rescaling BBox after Alignement
« on: February 28, 2024, 12:42:52 PM »
Hello Community,

currentyl I'm running my Workflow through an own Python script.
As I found out that Metashape often screws up the bounding box within my use cases, I have to check it manually everytime before creating the dense scene.
I'd like to skip this and keep my programm consistent so I'm looking for a solution to set the size of the bbox at 30 m x 30 m x 10 m for example.
But it seems that I do not understand the API in this case. Is there anyone who can help me out?

Thanks in advance.
Regards
Lennart
« Last Edit: February 28, 2024, 01:01:04 PM by Lennart Hildebrandt »

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 14846
    • View Profile
Re: Rescaling BBox after Alignement
« Reply #1 on: February 29, 2024, 05:38:02 PM »
Hello Lennart,

Please check below the script that allows to define the bounding box (region) by XY coordinates of its opposite corners and using min/max altitude value.
The center, size and rotation of the bounding box is defined by region.center vector, region.size vector and region.rot matrix, but note that they are defined in the internal coordinate system and need to be transformed to/form reference coordinate system used in the project.

Hope you can figure out main principles for chunk.region definition, but if any comments are required, please do not hesitate to ask.


Code: [Select]
import Metashape
from PySide2 import QtCore, QtGui, QtWidgets


def cross(a, b):
"""
vector product of two vectors
"""
result = Metashape.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result.normalized()

class define_box(QtWidgets.QDialog):
"""
main dialog
"""

def __init__ (self, parent):
QtWidgets.QDialog.__init__(self, parent)
self.setWindowTitle("Resize Bounding Box")
global doc
doc = Metashape.app.document
crs = Metashape.CoordinateSystem('LOCAL_CS["Local Coordinates (m)",LOCAL_DATUM["Local Datum",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]')
if len(doc.chunks):
crs = doc.chunk.crs

chunk = doc.chunk
center = chunk.region.center
size = chunk.region.size
rotate = chunk.region.rot
T = chunk.transform.matrix

corners = [T.mulp(center + rotate * Metashape.Vector([size[0] * ((i & 1) - 0.5), 0.5 * size[1] * ((i & 2) - 1), 0.25 * size[2] * ((i & 4) - 2)])) for i in range(8)]
if chunk.crs:
corners = [chunk.crs.project(x) for x in corners]
corners = [corners[3], corners[1], corners[2], corners[-1]]
else:
corners = [Metashape.Vector([0, 1, 0])] * 4

#labels and #text edits
i = 0
for label in ["X1", "Y1", "X2", "Y2"]:
attr = "txt" + label
setattr(self, attr, QtWidgets.QLabel())
box = getattr(self, attr)
box.setText(label[:-1] + ":")

attr = "edt" + label
setattr(self, attr, QtWidgets.QLineEdit())
box = getattr(self, attr)
box.setText("{:.5f}".format(corners[i // 2][i % 2])) #box.setText("0")

i += 1
if (i % 2):
attr = "Corner_" + str(i // 2 + 1)
setattr(self, attr, QtWidgets.QLabel())
box = getattr(self, attr)
box.setText(attr.replace("_"," ") + ":")

self.txtAltitude = QtWidgets.QLabel()
self.txtAltitude.setText("Altitude:")
self.txtAMin = QtWidgets.QLabel()
self.txtAMin.setText("Min:")
self.txtAMax = QtWidgets.QLabel()
self.txtAMax.setText("Max:")
self.edtAMin = QtWidgets.QLineEdit()
self.edtAMax = QtWidgets.QLineEdit()
self.edtAMin.setText("{:.2f}".format(corners[0].z))
self.edtAMax.setText("{:.2f}".format(corners[-1].z))

self.txtCrs = QtWidgets.QLabel()
self.txtCrs.setText("CS:")
self.edtCrs = QtWidgets.QLabel()
self.edtCrs.setDisabled(True)
self.edtCrs.setText(crs.name + " (" + crs.authority + ")")

#buttons
self.btnP1 = QtWidgets.QPushButton("Apply")
self.btnQuit = QtWidgets.QPushButton("Close")

#creating layout
layout = QtWidgets.QGridLayout()

layout.addWidget(self.txtCrs, 0, 0)
layout.addWidget(self.edtCrs, 0, 2, 1, 3)

layout.addWidget(self.Corner_1, 1, 0)
layout.addWidget(self.Corner_2, 2, 0)

layout.addWidget(self.txtX1, 1, 1)
layout.addWidget(self.txtY1, 1, 3)
layout.addWidget(self.txtX2, 2, 1)
layout.addWidget(self.txtY2, 2, 3)

layout.addWidget(self.edtX1, 1, 2)
layout.addWidget(self.edtY1, 1, 4)
layout.addWidget(self.edtX2, 2, 2)
layout.addWidget(self.edtY2, 2, 4)

layout.addWidget(self.txtAltitude, 4, 0)
layout.addWidget(self.txtAMin, 4, 1)
layout.addWidget(self.txtAMax, 4, 3)
layout.addWidget(self.edtAMin, 4, 2)
layout.addWidget(self.edtAMax, 4, 4)

layout.addWidget(self.btnP1, 5, 1, 5, 2)
layout.addWidget(self.btnQuit, 5, 3, 5, 4)
self.setLayout(layout)

proc_set_bbox = lambda : self.set_bbox()
QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_set_bbox)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))

self.exec()

def set_bbox(self):

global doc
doc = Metashape.app.document

if not len(doc.chunks):
message = "No chunks! Script aborted"
print(message)
Metashape.app.messageBox(message)
return False

try:
altitude = float(self.edtAMin.text()), float(self.edtAMax.text())
except ValueError:
message = "Invalid altitude values. Can't apply to bounding box."
print(message)
Metashape.app.messageBox(message)
return False
if altitude[0] > altitude[1]:
altitude[0], altitude[1] = altitude[1], altitude[0]
print("Min and max altitude values switched")

self.corners = list()
avg_alt = (altitude[0] + altitude[1]) / 2.
try:
self.corners.append(Metashape.Vector([float(self.edtX1.text()), float(self.edtY1.text()), avg_alt]))
self.corners.append(Metashape.Vector([float(self.edtX2.text()), float(self.edtY1.text()), avg_alt]))
self.corners.append(Metashape.Vector([float(self.edtX2.text()), float(self.edtY2.text()), avg_alt]))
self.corners.append(Metashape.Vector([float(self.edtX1.text()), float(self.edtY2.text()), avg_alt]))

except ValueError:
message = "Invalid corner coordinates values. Can't apply to bounding box."
print(message)
Metashape.app.messageBox(message)
return False
print("Script started...")

chunk = Metashape.app.document.chunk
crs = chunk.crs
T = chunk.transform.matrix
s = chunk.transform.scale
new_region = Metashape.Region()

self.corners = [T.inv().mulp(crs.unproject(x)) for x in list(self.corners)]
new_center = (self.corners[0] + self.corners[1]) / 2.
new_region.center = new_center

side1 = self.corners[0] - self.corners[1]
side2 = self.corners[0] - self.corners[-1]
side1g = T.mulp(self.corners[0]) - T.mulp(self.corners[1])
side2g = T.mulp(self.corners[0]) - T.mulp(self.corners[-1])
side3g = altitude[1] - altitude[0]
new_size = Metashape.Vector([side2g.norm()/s, side1g.norm()/s, side3g/s])
new_region.size = new_size

horizontal = side2
vertical = side1
normal = cross(vertical, horizontal)
horizontal = -cross(vertical, normal)
vertical = vertical.normalized()

R = Metashape.Matrix ([horizontal, vertical, -normal])
new_region.rot = R.t()

chunk.region = new_region

message = "Script finished. Bounding box adjusted."
print(message)
Metashape.app.messageBox(message)
return True


def main_bbox():

app = QtWidgets.QApplication.instance()
parent = app.activeWindow()
dlg = define_box(parent)

Metashape.app.addMenuItem("Custom menu/Set bounding box by corners", main_bbox)
Best regards,
Alexey Pasumansky,
Agisoft LLC