Forum

Author Topic: Is it possible to select/deselect matches between camera groups?  (Read 29540 times)

andyroo

  • Sr. Member
  • ****
  • Posts: 479
    • View Profile
Is it possible to select/deselect matches between camera groups?
« on: December 07, 2020, 09:30:29 AM »
I am trying to figure out how - or if - I can write a script to select or de-select only tie points/matches that are shared between camera groups (camera folders) in a chunk. I want to treat tie points between cameras differently than tie points for the same camera while performing gradual selection filtering, so after I select points with a certain threshold, I want to be able to deselect points that either do or don't have cameras for multiple groups (I want to do both at different times). If I knew how to do one I could do the other, but I'm confused where to start.

Is this possible to do? I'm not sure if I should start with chunk.point_cloud.projections or chunk.point_cloud.points or chunk.point_cloud.cameras. It looks like I can access chunk.point_cloud.projections by camera, but there's not a straightforward way to get a list of cameras for a given projection (tie point?).

Then I guess I'd have to parse that list and see if it had more than one camera group?

My brain wants to iterate through points (for point in chunk.point_cloud.points:) but maybe I have to iterate through cameras?

Any hints would be super-appreciated.

Andy

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 15730
    • View Profile
Re: Is it possible to select/deselect matches between camera groups?
« Reply #1 on: December 07, 2020, 01:00:02 PM »
Hello Andy,

So basically the task is to select the tie points that are common for all the cameras in the selected camera folder (or folders)?
Best regards,
Alexey Pasumansky,
Agisoft LLC

andyroo

  • Sr. Member
  • ****
  • Posts: 479
    • View Profile
Re: Is it possible to select/deselect matches between camera groups?
« Reply #2 on: December 08, 2020, 01:04:42 AM »
Hi Alexey,

I would like to be to do the following tasks:

1. deselect all of the selected points that were selected by gradual selection that are common to cameras from more than one folder, and
2. be able to select (and save/export) only the tie points that are common to cameras from more than one folder.

I think if you could show me how to do #1 I could probably figure out how to do #2 and all the other things I'd like to.

Alexey Pasumansky

  • Agisoft Technical Support
  • Hero Member
  • *****
  • Posts: 15730
    • View Profile
Re: Is it possible to select/deselect matches between camera groups?
« Reply #3 on: December 08, 2020, 05:59:32 PM »
Hello Andy,

I have written some clumsy not optimized code that should deselect any tie point from the current selection, providing that it has projection on at least two images from different selected camera groups:

Code: [Select]
import Metashape

doc = Metashape.app.document
chunk = doc.chunk
point_cloud = chunk.point_cloud
points = point_cloud.points
projections = point_cloud.projections
npoints = len(points)

selected_groups = dict()
for camera in chunk.cameras:
if not camera.group:
continue
if camera.group.selected:
if chunk.camera_groups.index(camera.group) in selected_groups.keys():
selected_groups[chunk.camera_groups.index(camera.group)].append(camera)
else:
selected_groups[chunk.camera_groups.index(camera.group)] = [camera]

selected_points = dict()
for igroup in selected_groups.keys():
for camera in selected_groups[igroup]:
point_index = 0
for proj in projections[camera]:
track_id = proj.track_id
while point_index < npoints and points[point_index].track_id < track_id:
point_index += 1
if point_index < npoints and points[point_index].track_id == track_id:
if not points[point_index].valid:
continue
else:
if points[point_index].selected:
if points[point_index].track_id in selected_points.keys():
selected_points[points[point_index].track_id][1] += 1
else:
selected_points[points[point_index].track_id] = [points[point_index], 1]

for point in selected_points.keys():
if selected_points[point][1] > 1:
selected_points[point][0].selected = False
print("done")

Can you please check on some small test project, if it works as expected?
Best regards,
Alexey Pasumansky,
Agisoft LLC

Paulo

  • Hero Member
  • *****
  • Posts: 1631
    • View Profile
Re: Is it possible to select/deselect matches between camera groups?
« Reply #4 on: December 09, 2020, 06:45:46 PM »
Hey Alexey,

I think that the code would still deselect tpts that have 2 or more projections in one selected group only and not different groups maybe following code could serve as basis for some better solution:

