Agisoft Metashape
Agisoft Metashape => Python and Java API => Topic started by: jmsuomal on January 09, 2014, 06:19:32 PM
-
I would like to set the mapping region with Pytho scripting, but I seem to have problems because I dont understand the chunk.region object.
Here is what I have managed to do so far:
# Define: Region [CenterX, CenterY, SizeX, SizeY, ZRotationDeg]
MapRegionVec = [304331.4, 5740607.3, 173.8, 143.6, -25.0]
# Define: Coordinate system
CoordinateSystemEPSG = "EPSG::32632"
# create chunk...
# Set the coordinate system
CoordinateSystem = PhotoScan.CoordinateSystem()
CoordinateSystem.init(CoordinateSystemEPSG)
chunk.crs = CoordinateSystem
chunk.projection = CoordinateSystem
# (load camera calib...)
# (load photos...)
# (load initial camera orientations...)
# (matchPhotos...)
# (alignPhotos...)
# Set region center:
newregion = PhotoScan.Region()
centerUTM = PhotoScan.Vector([MapRegionVec[0], MapRegionVec[1], chunk.region.center[2]])
centerGEO = chunk.crs.unproject(centerUTM)
centerLocal = chunk.crs.localframe(centerGEO)
# LocalCoordinates are a 4x4 matrix instead of XYZ vector!
# I cant put that to XYZ. I have no idea what to do so I use the original center point for now...
newregion.center = chunk.region.center
# Set region size (this one works fine)
newregion.size = PhotoScan.Vector([MapRegionVec[2], MapRegionVec[3], chunk.region.size[2]])
# Set region rotation
import math
SinRotZ= math.sin(math.radians(MapRegionVec[4]))
CosRotZ= math.cos(math.radians(MapRegionVec[4]))
newregion.rot = PhotoScan.Matrix( [[SinRotZ,-CosRotZ,0], [CosRotZ,SinRotZ,0], [0,0,1]] )
# This works technically but does not do what I want!
# put newregion to chunk
chunk.region = newregion
But on that I cannot get the chunk.region.center or chunk.region.rot configured correctly. Thus I have a couple open questions.
- What are the units/coordinatesystem in the chunk.region.center values? My chunk coordinate system is WGS84/UTM 32. Here's how the current center numbers look:
>>> chunk.region.center
Vector([-4.059450202416892, -0.8626608305524442, 12.09349905182462])
Z could be quite sensible value for altitude, but what are those X and Y units. They are not UTM coordinates for sure. How do I convert my UTM coordinates to these?
- What is this chunk.region.rot object? Rotation matrix? Using standard z-rotation matrix of:
[sin(RotAngleRad),-cos(RotAngleRad),0],
[cos(RotAngleRad),sin(RotAngleRad),0],
[0,0,1]
does not seem to do what I would assume it does. For example running:
region = chunk.region
region.rot = PhotoScan.Matrix([[1,0,0],[0,1,0],[0,0,1]])
chunk.region = region
...does not put the region along the XY axis, but rotates it in an angle (that might be based to the orientation of first photo?) How do I set the region to certain Kappa angle?
Any hints or even an example script how to do this?
-
Hello jmsuomal,
Chunk.region.size and center are in the internal units referred to the internal chunk coordinate system.
So, for example, to convert chunk.region.center vector from this system you need to use chunk.transform matrix:
c = chunk.region.center
c.size = 4
c.w = 1
c_temp = chunk.transform * c
c_temp.size = 3
c_geo = chunk.crs.project(c_temp)
and to get vector V from geographic coordinates to the internal chunk coordinate system you need to perform inverted workflow:
v_temp = chunk.crs.unproject(v)
v_temp.size = 4
v_temp.w = 1
v_internal = chunk.transform.inv() * v_temp
v_internal.size = 3
-
Thanks Alexey,
That solved the center coordinates. :)
Any suggestion how I should handle the rotation?
-
Hello,
Somewhere on forum I have already posted the code that rotates the bounding box in accordance of the chunk coordinate system, so you need to use it and for additional rotations multiply the matrix (m) by rotation 3x3 matrices:
T = chunk.transform
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
reg = chunk.region
reg.rot = R.t()
chunk.region = reg
If chunk.transform and chunk.crs are None, you should use identity matrix instead them.
-
Thanks Alexey. You are the best! I definitely would not have been able to work that out myself.
For other readers of the forum: here's my final working code:
# Define: Map region
#MapRegionVec = [CenterX, CenterY, SizeX0, SizeY0, ZRotationDeg]
MapRegionVec = [304331.4, 5740607.3, 173.8, 143.6, -25.0]
# ALIGN PHOTOS
chunk.alignPhotos()
# SET MAPPING REGION (BOUNDING BOX)
# create new region object
newregion = PhotoScan.Region()
# Set region center:
centerUTM = PhotoScan.Vector([MapRegionVec[0], MapRegionVec[1], 0])
centerGEO = chunk.crs.unproject(centerUTM)
centerGEO.size = 4
centerGEO.w = 1
centerLocal = chunk.transform.inv() * centerGEO
newregion.center = PhotoScan.Vector([centerLocal[0], centerLocal[1], chunk.region.center[2]])
# Set region size
newregion.size = PhotoScan.Vector([MapRegionVec[2], MapRegionVec[3], chunk.region.size[2]])
# Set region rotation
# build rotation matrix in our coordinate system
RotZDeg = -MapRegionVec[4]
SinRotZ= math.sin(math.radians(RotZDeg))
CosRotZ= math.cos(math.radians(RotZDeg))
RotMat = PhotoScan.Matrix([[CosRotZ,-SinRotZ,0,0],[SinRotZ,CosRotZ,0,0],[0,0,1,0],[0,0,0,1]])
# rotate region bounding box
T = chunk.transform
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
m = RotMat*m
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
newregion.rot = R.t()
# put newregion to chunk
chunk.region = newregion
-
Thanks,Very much!
-
Hello ppkong,
The script from above (http://www.agisoft.ru/forum/index.php?topic=1886.msg10045#msg10045) orients bounding box in accordance of the coordinate system applied to the chunk.
-
Thanks,Very much!
And I have another question. I set the region size with 500*500(meters),and the result region become 475*520,I want to know why?
-
Hello ppkong,
Could you please send the project file and the script used on support@agisoft.ru for the further investigation?
Please also specify how do you measure the size of the region?
-
Hello ppkong,
Could you please send the project file and the script used on support@agisoft.ru for the further investigation?
Please also specify how do you measure the size of the region?
I'm sorry, I found out that I had made a mistake in the calculation process leading to incorrect results. I've solved the problem.
Thank you so much!
-
Hi Alexey,
>> If chunk.transform and chunk.crs are None, you should use identity matrix instead them.
Would you provide an example for doing this?
My chunk.crs are None.
Thanks.
-
how can i implement this code
T = chunk.transform
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
reg = chunk.region
reg.rot = R.t()
chunk.region = reg
into a MenuItem?! so i can run the script from there?
Thanks for the help!
-
Hello ristag,
Save the file as *.py as plain text (don't forget to add import PhotoScan, math line in the beginning of the code), then use Run Script option in Tools Menu or the corresponding button on the Console pane.
-
thanks for the fast answer...
but I would like to run the script with a MenuItem like in the picture:
where i can run the script from there
-
Hello ristag,
Then you should use something similar to the following:
import PhotoScan, math
def rotateBb():
doc = PhotoScan.app.document
chunk = doc.activeChunk
T = chunk.transform
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
reg = chunk.region
reg.rot = R.t()
chunk.region = reg
PhotoScan.app.addMenuItem("Custom menu/Bounding box to coordinate system", rotateBb)
-
thank you very much!
and is there a option to run a script automatically on every photoscan start?
-
Hello ristag,
You can put it to the following folder:
C:/users/<user name>/AppData/Local/AgiSoft/PhotoScan Pro/scripts
But I recommend to check every new script you are moving to the autorun folder to avoid any unwanted issues.
-
Hello!
I tried to use this script:
# Define: Map region
#MapRegionVec = [CenterX, CenterY, SizeX0, SizeY0, ZRotationDeg]
MapRegionVec = [304331.4, 5740607.3, 173.8, 143.6, -25.0]
# ALIGN PHOTOS
chunk.alignPhotos()
# SET MAPPING REGION (BOUNDING BOX)
# create new region object
newregion = PhotoScan.Region()
# Set region center:
centerUTM = PhotoScan.Vector([MapRegionVec[0], MapRegionVec[1], 0])
centerGEO = chunk.crs.unproject(centerUTM)
centerGEO.size = 4
centerGEO.w = 1
centerLocal = chunk.transform.inv() * centerGEO
newregion.center = PhotoScan.Vector([centerLocal[0], centerLocal[1], chunk.region.center[2]])
# Set region size
newregion.size = PhotoScan.Vector([MapRegionVec[2], MapRegionVec[3], chunk.region.size[2]])
# Set region rotation
# build rotation matrix in our coordinate system
RotZDeg = -MapRegionVec[4]
SinRotZ= math.sin(math.radians(RotZDeg))
CosRotZ= math.cos(math.radians(RotZDeg))
RotMat = PhotoScan.Matrix([[CosRotZ,-SinRotZ,0,0],[SinRotZ,CosRotZ,0,0],[0,0,1,0],[0,0,0,1]])
# rotate region bounding box
T = chunk.transform
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
m = RotMat*m
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
newregion.rot = R.t()
# put newregion to chunk
chunk.region = newregion
But it didn't work. I've changed: MapRegionVec = [5592663.063, 5840723.167, 500, 500, 0] and disable "Allign Photos" option because my photos are already alligned.
I set correct coordinate system in my project and assumed that CRS [in scritpt] is loaded from project.
What could be wrong?
-
Hello eurosystem,
And what is the console pane output?
Probably, chunk variable used in the script is not defined? You need to assign it as chunk = PhotoScan.app.document.activeChunk.
-
Thank you Alexey, your advice was helpful and now it works fine:)
EDIT
unfortunately not everything is right
I think that my units are wrong. I set bounding box size as 1x1 and when I checked reg.size it shows values correctly. But when I analyzed result I saw that region is much more bigger (~40). Further, reg.center is placed in wrong place- I set it as a point 105.
(http://fotozrzut.pl/zdjecia/ca3f81cf2f.jpg) (http://fotozrzut.pl/)
-
I haven't solved this problem yet:/
Maybe it is caused somehow by transformation?
-
Hello eurosystem,
Could you please send the script you are using (with all the modifications) and the project file (just with sparse point cloud and markers, no need of dense cloud and mesh) to support@agisoft.ru?
-
Hello eurosystem,
Seems like the problem is in the input coordinates for the starting point - it has zero altitude in the following line: centerEPSG = PhotoScan.Vector([MapRegionVec[0], MapRegionVec[1], 0])
if you change the third coordinate to corresponding marker altitude you'll get the region centered by source marker location.
As for the size, you need to put size section after rotation calculation:
real_size = PhotoScan.Vector([MapRegionVec[2], MapRegionVec[3], 200]) #here z-size is 200m
newregion.size = real_size / s
-
Hello,
Somewhere on forum I have already posted the code that rotates the bounding box in accordance of the chunk coordinate system, so you need to use it and for additional rotations multiply the matrix (m) by rotation 3x3 matrices:
T = chunk.transform
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
reg = chunk.region
reg.rot = R.t()
chunk.region = reg
If chunk.transform and chunk.crs are None, you should use identity matrix instead them.
When I run python script in PS1.0 everything is ok,but some code can't run in PS 1.1 pre. It always prompt me some code error, for example "v_t = T * v","chunk.transform.inv()" etc
-
Hello ppkong,
There were some major changes in Python API for version 1.1.
Transformation matrix for chunk is now accessible via chunk.transform.matrix.
-
Hi Alexey,
I've used following the code (taken from the code earlier in this thread - big thanks to you and the other contributors!) to change my bbox x and y dimensions (and center location), however I'm also seeing an unwanted shift in the bbox z center location when the transformation is applied. This is particularly strange because the chunk region size and center z values (checked via the console) are unchanged after the transformation. I've attached screenshots of the bbox before and after transformation, showing the console output for the region size and center.
My code:
import PhotoScan, math
doc = PhotoScan.app.document
chunk = doc.activeChunk
reg = chunk.region
trans = chunk.transform
newregion = PhotoScan.Region()
map_center = [-122.4004003, 37.7772147]
map_size = [50., 50.]
RotZDeg = 0.0
# SET MAPPING REGION (BOUNDING BOX)
# Set region center:
center_geo = PhotoScan.Vector([map_center[0], map_center[1], 0.]) # uses existing region height
v_temp = chunk.crs.unproject(center_geo)
v_temp.size = 4
v_temp.w = 1
centerLocal = chunk.transform.inv() * v_temp
centerLocal.size = 3
newregion.center = PhotoScan.Vector([centerLocal[0], centerLocal[1], reg.center[2]]) # uses existing region height
# Set region size
#<---- Rotation ---->
rot_untransformed = PhotoScan.Matrix().diag([1,1,1,1])
rot_temp = trans * rot_untransformed
s = math.sqrt(rot_temp[0, 0]**2 + rot_temp[0, 1]**2 + rot_temp[0, 2]**2)
R = PhotoScan.Matrix( [[rot_temp[0,0],rot_temp[0,1],rot_temp[0,2]], [rot_temp[1,0],rot_temp[1,1],rot_temp[1,2]], [rot_temp[2,0],rot_temp[2,1],rot_temp[2,2]]])
R = R * (1.0 / s)
#<---- Size ---->
inter_size = PhotoScan.Vector([0,0,0])
geo_size = PhotoScan.Vector(map_size) # uses original chunk region z size
inter_size = geo_size / s
newregion.size = PhotoScan.Vector([inter_size[0], inter_size[1], reg.size[2]])
# Set region rotation
SinRotZ = math.sin(math.radians(RotZDeg))
CosRotZ = math.cos(math.radians(RotZDeg))
RotMat = PhotoScan.Matrix([[CosRotZ, -SinRotZ, 0, 0], [SinRotZ, CosRotZ, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) # just rotate about z-axis
# rotate region bounding box
T = chunk.transform
v = PhotoScan.Vector([0, 0, 0, 1])
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
m = RotMat*m
s = math.sqrt(m[0, 0]**2 + m[0, 1]**2 + m[0, 2]**2) # scale factor
R = PhotoScan.Matrix([[m[0, 0], m[0, 1], m[0, 2]], [m[1, 0], m[1, 1], m[1, 2]], [m[2, 0], m[2, 1], m[2, 2]]])
R = R * (1. / s)
newregion.rot = R.t()
# put newregion to chunk
chunk.region = newregion
Any ideas on what I need to do to keep the z size and center fixed would be greatly appreciated.
-
Hello ppkong,
There were some major changes in Python API for version 1.1.
Transformation matrix for chunk is now accessible via chunk.transform.matrix.
Thanks for your reply,there have many error when i run old script in 1.1 so can you tell me How to set map region in Python API 1.1?
-
Hello ppkong,
Try to replace doc.activeChunk by doc.chunk and chunk.transform by chunk.transform.matrix. I think that there should not be other changes for 1.1, but if it still doesn't work, please post an error message from Console pane output.
-
Thanks Alexey!
I have solved this problem according to your method.But i have a question about buildDenseCloud,My script:
chunk.buildDenseCloud(quality=PhotoScan.MediumQuality,filter=PhotoScan.AgressiveFiltering,keep_depth=True,reuse_depth=True
When I set reuse_depth as True,the Python Script can not run,she prompt me "selected 0 cameras from xxx in 0.00 sec" And can't Loading Photos...
I think “reuse_depth” function should be preserve the last available depth, and generated depth images without depth,But it seems not so,The reuse function is only use the last depth map.I think he will greatly save the processing time.
-
Hello ppkong,
Reuse depth maps option is only available if depth maps have been estimated earlier and were kept in the project (corresponding option in Advanced tab of PhotoScan Preferences window should be selected).
But this option could not be used properly if the camera positions have changed relatively to the moment when the depth maps have been calculated.
-
Thanks Alexey!
Another question is about GPU&CPU,Before “buildDenseCloud” I switch GPU on and switch of all CPU core.
PhotoScan.app.cpu_cores_inactive=16
PhotoScan.app.gpu_mask=1
When the buildDenseCloud is completed do I need to switch back to CPU in the python script for "chunk.buildModel"?
-
Hello ppkong,
You do not need to switch the number of CPU cores back after dense cloud generation stage. Currently these two mentioned keys are used only during buildDenseCloud stage.
-
Hello ppkong,
Reuse depth maps option is only available if depth maps have been estimated earlier and were kept in the project (corresponding option in Advanced tab of PhotoScan Preferences window should be selected).
But this option could not be used properly if the camera positions have changed relatively to the moment when the depth maps have been calculated.
Thanks,Alexey!
Your means is if I modify the region range, the position of the camera will change?I don't know much about depth map, but I also want to improve efficiency in this step.
-
Hello ppkong,
Changing the region may be the case if at first you've generated dense cloud for the small region and then extended the bounding box to fit all the scene.
-
;D Ha-ha,you're right.
I use this function for aero images process,And I think it is useful for process aero images.
When build Depth maps of area 1(Blue boundary of area), The program is generated for every entire image(Selected red boundary of images) that contain area 1.Then I think it can be reuse for process area 2,3,4.
-
I'm also trying to use the API to prescribe a reconstruction region.
Can anyone explain how to determine the parameter values needed for the MapRegionVec array as described by jmsuomal? (My chunk is in the geographic coordinate system ESPG::4326).
Thanks, Andy
-
Hello
I am using the following code:
# Get the coordinates from the user
coord_x = app.getFloat("x coordinate:")
coord_y = app.getFloat("y coordinate:")
mySize = 9
# Define: Map region
# MapRegionVec = [CenterX, CenterY, SizeX0, SizeY0, ZRotationDeg]
MapRegionVec = [coord_x, coord_y, mySize, mySize, 0.0]
# Select the project
doc = PhotoScan.app.document
# Get the current chunk
chunk = doc.chunk
# Set Bounding Box
# create new region object
newregion = PhotoScan.Region()
# Set region center
centerUTM = PhotoScan.Vector([MapRegionVec[0], MapRegionVec[1], 0])
centerGEO = chunk.crs.unproject(centerUTM)
centerGEO.size = 4
centerGEO.w = 1
centerLocal = chunk.transform.matrix.inv() * centerGEO
newregion.center = PhotoScan.Vector([centerLocal[0], centerLocal[1], chunk.region.center[2]])
# Set region size
newregion.size = PhotoScan.Vector([MapRegionVec[2], MapRegionVec[3], chunk.region.size[2]])
# Set region rotation
# build rotation matrix in our coordinate system
RotZDeg = -MapRegionVec[4]
SinRotZ= math.sin(math.radians(RotZDeg))
CosRotZ= math.cos(math.radians(RotZDeg))
RotMat = PhotoScan.Matrix([[CosRotZ,-SinRotZ,0,0],[SinRotZ,CosRotZ,0,0],[0,0,1,0],[0,0,0,1]])
# Rotate region bounding box
T = chunk.transform.matrix
v = PhotoScan.Vector( [0,0,0,1] )
v_t = T * v
v_t.size = 3
m = chunk.crs.localframe(v_t)
m = m * T
m = RotMat*m
s = math.sqrt(m[0,0]**2 + m[0,1]**2 + m[0,2]**2) #scale factor
R = PhotoScan.Matrix( [[m[0,0],m[0,1],m[0,2]], [m[1,0],m[1,1],m[1,2]], [m[2,0],m[2,1],m[2,2]]])
R = R * (1. / s)
newregion.rot = R.t()
# Put newregion to chunk
chunk.region = newregion
But I have 2 problems.
The first thing is that the size argument is wrong because if I enter 9.0 (as above) this is ca. 500 meters (But if I enter 500 in the size argument it's waaayyy to huge -> about 30km). How can I fix this?
And also the center coordinates that I'm entering are also not in the center of this. I'm using EPSG:21781 for my calculations. How can I really use them with center coordinates?
Or is it just the script that is wrong?
-
Hello r0xx,
When using real-world scales for the bounding box size you need to divide them by the scale factor s, calculated during rotation estimation.
As for the region center coordinates, you need to use all three point coordinates X, Y and Z, otherwise centerGEO vector in geocentric coordinates will be calculated incorrectly due to zero altitude above ellipsoid using in the script provided.
-
Adding a Z coordinate and dividing the size by scale factor fixed the problems! Thanks alot!