Forum

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Sylvain M.

Pages: [1] 2
1
General / Managing non-aligned photos
« on: July 07, 2025, 05:29:14 PM »
Hello everyone!

I use Metashape to create orthophotos from aerial drone missions over forests.
Most of the time, I fly a DJI Mavic 3M at an altitude of 120 metres (the maximum regulatory height in Europe), which produces orthophotos at around 4 cm/px. This resolution is more than sufficient for our forestry needs.

However, I sometimes have problems aligning the photos, especially when flying low (60 m) above a dense canopy. This seems normal as the shots look the same and it is difficult to distinguish one tree from another.

However, last week I experienced alignment issues during a flight at 120 m.

No matter how much I adjust the alignment settings, a few photos still don't align.
Could someone who has mastered this step give me some advice?
If automated alignment fails, can you manually enter matching points between the photos?

(Attached are a few screenshots to illustrate the problem and the nature of the photos to be processed.)

2
Here is, i think, a better approch :
Code: [Select]
import os
import Metashape

# Paths to the folders containing the MS images
image_folders = [
    r"D:\PATH\FOLDER1",
    r"D:\PATH\FOLDER2"
]

# Access the document and add a chunk
doc = Metashape.Document()
chunk = doc.addChunk()

# File extensions to search for
extensions = ('.jpg', '.tif')

# List to store found files
files = []

# Collect all relevant files from the folders
for folder in image_folders:
    for file in os.listdir(folder):
        if file.lower().endswith(extensions):
            files.append(os.path.join(folder, file))

# Initialize lists for each type of image
rgb = []
green = []
nir = []
red = []
red_edge = []

# Sort files by their type
for file in files:
    if file.endswith("_D.JPG"):
        rgb.append(file)
    elif file.endswith("_MS_G.TIF"):
        green.append(file)
    elif file.endswith("_MS_NIR.TIF"):
        nir.append(file)
    elif file.endswith("_MS_R.TIF"):
        red.append(file)
    elif file.endswith("_MS_RE.TIF"):
        red_edge.append(file)

# Verify that each type has the same number of images
assert len(rgb) == len(green) == len(nir) == len(red) == len(red_edge), "The number of images per type is not the same."

# Create a combined list of images alternating the types
images = []
for i in range(len(rgb)):
    images.append(rgb[i])
    images.append(green[i])
    images.append(nir[i])
    images.append(red[i])
    images.append(red_edge[i])

# Define the image groups
groups = [5] * len(rgb)

# Add the images to the chunk
chunk.addPhotos(filenames=images, filegroups=groups, layout=Metashape.MultiplaneLayout)

# Save the project
doc.save(r'D:\PATH\Project.psx')

3
Unfortunately, my script doesn't work in all situations.
Most of the time, the 5 ‘simultaneous’ photos of the different spectral bands have the same timestamp, to the nearest second.
But there are cases where there can be a one-second difference (probably 1 hundredth of a second in reality), and so my groupings don't work.
Case in point:
Code: [Select]
DJI_20250611121953_0275_D.JPG
DJI_20250611121954_0275_MS_G.TIF
DJI_20250611121954_0275_MS_NIR.TIF
DJI_20250611121954_0275_MS_R.TIF
DJI_20250611121954_0275_MS_RE.TIF
(the first RGB photo is at 12h19m53s and the TIFs are at 12h19m54s)

I can try to adapt by only taking into account the fixed characters in a sequence (date and number, in this case 0275).
But I get the impression that this would not be optimal : could I ask what approach you would recommend for this situation?

Metashape, when I import my images manually, automatically detects the multi-camera system, and handles groupings well, even if the timestamp is different.
Is there any way of knowing Metashape grouping criterias  ?

4
That's it, I've managed (with a little help from iA) to write this script.
Here's what it looks like:

Code: [Select]
import Metashape
import os

doc = Metashape.Document()
chunk = doc.addChunk()

image_folder = r'D:\PATH\FOLDER'
files = os.listdir(image_folder)

image_groups = {}