Code: [Select]
doc = Metashape.app.document
chunk = doc.chunk
point_cloud = chunk.point_cloud
points = point_cloud.points
projections = point_cloud.projections
npoints = len(points)

selected_groups = dict()
for camera in chunk.cameras:
if not camera.group:
continue
if camera.group.selected:
if chunk.camera_groups.index(camera.group) in selected_groups.keys():
selected_groups[chunk.camera_groups.index(camera.group)].append(camera)
else:
selected_groups[chunk.camera_groups.index(camera.group)] = [camera]
nselgrps = len(selected_groups.keys())
selected_points = dict()
for i, igroup in enumerate(selected_groups.keys()):
for camera in selected_groups[igroup]:
point_index = 0
for proj in projections[camera]:
track_id = proj.track_id
while point_index < npoints and points[point_index].track_id < track_id:
point_index += 1
if point_index < npoints and points[point_index].track_id == track_id:
if not points[point_index].valid:
continue
else:
if points[point_index].selected  :
if points[point_index].track_id in selected_points.keys() :
if selected_points[points[point_index].track_id][i+2] == 0:
selected_points[points[point_index].track_id][1] += 1
selected_points[points[point_index].track_id][i+2] = 1
else:
selected_points[points[point_index].track_id][i+2] += 1
else:
selected_points[points[point_index].track_id] = [points[point_index], 1] + nselgrps*[0]
selected_points[points[point_index].track_id][i+2] = 1
ndsel = 0
for point in selected_points.keys():
if selected_points[point][1] > 1:
selected_points[point][0].selected = False
ndsel += 1
print("Number pts unselected: ",ndsel,"Number remaining: ",len(selected_points.keys())-ndsel)
print("done")

I added n flags (where n is number of selected camera groups or folders) to selected_points dict so as to test if a projection in one group has not been already detected for a given selected tiept..I am sure it can be improved as I am not a Python expert.

Result would be something like this in case of 4 selected groups:

