Version: [5662] SkyStitch - Drone Orthomosaic 2.3.2

2.3.0
- NEW: default 3D engine "SkyStitch - 3D Photogrammetry (local node)".
It
talks directly to a NodeODM node running on the user's own machine
(e.g. the
free WebODM native installer's node, or a standalone NodeODM): uploads
the
photos (resumable), processes locally, downloads all.zip, extracts it,
and
loads ortho/DSM/DTM/point cloud into QGIS. Each user installs the
engine
once; the plugin then runs everything from QGIS -- no Docker to
launch, no
WebODM login, no cloud. Has a Test Connection button and optional node
token.
New files: nodeodm_dialog.py, nodeodm_worker.py,
pipeline/nodeodm_client.py.
- CHANGED: menu now shows just the 2D engine and the local-node 3D
engine. The
WebODM (login) and local-Docker engines still ship but are hidden by
default
(SHOW_WEBODM_REMOTE_ENGINE / SHOW_LOCAL_DOCKER_ENGINE in
skystitch_plugin.py).

2.2.2
- CHANGED: the local Docker OpenDroneMap tool is now hidden from the
menu to
avoid Docker confusion. The menu shows just two tools: the 2D stitch
and
"3D Photogrammetry (WebODM, no Docker)". Nothing was deleted -- the
Docker
engine's files still ship and can be brought back by setting
SHOW_LOCAL_DOCKER_ENGINE = True in skystitch_plugin.py.

2.2.1
- CHANGED: WebODM is now the recommended path for 3D. Menu order is
2D -> "3D Photogrammetry (WebODM, no Docker)" -> "3D via local Docker
(advanced)", and the tools were renamed to make clear that WebODM
needs no
Docker (its server can be your own PC at http://localhost:8000 via the
free
native installer), while the local OpenDroneMap engine is the
Docker-based
advanced option. No functionality removed.

2.2.0
- NEW: third engine "SkyStitch - WebODM (remote)". It connects to a
WebODM
server (the free native Windows installer running locally, or a remote
server) over the WebODM REST API: it uploads the photos, processes on
the
server, and downloads the orthophoto, DSM, DTM, point cloud and 3D
mesh
back into QGIS -- with NO Docker needed on the user's machine. Has a
"Test Connection" button, resumable per-image upload, live
progress/log,
and cancel. New files: webodm_dialog.py, webodm_worker.py,
pipeline/webodm_client.py. Requires the Python package "requests" (a
small,
common dependency; the dialog shows a pip hint if missing).
Tip: WebODM's Windows installer (webodm.org/download) runs natively
without
Docker, so pairing it with this engine avoids Docker/WSL entirely.

2.1.0
- NEW: "Test Docker" and "Install Docker" buttons in the OpenDroneMap
(3D)
dialog (Advanced section). Test checks that the docker CLI and engine
respond; Install runs "winget install Docker.DockerDesktop" on Windows
(a UAC prompt appears; a restart is needed afterwards) or opens the
official download page on macOS/Linux. Helper logic lives in
pipeline/odm_engine.py (docker_status, docker_install_hint,
start_docker_install). Note: Docker still cannot be bundled inside the
plugin zip -- it is system software needing admin rights and a reboot
-- so
these buttons make setup easier but can't make it fully silent.

2.0.0
- NEW: optional "OpenDroneMap (3D)" engine, added as a second
tool/dialog
alongside the original 2D stitcher (the 2D engine is left unchanged).
It
drives OpenDroneMap through its official Docker image to run real
SfM/MVS
photogrammetry and produce a true (relief-corrected) orthophoto plus
DSM,
DTM/DEM, point cloud (LAZ) and textured 3D mesh (OBJ). It can
optionally
generate contour, slope and hillshade from the DSM using QGIS
Processing,
and accepts an ODM gcp_list.txt for survey-grade accuracy. This mode
needs
Docker (see README.md -> "OpenDroneMap (3D) mode"); the 2D engine's
Python
dependencies are not required for it.
New files: odm_dialog.py, odm_worker.py, pipeline/odm_engine.py,
pipeline/derivatives.py.

1.9.5
- FIX: a GCP/ICP file with more than one row pointing at the same
photo
previously had each later row silently overwrite the earlier one, so
only the last-listed point for that photo was ever used. Multiple GCP
rows matched to the same photo are now averaged into a single anchor
position for that photo (this plugin anchors whole photos, not
individual pixels within one, so averaging is the correct way to fold
several points into that model). If the matched rows disagree by more
than 2m after conversion, a [WARNING] is now logged so a mistyped
coordinate, wrong photo name, or wrong CRS gets noticed instead of
silently producing an averaged-but-wrong anchor.
- Cleaned up three bare `except: pass`/`except: continue` blocks
(flagged by static analysis) in geo_utils.py, proj_fix.py, and
skystitch_dialog.py. Behavior is unchanged (all three are genuinely
optional/best-effort paths), but failures are now logged at debug
level via the standard `logging` module instead of being silently
discarded, so a real underlying problem is still discoverable.

1.9.4
- NEW: output GeoTIFF compression is now selectable (Advanced options
->
"Output compression"): DEFLATE (previous default, lossless), ZSTD
(lossless, usually smaller/faster than DEFLATE), LZW (lossless), JPEG
(lossy, smallest files, adjustable quality 1-100%), or None
(uncompressed). JPEG mode stores the transparency mask as an internal
GDAL mask band instead of a literal 4th band, since JPEG doesn't
support an alpha channel directly; QGIS and other GDAL-aware viewers
still render it transparently the same way. The chosen setting is
remembered between sessions, same as the other advanced options.

1.9.3
- CHANGED: photos that don't overlap with the main feature-matched
group
(or, in the extreme case, when NO photo overlaps any other at all) are
no longer dropped from the output. They're still placed on the canvas
at their own GPS coordinate (using the same scale/rotation as the
fitted mosaic, since there's no matching info to refine their own
orientation), and a [NOTICE] is logged listing which photos were
placed this way -- the build no longer stops or silently skips them.
Overlap between a GPS-only-placed photo and any other photo (matched
or GPS-only) is handled the same as any other overlap (seam/blend),
not treated as an error. Previously, a fully non-overlapping photo set
raised "No photos could be matched to each other" and stopped the
whole build; individual non-overlapping photos within an otherwise
fine set were silently left out of the mosaic.

1.9.2
- CRITICAL FIX: the output .tif's 4th band was written as the alpha
channel, but only got a text label (set_band_description(4, "alpha"))
-- it was never actually tagged with GDAL's alpha color
interpretation. QGIS (and other GDAL-aware viewers) only render
nodata as transparent when a band is tagged that way; without it,
band 4 was treated as an ordinary band and the literal black (0,0,0)
pixels filling the canvas outside each photo's rotated footprint
showed up as a solid black border around the mosaic, even inside
QGIS with a basemap underneath. Band 4 is now explicitly set to
GDAL's alpha color interpretation (rasterio.enums.ColorInterp.alpha)
on save, so that area renders transparent as intended. Mosaics built
with 1.9.1 or earlier will need to be rebuilt to get the fix (existing
.tif files aren't modified retroactively).

1.9.1
- FIX: the output .tif already carries a proper alpha band, so the
area
outside the mosaic's tilted footprint shows up as transparent (not
black) in GIS software like QGIS. But the separate "_preview" image
the pipeline also saves alongside it was a plain RGB .jpg, which
dropped that alpha band entirely (and JPEG can't represent
transparency even if it hadn't) -- so that same area showed as a
solid black border whenever the preview was opened outside a GIS
viewer. The preview is now saved as "_preview.png" with the same
alpha channel as the .tif, so the border is transparent there too.

1.9.0
- NEW: optional GCP/ICP correction. A new "GCP / ICP file (optional)"
field in Advanced options accepts a CSV or Excel (.xlsx) file with
surveyed, high-accuracy coordinates for specific photos (matched by
filename). Matched photos are used as trusted anchors, refined with
an iterative (ICP-style) reweighting pass that also down-weights
GPS-derived positions that disagree strongly with the GCP-anchored
fit, correcting the whole mosaic's position/scale/rotation beyond
plain consumer-GPS accuracy. Also available on the CLI via --gcp.
Reading .xlsx files requires the optional 'openpyxl' package; .csv
needs no extra dependency. Leaving the field empty uses GPS only,
identical to previous behavior.
- The small result-preview thumbnail previously shown in the dialog
after a build finished has been removed from the UI. The preview
.jpg is still saved next to the output .tif by the pipeline; open
the layer in the QGIS canvas (or that file) to inspect the result.

1.8.0
- NEW: heads-up warning when photo GPS altitudes vary by more than 15m
across the flight, which can indicate hilly/non-flat terrain or a
mid-flight altitude change -- this plugin does not perform DEM-based
elevation correction, so the result may be misaligned in those areas.
This is a lightweight heuristic based on EXIF GPS altitude only (no
DEM/elevation data is used or required).
- All [WARNING] messages (unrecognized camera model, GPS baseline too
tight, terrain relief, ...) now also appear in the QGIS message bar
as they happen, not just in the scrolling log.

1.7.0
- NEW: optional exposure/brightness compensation step (on by default,
can be turned off via a new checkbox in Advanced options, or
--no-exposure-compensation on the CLI). Equalizes each photo's
overall brightness to the group's median before blending, so a
frame taken under a passing cloud (or with slightly different
auto-exposure) doesn't leave a visible brightness seam. Runs as a
new "[STEP 5/7]" stage; total step count increased from 6 to 7.
- The dialog now shows a rough pre-run estimate of build time and
peak RAM usage (based on photo count/resolution) next to the photo
count, before "Build Mosaic" is even clicked.
- The output CRS (previously only visible buried in the log text) is
now shown as a selectable/copyable label in the dialog once a
build finishes, alongside the final resolution.

1.6.2
- GeoTIFF output now gets internal overviews/pyramids (average
resampling) built automatically, plus horizontal-differencing
(predictor=2) and RGB-tagged compression, so large mosaics pan/zoom
smoothly in QGIS instead of being resampled on the fly every time.
- The dialog now shows which of the 6 pipeline steps is currently
running next to the progress bar, instead of only in the scrolling
log.
- The dialog now displays the small preview .jpg the pipeline already
generates, right below the progress bar, once the run finishes --
no need to open the output folder or the QGIS canvas to sanity-check
the result.
- The success message now reports the final ground sample distance
(m/px) actually used, since it can differ from the analytic GSD
estimate shown earlier in the log (e.g. after the memory-based
resolution guard in mosaic_builder.py lowers it).

1.6.1
- CRITICAL FIX: the parallel feature detection added in 1.6 used
ProcessPoolExecutor, which spawns brand-new OS processes. Inside a
plugin running in QGIS's own embedded Python, a new process's entry
point is the QGIS executable itself, not this plugin -- so every
worker "process" actually relaunched a whole new QGIS window (several
of which then crashed with "Unexpected error: A process in the
process pool was terminated abruptly..." once the pool tried to use
them as plain workers). Switched to ThreadPoolExecutor, which never
spawns a process and so cannot trigger this. Detection is still run
in parallel and still benefits from multiple CPU cores, since cv2's
heavy C++ calls (including SIFT's detectAndCompute) release Python's
GIL while running -- same mechanism numpy already relies on for
thread-based parallelism. If 1.6 caused extra QGIS windows to open on
your machine, close them and update to this version.

1.6
- Feature detection (SIFT) in build_match_graph now runs across
multiple
processes (concurrent.futures.ProcessPoolExecutor) instead of one
photo at a time, since detecting each photo's features is completely
independent of the others. On a multi-core machine this cuts Step 2's
detection time roughly in proportion to available CPU cores for
flights with dozens-to-hundreds of photos. Cancellation remains
responsive: an in-progress batch stops promptly instead of waiting for
every worker to finish. No effect on results -- same keypoints/
descriptors are produced either way, just computed in parallel.

- render_mosaic gained a new default blending mode, blend_mode="seam"
(previous behavior kept as blend_mode="feather"). The old feather mode
averaged every overlapping photo together weighted by distance to each
photo's edge; this is seamless on flat ground but any object taller
than ground level (rooftops, walls, trees) is seen in a slightly
different pixel position by each photo, so it got blended into a
semi-transparent double image ("ghosting") instead of a clean line.
The new seam mode instead finds a cut line between each pair of
overlapping photos with graph-cut seam finding
(cv2.detail.GraphCutSeamFinder, run only on the overlap's own bounding
box for speed, with an automatic downscale-then-upscale fallback for
unusually large overlaps) and hard-assigns each pixel to one photo or
the other, with only a narrow (~12px) feathered band right at the cut
line to hide pixel-level aliasing. This does not fix the underlying
parallax error on tall objects (that needs true DSM-based
orthorectification, which is out of scope for this planar-homography
pipeline) but replaces the ghosted double-exposure look with a
sharper, cleaner cut through it. Takes noticeably longer to render
than feather mode (graph-cut has a real per-overlap cost); "feather"
is still available for flat/open terrain (farmland, open fields) where
there's nothing tall to ghost and a fully seamless blend is
preferable.

1.5
- The tight-GPS-baseline fallback in fit_world_similarity (triggered
when photos are too close together to fit rotation/scale from GPS
alone) previously always assumed zero rotation ('photo up = north'),
which was only right by coincidence. It now uses the root photo's
gimbal yaw (read from DJI-style XMP metadata, previously parsed by
read_xmp_gimbal_yaw() but never actually used anywhere) as a real
orientation estimate when available, falling back to the previous
zero-rotation behavior when it isn't (e.g. camera doesn't expose
gimbal yaw). No effect on the normal (wide GPS baseline) case.

1.4
- Unrecognized camera models used to silently fall back to a guessed
sensor width (DEFAULT_SENSOR_WIDTH_MM) when estimating GSD from camera
parameters, with no indication to the user. Now flagged explicitly:
estimate_analytic_gsd() reports whether the sensor width was actually
known, and the pipeline logs a warning (once per distinct unrecognized
make/model) when this estimate is computed, since it's silently relied
on as the scale fallback whenever the GPS baseline between photos is
too tight to calibrate scale on its own.

1.3
- render_mosaic now warps each photo only into its own footprint's
bounding box on the canvas, instead of warping every photo onto the
FULL canvas every time. This was previously the biggest memory/CPU
cost in the whole pipeline for large surveys -- cost scaled with
(number of photos x full canvas size) even though each photo only
ever covers a small fraction of the final mosaic.
- Replaced the old fixed max_canvas_dim=18000px cap with a
memory-budget
based guard (default 2 GiB for the accumulator buffers): if the
canvas at the estimated GSD would need more memory than that, GSD is
automatically coarsened (same coverage area, lower resolution) instead
of risking a MemoryError/crash. A clear warning is logged when this
happens. A secondary max_canvas_dim=30000px hard cap still protects
against degenerate/very elongated canvases.

1.2
- Feature matching (SIFT) now uses each photo's GPS position to only
try
matching it against its nearest neighbors (default: 12), instead of
every other photo in the flight. This turns matching from O(n^2) into
roughly O(n x k), which matters a lot for flights with hundreds of
photos -- most distant pairs could never overlap anyway. Falls back to
matching every pair when GPS positions aren't available or the photo
count is small, so behavior/accuracy is unchanged for small flights.

yes

kholifatkhur

2026-08-03T08:23:58.279874+00:00

3.16.0

3.99.0

None

no

Version management

Plugin details