for file in files:
    if file.endswith('_D.JPG'):
        prefix = file[:-6]
        if prefix not in image_groups:
            image_groups[prefix] = {'rgb': None, 'green': None, 'nir': None, 'red': None, 're': None}
        image_groups[prefix]['rgb'] = os.path.join(image_folder, file)
    elif file.endswith('_MS_G.TIF'):
        prefix = file[:-9]
        if prefix not in image_groups:
            image_groups[prefix] = {'rgb': None, 'green': None, 'nir': None, 'red': None, 're': None}
        image_groups[prefix]['green'] = os.path.join(image_folder, file)
    elif file.endswith('_MS_NIR.TIF'):
        prefix = file[:-11]
        if prefix not in image_groups:
            image_groups[prefix] = {'rgb': None, 'green': None, 'nir': None, 'red': None, 're': None}
        image_groups[prefix]['nir'] = os.path.join(image_folder, file)
    elif file.endswith('_MS_R.TIF'):
        prefix = file[:-9]
        if prefix not in image_groups:
            image_groups[prefix] = {'rgb': None, 'green': None, 'nir': None, 'red': None, 're': None}
        image_groups[prefix]['red'] = os.path.join(image_folder, file)
    elif file.endswith('_MS_RE.TIF'):
        prefix = file[:-10]
        if prefix not in image_groups:
            image_groups[prefix] = {'rgb': None, 'green': None, 'nir': None, 'red': None, 're': None}
        image_groups[prefix]['re'] = os.path.join(image_folder, file)

camera = []
for group in image_groups.values():
    if group['rgb']:
        camera.append([group['rgb'], group['green'], group['nir'], group['red'], group['re']])

C = len(camera)

images = [None] * sum(len(cam) for cam in camera)

index = 0
for cam_list in camera:
    for path in cam_list:
        if path:
            images[index] = path
            index += 1

filegroups = [C] * (len(images) // C)

chunk.addPhotos(filenames=images, filegroups=filegroups, layout=Metashape.MultiplaneLayout)

doc.save(r'D:\PATH\Project.psx')

Don't hesitate to let me know if you see any improvements or errors!

5
Hi everyone,

Based on the script available at this address [1], I've set up a processing chain to build Drone orthophotos using a Python script.
I'm equipped with a DJI Mavic 3M drone, and I sometimes fly multispectral missions, which generates not only RGB photos (in JPG format), but also TIF images for each spectral band (Red, RedEdge, NIR, Green).
If I import the photos manually into a Metashape project, I can define it as a multi-camera system.

However, I don't know how to code this in Python.

Does anyone have a code snippet that handles this functionality?

Or, if anyone is able to help me ‘from scratch’, here's how the Mavic 3M's photo folders are structured:
- all images are in the same input folder.
- the “rgb” images are the only ones in JPG format (filegroup = “rgb”)
- the “green” images are in TIF format and their name ends with “_MS_G.TIF” (filegroup = “green”)
- nir' images are in TIF format and their name ends with “_MS_NIR.TIF” (filegroup = “nir”)
- red' images are in TIF format and their name ends with “_MS_R.TIF” (filegroup = “red”)
- re' images are in TIF format and their name ends with “_MS_RE.TIF” (filegroup = “re”)

This is what it would look like as a Python function:
Code: [Select]
def load_images_from_folder(folder):
    images = []
    for filename in os.listdir(folder):
        if filename.endswith('.JPG'):
            images.append((os.path.join(folder, filename), 'rgb'))
        elif filename.endswith('_MS_G.TIF'):
            images.append((os.path.join(folder, filename), 'green'))
        elif filename.endswith('_MS_NIR.TIF'):
            images.append((os.path.join(folder, filename), 'nir'))
        elif filename.endswith('_MS_R.TIF'):
            images.append((os.path.join(folder, filename), 'red'))
        elif filename.endswith('_MS_RE.TIF'):
            images.append((os.path.join(folder, filename), 're'))
    return images

Thanks in advance to anyone who can help me!

Sylvain M.

[1] https://github.com/agisoft-llc/metashape-scripts/blob/master/src/samples/general_workflow.py

8
Python and Java API / Re: Disabling read-only lock when quitting Metashape?
« on: November 22, 2024, 08:22:27 PM »
Code: [Select]
del docworks fine. It closes the file and removes the lock file. You can then reopen it read-only or read-write.

Many thanks Frédéric!
It was so easy!!!
Tested and approved just now  8)
(Merci Beaucoup Frédéric, puisque je vois que tu es francophone !  ;) )

9
Rather than deleting the lock file at the end of processing (which doesn't work at the moment because the lock file is locked by the current processing), I finally added the deletion of the previous lock file at the start of new processing.
This works because previous processing is usually finished when a new Python script is executed.
Code: [Select]
# Remove Lockfile (if exist)
lockfile = project_path.replace(".psx", ".files\\lock")

try:
    os.remove(lockfile)
    print(f"lockfile deleted")
except FileNotFoundError:
    print(f"no lockfile")
except PermissionError:
    print(f"Permission denied to delete lockfile")

It works for my needs ;)