selected_points
Out[18]: 2020-12-11 10:52:36
2020-12-11 10:52:36 {27906: [<Metashape.PointCloud.Point at 0x1c861a65e50>, 2, 1, 0, 1, 0],
2020-12-11 10:52:36  114398: [<Metashape.PointCloud.Point at 0x1c861a65e10>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  114498: [<Metashape.PointCloud.Point at 0x1c8011ef410>, 1, 3, 0, 0, 0],
2020-12-11 10:52:36  114499: [<Metashape.PointCloud.Point at 0x1c861a65910>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  114500: [<Metashape.PointCloud.Point at 0x1c8011ef510>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  114503: [<Metashape.PointCloud.Point at 0x1c8011ef770>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  114505: [<Metashape.PointCloud.Point at 0x1c8011ef7b0>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  115029: [<Metashape.PointCloud.Point at 0x1c8011ef790>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  115030: [<Metashape.PointCloud.Point at 0x1c8011ef610>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  115129: [<Metashape.PointCloud.Point at 0x1c8011ef210>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  115138: [<Metashape.PointCloud.Point at 0x1c8011ef630>, 1, 2, 0, 0, 0],
2020-12-11 10:52:36  33357: [<Metashape.PointCloud.Point at 0x1c8011ef890>, 2, 2, 3, 0, 0],
2020-12-11 10:52:36  36919: [<Metashape.PointCloud.Point at 0x1c8011ef6d0>, 2, 2, 2, 0, 0],
2020-12-11 10:52:36  37316: [<Metashape.PointCloud.Point at 0x1c8011ef7d0>, 2, 2, 2, 0, 0],
2020-12-11 10:52:36  37874: [<Metashape.PointCloud.Point at 0x1c8011ef650>, 3, 2, 2, 1, 0],

....

where when 2 or more  flags are not 0 then there exists one or more projections from 2 or more different groups...so first integer after each PointCloud. Point in dict represents the number of groups or folders having projections.

Hope this can helpful,


« Last Edit: December 19, 2020, 02:50:38 PM by Paulo »
Best Regards,
Paul Pelletier,
Surveyor

andyroo

  • Sr. Member
  • ****
  • Posts: 479
    • View Profile
Re: Is it possible to select/deselect matches between camera groups?
« Reply #5 on: May 05, 2023, 01:55:28 AM »
I've recently started working on this again and I found Paul's code useful, but it's very slow on a large point cloud with many camera groups (85 million tie points, 40 groups). Is there a way to use concurrent.futures with this code? I'm trying with this version but I don't think I did it right - it looks like I'm still using just a single core. If I understand the code right, it's going to have to iterate through all 85 million tie points ~40 times, which seems inefficient...

Code: [Select]
import Metashape
import concurrent.futures
import multiprocessing

# Get the document, chunk, point cloud, points, and projections
doc = Metashape.app.document
chunk = doc.chunk
point_cloud = chunk.point_cloud
points = point_cloud.points
projections = point_cloud.projections
npoints = len(points)


# Create a dictionary of selected camera groups
selected_groups = dict()
for camera in chunk.cameras:
    if not camera.group:
        continue
    if camera.group.selected:
        if chunk.camera_groups.index(camera.group) in selected_groups.keys():
            selected_groups[chunk.camera_groups.index(camera.group)].append(camera)
        else:
            selected_groups[chunk.camera_groups.index(camera.group)] = [camera]
nselgrps = len(selected_groups.keys())


# Create a dictionary of selected points
selected_points = dict()


# Define a function to process each camera group
def process_group(group):
    #nonlocal npoints, points, selected_points, selected_groups, nselgrps
    for camera in group:
        point_index = 0
        for proj in projections[camera]:
            track_id = proj.track_id
            while point_index < npoints and points[point_index].track_id < track_id:
                point_index += 1
            if point_index < npoints and points[point_index].track_id == track_id:
                if not points[point_index].valid:
                    continue
                else:
                    if points[point_index].selected:
                        if points[point_index].track_id in selected_points.keys():
                            selected_points[points[point_index].track_id][1] += 1
                        else:
                            selected_points[points[point_index].track_id] = [points[point_index], 1]

 # Use concurrent.futures to execute the process_group function for each camera group with a lambda function
with concurrent.futures.ThreadPoolExecutor(multiprocessing.cpu_count()) as executor:
    executor.map(lambda group: process_group(group), selected_groups.values())

# Unselect any points that are visible in more than one camera group
ndsel = 0
for point in selected_points.keys():
    if selected_points[point][1] > 1:
        selected_points[point][0].selected = False
        ndsel += 1

# Print the number of points unselected and remaining
print("Number pts unselected: ",ndsel,"Number remaining: ",len(selected_points.keys())-ndsel)
print("done")

andyroo

  • Sr. Member
  • ****
  • Posts: 479
    • View Profile
Selecting/deselecting matches between camera groups - need API method?
« Reply #6 on: June 06, 2025, 03:47:11 AM »
I finally got some python together (attached) that does a ... decent(?) job of enumerating selected points in different camera groups, and it is pretty quick at extracting coordinates and colors even! I think I am pretty close to figuring out how to extract the tie point "size", but a little unsure of how I should do that - maybe an average of all of the projections?

But this script is VERY slow (for my use case) in deselecting points, which is mostly what I'm trying to do. On a test 2-camera-group project with a point cloud with 1000 images and 1 million points it takes ~ 3 minutes on my hardware to deselect about 20k points, and with 10000 images and 10 million points it takes ~ 6 HOURS to deselect 200k points. My full-scale projects are more like a dozen camera groups and 100 million points, so this still seems unfeasible to me.

I think it's limited by the per-point performance penalties in the current implementation of the API. Or maybe by my incompetence at python multithreading or wrapping my head around the API. I tried all sorts of concurrent futures and multiprocessing iterations on the deselect portion of the code, but the two fastest ones in my tests have just been for loops.

I think to make this faster maybe the API needs a way to pass a list of points to select or deselect but I'm not sure. It would also be nice to be able to get the gradual selection and other values.


vineg

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Re: Is it possible to select/deselect matches between camera groups?
« Reply #7 on: June 06, 2025, 02:17:17 PM »
Hi, Andy!

Slow tie point .selected flag changing will be fixed in the next 2.2.2 update

Egor