238 lines
13 KiB
HTML
238 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>FireMapper — Session Post-Processing — User Manual</title>
|
||
<style>
|
||
:root{
|
||
--fg:#1c2230; --muted:#5b6473; --bg:#f6f7f9; --card:#ffffff; --line:#e2e6ec;
|
||
--accent:#1565c0; --accent2:#e65100; --code:#0f172a; --codebg:#f0f2f5;
|
||
}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;background:var(--bg);color:var(--fg);
|
||
font:16px/1.6 "Segoe UI",system-ui,Arial,sans-serif}
|
||
header{background:linear-gradient(135deg,#1565c0,#e65100);color:#fff;padding:34px 24px}
|
||
header .wrap{max-width:900px;margin:0 auto}
|
||
header h1{margin:0 0 6px;font-size:28px}
|
||
header p{margin:0;opacity:.92}
|
||
main{max-width:900px;margin:0 auto;padding:24px}
|
||
nav{background:var(--card);border:1px solid var(--line);border-radius:10px;
|
||
padding:14px 18px;margin:22px 0}
|
||
nav b{display:block;color:var(--muted);font-size:13px;text-transform:uppercase;
|
||
letter-spacing:.04em;margin-bottom:6px}
|
||
nav a{color:var(--accent);text-decoration:none;margin-right:16px;white-space:nowrap}
|
||
nav a:hover{text-decoration:underline}
|
||
section{background:var(--card);border:1px solid var(--line);border-radius:12px;
|
||
padding:20px 24px;margin:18px 0}
|
||
h2{margin:.2em 0 .6em;font-size:22px;border-bottom:2px solid var(--line);padding-bottom:8px}
|
||
h3{margin:1.2em 0 .4em;font-size:17px;color:var(--accent)}
|
||
code,kbd{background:var(--codebg);border-radius:4px;padding:1px 6px;font-family:Consolas,monospace;font-size:.92em}
|
||
pre{background:var(--code);color:#e6edf3;border-radius:8px;padding:14px 16px;overflow:auto;font-size:13px}
|
||
pre code{background:none;color:inherit;padding:0}
|
||
ol,ul{padding-left:22px}
|
||
li{margin:4px 0}
|
||
table{border-collapse:collapse;width:100%;margin:10px 0;font-size:14.5px}
|
||
th,td{border:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:top}
|
||
th{background:var(--codebg)}
|
||
.pill{display:inline-block;border-radius:999px;padding:1px 10px;font-size:13px;font-weight:600;color:#fff}
|
||
.blue{background:var(--accent)} .orange{background:var(--accent2)}
|
||
.note{border-left:4px solid var(--accent);background:#eef4fc;padding:10px 14px;border-radius:6px;margin:12px 0}
|
||
.warn{border-left:4px solid var(--accent2);background:#fdf0e6;padding:10px 14px;border-radius:6px;margin:12px 0}
|
||
.muted{color:var(--muted)}
|
||
footer{max-width:900px;margin:0 auto;padding:10px 24px 40px;color:var(--muted);font-size:13px}
|
||
.step{counter-reset:none}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header><div class="wrap">
|
||
<h1>FireMapper — Session Post-Processing</h1>
|
||
<p>User manual for converting a raw capture session into mapping-ready imagery.</p>
|
||
</div></header>
|
||
|
||
<main>
|
||
<p>For every image it captures, FireMapper writes a small <code>.json</code> file alongside
|
||
it containing the GPS position, platform/IMU attitude, lens parameters and timing. These
|
||
tools read those files and:</p>
|
||
<ul>
|
||
<li><b>Embed</b> the data into each image's EXIF/XMP so that mapping software can position
|
||
it geographically;</li>
|
||
<li><b>Stretch</b> the low-contrast 16-bit thermal frames into clear colour images;</li>
|
||
<li><b>Map</b> the trigger points and image footprints on OpenStreetMap.</li>
|
||
</ul>
|
||
<p class="note"><b>Original files are never modified.</b> Every step writes its results to a
|
||
new folder.</p>
|
||
|
||
<nav>
|
||
<b>Contents</b>
|
||
<a href="#start">Getting started</a>
|
||
<a href="#embed">1 · Embed</a>
|
||
<a href="#thermal">2 · Thermal</a>
|
||
<a href="#map">3 · Map</a>
|
||
<a href="#cli">Command line</a>
|
||
<a href="#trouble">Troubleshooting</a>
|
||
</nav>
|
||
|
||
<section id="start">
|
||
<h2>Getting started</h2>
|
||
<ol>
|
||
<li>Ensure Python 3 is installed, together with the required packages:
|
||
<pre><code>pip install Pillow numpy tkintermapview requests</code></pre></li>
|
||
<li>Place the four scripts (<code>firemapper_gui.py</code>, <code>embed_metadata.py</code>,
|
||
<code>stretch_thermal.py</code>, <code>session_map.py</code>) in a single folder.
|
||
Locating them alongside your session folders (or alongside a parent folder that groups
|
||
several flight strips) allows the program to detect your data automatically.</li>
|
||
<li>Start the application:
|
||
<pre><code>python firemapper_gui.py</code></pre>
|
||
A window opens with three tabs. Select a tab, review the on-screen description, set the
|
||
options, and start the operation.</li>
|
||
</ol>
|
||
<p class="muted">The program detects session folders automatically: any folder containing a
|
||
<code>thermal/</code> directory, a <code>cam…/</code> directory, or a
|
||
<code>manifest.json</code> file is treated as a session. Grouping folders (for example
|
||
<code>Streifen/</code>) are also scanned one level deep.</p>
|
||
</section>
|
||
|
||
<section id="embed">
|
||
<h2><span class="pill blue">Tab 1</span> Embed GPS & Metadata</h2>
|
||
<p>Writes each image's JSON data into a tagged <b>copy</b> of the file: GPS position,
|
||
true camera pointing direction, capture time (UTC), lens and exposure, and the complete
|
||
JSON record in the comment field. The copies are compatible with Pix4D, Metashape, QGIS
|
||
and similar applications.</p>
|
||
<h3>How to use</h3>
|
||
<ol>
|
||
<li><b>Session folder</b> — pre-filled with the most recent session, or selected via
|
||
<kbd>Browse…</kbd>.</li>
|
||
<li><b>Output folder</b> — if left blank, results are written to
|
||
<code><session>_exif</code> beside the session.</li>
|
||
<li><b>GPS source</b> — <i>Fused INS (position)</i> provides the highest accuracy
|
||
(recommended); <i>Raw GNSS (gps)</i> uses the uncorrected satellite fix. Thermal
|
||
frames always use raw GNSS.</li>
|
||
<li><b>Parallel workers</b> — the number of images copied and tagged concurrently
|
||
(default: derived from the available CPU cores). Higher values are faster on
|
||
SSD/NVMe storage.</li>
|
||
<li>Click <b>Embed metadata</b>. A progress bar and log are displayed; on completion the
|
||
output folder can be opened.</li>
|
||
</ol>
|
||
<p class="muted">Requires <b>exiftool</b>, which is located automatically and installed on
|
||
first use if it is not already present (Windows). Leave the “exiftool path” field blank.</p>
|
||
</section>
|
||
|
||
<section id="thermal">
|
||
<h2><span class="pill orange">Tab 2</span> Thermal Stretch</h2>
|
||
<p>The thermal camera records 16-bit radiometric frames that occupy only a small portion
|
||
of the available range; consequently the raw files appear nearly black and vary in
|
||
brightness from frame to frame. This step determines <b>a single brightness window for the
|
||
entire session</b> and rescales every frame into it, producing viewable 8-bit images.
|
||
Because all frames share one window, warm and cool areas remain consistent across the
|
||
flight.</p>
|
||
<div class="note">Pixel values represent radiometric <b>signal</b> (proportional to
|
||
temperature), not calibrated degrees.</div>
|
||
<h3>Options</h3>
|
||
<table>
|
||
<tr><th>Palette</th><td><b>inferno</b> / <b>ironbow</b> — thermal colour maps (dark = cool,
|
||
bright = hot); <b>gray</b> — plain grayscale.</td></tr>
|
||
<tr><th>Window low % / high %</th><td>The brightness window, expressed as percentiles
|
||
pooled across the whole session (default 1–99 %). Decrease the high % or increase the
|
||
low % for greater contrast; enable <b>Absolute min/max</b> to use the true extremes.</td></tr>
|
||
<tr><th>Embed metadata</th><td>When enabled (default), the stretched PNGs also receive
|
||
GPS and orientation EXIF, making them self-contained.</td></tr>
|
||
<tr><th>Parallel workers</th><td>The number of frames rendered and tagged concurrently
|
||
(default: derived from the available CPU cores). Higher values are faster on SSD/NVMe
|
||
storage.</td></tr>
|
||
</table>
|
||
<h3>How to use</h3>
|
||
<ol>
|
||
<li>Select the <b>session folder</b> and <b>palette</b>.</li>
|
||
<li>Click <b>Preview palettes</b> to compare gray, inferno and ironbow on a sample
|
||
frame before processing.</li>
|
||
<li>Click <b>Stretch all frames</b>. Output is written to
|
||
<code><session>/thermal_stretched</code> unless an output folder is specified.</li>
|
||
</ol>
|
||
</section>
|
||
|
||
<section id="map">
|
||
<h2><span class="pill blue">Tab 3</span> Map</h2>
|
||
<p>Plots each image as a <b>trigger point</b> (its GPS position) and, optionally, as an
|
||
<b>oblique footprint</b> — the area of ground the image covers — on OpenStreetMap.
|
||
Cameras are shown in <span class="pill blue">blue</span>, thermal in
|
||
<span class="pill orange">orange</span>.</p>
|
||
<h3>How to use</h3>
|
||
<ol>
|
||
<li><b>Select the sessions</b> to display (use <kbd>All</kbd>/<kbd>None</kbd>;
|
||
<kbd>Refresh</kbd> rescans the folder). Several flight strips may be shown together.</li>
|
||
<li>Set the <b>options</b> (described below), then click <b>Show on map</b>. The map
|
||
zooms to fit the data.</li>
|
||
<li>For a complete view, click <b>Open full map in browser</b>. This renders
|
||
<b>every</b> trigger point and footprint (the in-app map thins dense flights for
|
||
speed) on an interactive page in your web browser, with marker clustering and a
|
||
panel to toggle individual sessions on and off and to switch each camera's footprints
|
||
separately (cam25, cam45 and thermal each have their own colour). Click any point or
|
||
footprint to see its metadata.</li>
|
||
</ol>
|
||
<h3>Options</h3>
|
||
<table>
|
||
<tr><th>Ground elev (m)</th><td>Terrain height used to project the footprints (flat-ground
|
||
assumption). Default 110 m; set this to the elevation of your site.</td></tr>
|
||
<tr><th>Thermal FOV H/V</th><td>Field of view of the thermal camera, required for its
|
||
footprints. Pre-filled for the FLIR A65 25° lens (25° × 20°).</td></tr>
|
||
<tr><th>Thermal off-nadir</th><td>Mounting tilt of the thermal camera (0 = straight down).</td></tr>
|
||
<tr><th>Plot every Nth</th><td>Applies to the in-app <b>Show on map</b> only: <i>auto</i>
|
||
reduces very large surveys so the embedded map remains responsive; enter a number to
|
||
override. <b>Open full map in browser</b> ignores this and shows everything.</td></tr>
|
||
<tr><th>Trigger points / Footprints / Cameras / Thermal</th><td>Control which layers are drawn.</td></tr>
|
||
</table>
|
||
<div class="note"><b>Needs an internet connection</b> for the map tiles.</div>
|
||
<h3>How footprints are computed</h3>
|
||
<p>The footprint is projected from the true camera pointing direction: the aircraft
|
||
attitude, plus the platform's left/right swivel (<code>platform_angle_deg</code>), plus
|
||
each camera's fixed mounting — thermal looks straight down (mounted vertical to the
|
||
flight line); the RGB cameras look forward-and-down by their off-nadir angle
|
||
(cam25 = 25°, cam45 = 45°) and are mounted landscape across the flight line.</p>
|
||
<div class="warn">The footprints assume flat ground at the elevation you specify. If the
|
||
entire swath appears on the <b>wrong side</b> of the flight line relative to reality, flip
|
||
the <code>SCAN_SIGN</code> setting at the top of <code>session_map.py</code>.</div>
|
||
</section>
|
||
|
||
<section id="cli">
|
||
<h2>Command line (optional)</h2>
|
||
<p>The embed and stretch steps may also be run without the graphical interface:</p>
|
||
<pre><code>python embed_metadata.py [SESSION] [--out DIR] [--gps-source position|gps]
|
||
[--workers N] [--dry-run]
|
||
|
||
python stretch_thermal.py [SESSION] [--colormap inferno|ironbow|gray]
|
||
[--lo-pct 1 --hi-pct 99 | --absolute | --lo N --hi N]
|
||
[--out DIR] [--no-embed-exif] [--workers N] [--sample]</code></pre>
|
||
<p class="muted">If <code>SESSION</code> is omitted, the most recent session found nearby is
|
||
used. <code>--workers</code> sets the number of parallel workers (default: derived from the
|
||
CPU core count).</p>
|
||
</section>
|
||
|
||
<section id="trouble">
|
||
<h2>Troubleshooting</h2>
|
||
<table>
|
||
<tr><th>“exiftool could not be found”</th>
|
||
<td>Install it once: <code>winget install OliverBetz.ExifTool</code> (or
|
||
<code>choco install exiftool</code>), then restart the application.</td></tr>
|
||
<tr><th>Stretched or embedded images have no GPS</th>
|
||
<td>exiftool writes metadata via a temporary copy, so a full (or nearly full) disk can
|
||
drop the GPS tag. Free up disk space and run the step again. The tools verify that GPS
|
||
was written and report a clear error rather than producing files without it.</td></tr>
|
||
<tr><th>Map is blank / tiles don't load</th>
|
||
<td>The map needs internet access for OpenStreetMap tiles.</td></tr>
|
||
<tr><th>No sessions in the Map list</th>
|
||
<td>Click <kbd>Refresh</kbd>. Make sure the scripts are in (or next to) the folder
|
||
that holds your session folders.</td></tr>
|
||
<tr><th>Footprints / headings look mirrored</th>
|
||
<td>Flip <code>SCAN_SIGN</code> at the top of <code>session_map.py</code>.</td></tr>
|
||
<tr><th>Thermal footprints missing</th>
|
||
<td>Fill in the <b>Thermal FOV</b> fields on the Map tab.</td></tr>
|
||
</table>
|
||
</section>
|
||
</main>
|
||
|
||
<footer>FireMapper — Session Post-Processing · GGS Speyer. Original files are never modified;
|
||
all results are written to new folders.</footer>
|
||
</body>
|
||
</html>
|