10
@Alexey Pasumansky
Related to this thread, do you think it's better to reproject when exporting the orthomosaic, or directly when building it (and therefore also reproject the DEM)?

11
Thank you Alexey!
That's pretty much what I came up with!
I can't test it right now as my workstation is locked on heavy processing, but I think it should work! (if not, I'll come back here  ;))

12
Further to this thread, I'm opening a new thread about reprojecting an Orthomosaic via Metashape's Python module.

I have an orthomosaic in WGS 84 (EPSG 4326), which I'd like to export in Lambert 93 (EPSG 2154).

I haven't found the right syntax for this kind of task.

Could you help me adapt this part of my code?

Code: [Select]
chunk.exportRaster(ortho_folder, source_data = Metashape.OrthomosaicData)
Thank you in advance for your help!  :)

13
Now I've managed to write a script that sequences the necessary tasks.
I'm sharing it here to get your feedback and possible optimizations :
(I just deleted the print() which was in French)

Code: [Select]
import Metashape
import os, sys, time

# Params
project_path =  r"D:\PATH_TO_PROJECT\Project.psx"
photos_folder = r"D:\PATH_TO_PHOTOS\Photos"
ortho_folder =  r"D:\PATH_TO_ORTHO\Orthomosaic.tif"

# Find files function
def find_files(folder, types):
    return [entry.path for entry in os.scandir(folder) if (entry.is_file() and os.path.splitext(entry.name)[1].lower() in types)]

# Create Project and Chunk
doc = Metashape.Document()
doc.save(path=project_path)
chunk = doc.addChunk()

# Photos import
photos = find_files(photos_folder, [".jpg", ".jpeg", ".tif", ".tiff"])
chunk.addPhotos(photos)
doc.save()

# match photos
chunk.matchPhotos(keypoint_limit = 40000, tiepoint_limit = 10000, generic_preselection = True, reference_preselection = True)
doc.save()

# Align cameras
chunk.alignCameras()
doc.save()

# Build Depth Maps
chunk.buildDepthMaps(downscale = 2, filter_mode = Metashape.MildFiltering)
doc.save()

# Build Model
chunk.buildModel()
doc.save()

# Build DEM
chunk.buildDem(source_data=Metashape.DepthMapsData)
doc.save()

# Build Orthomosaic
chunk.buildOrthomosaic(surface_data=Metashape.ElevationData)
doc.save()

# Export Orthomosaique (missing reprojection to EPSG 2154)
chunk.exportRaster(ortho_folder, source_data = Metashape.OrthomosaicData)
doc.save()

# Quit Metashape
Metashape.app.quit()
#exit()

Now I just need to find the syntax to convert the orthomosaic from WGS84 (EPSG 4326) to Lambert 93 (EPSG 2154).
But I think it's best to open a new topic on this precise point.

To be continued here : https://www.agisoft.com/forum/index.php?topic=16598.0

14
I'm taking my first steps with Metashape Python API, and I'm having the same problem with Locked Projects.
I've tried
Code: [Select]
Metashape.app.quit()or
Code: [Select]
os.remove(r"D:\PATH\PROJECT.files\lock")but nothing works: the project remains locked at the end of the execution of my Python scripts.

I see that the problem has been raised several times * over the years: are there any possible workarounds?

 * examples :
https://www.agisoft.com/forum/index.php?topic=16331
https://www.agisoft.com/forum/index.php?topic=9981
https://www.agisoft.com/forum/index.php?topic=12951.0
https://www.agisoft.com/forum/index.php?topic=13581

15
Feature Requests / Re: request doc.close()
« on: July 18, 2024, 05:50:37 PM »
I'm taking my first steps with Metashape Python API, and I'm having the same problem with Locked Projects.
I've tried
Code: [Select]
Metashape.app.quit()or
Code: [Select]
os.remove(r"D:\PATH\PROJECT.files\lock")but nothing works: the project remains locked at the end of the execution of my Python scripts.

I see that the problem has been raised several times * over the years: are there any possible workarounds?

 * examples :
https://www.agisoft.com/forum/index.php?topic=16331
https://www.agisoft.com/forum/index.php?topic=9981
https://www.agisoft.com/forum/index.php?topic=12951.0
https://www.agisoft.com/forum/index.php?topic=13581

Pages: [1] 2