Board index FlightGear Media

Making panoramas from screenshots very easily with Hugin

Screenshots, videos, sound recording etc. taken in/with FlightGear.

Making panoramas from screenshots very easily with Hugin

Postby vnts » Fri Sep 06, 2019 1:12 pm

Experimented very quickly making a panorama recently. FG screenshot guide mentioned Hugin.

Worked out well first try :D . LOWI.

Full size:


Lower vegetation density to contrast between 2 grass types:

Full size:


Hugin: ... in-2019.0/

Hugin automatically finds adjacency between screenshots named in any order by feature recognition between overlapping areas, and compensates for any rolling, pitching, etc. of the camera between screenshots. Works even for multiple rows of screenshots AIUI (not tried).

It's almost fully automated. Just pressing a sequence of buttons in simple mode will get good results, no fiddling. Followed this guide from google: link here. Only horizontal FOV is required (pressing x/X displays tooltip with fov in FG). Menu->interface->simple to follow the guide. I did manually delete auto-matched feature points for grass and wind that were swaying between images but it worked fine even without it. Processing in the current Hugin is faster than mentioned in the old guide - much less than 30s.

Guides recommend 30%+ overlap to match features. I did between 33-50. Just pick a feature in the furthermost 1/3rd or half of the current shot and move camera. FOVs I used for these were small, 70-ish, and I used a cylindrical projection.

One issue is panoramas with external views. They orbit around craft, instead of turning the camera on the spot like in cockpit. Not sure if there's a control to turn camera regardless of view (?). Walker or video assistant is an option. The screenshot guide has a script bound to f3 for turning camera precisely, and a stitch-file to tell Hugin exactly what the camera rotations are for perfect accuracy. However, that's for creating 360 degree spherical projections, not these types of panoramic screenshots. It's probably(?) possible to add funtionality to FG UI as default for panoramas - by defining desired horizontal/vertical FOV, and no. of rows/coloumns of screenshots, and assigning a key like ctrl+f3 to save shots to a panorama folder with FOV in filename. This still requires panorama software afterwards though.

Kind regards
Posts: 285
Joined: Thu Apr 02, 2015 12:29 am

Re: Making panoramas from screenshots very easily with Hugin

Postby Johan G » Fri Sep 06, 2019 5:50 pm

You may or may not find Howto:Make full spherical panorama (perm) interesting. :wink:
Low-level flying — It's all fun and games till someone looses an engine. (Paraphrased from a YouTube video)
Improving the Dassault Mirage F1 (Wiki, Forum, GitLab. Work in slow progress)
Johan G
Posts: 6233
Joined: Fri Aug 06, 2010 5:33 pm
Location: Sweden
Callsign: SE-JG
IRC name: Johan_G
Version: 2020.3.4
OS: Windows 10, 64 bit

Re: Making panoramas from screenshots very easily with Hugin

Postby vnts » Sat Sep 07, 2019 11:24 am

I did notice that page, but that script and Hugin stitch file are for spherical projection panoramas AFAICS. They need a viewer like the one in google street-view to be viewed without severe warping like this AIUI.

On a 2nd look at the page, I found links to this thread from 2010 in the see also section. Someone worked (partially?) on a script taking a row of screenshots suitable for cylindrical projections. :mrgreen:

Kind regards
Posts: 285
Joined: Thu Apr 02, 2015 12:29 am

Re: Making panoramas from screenshots very easily with Hugin

Postby vnts » Mon Sep 09, 2019 2:30 pm

I quickly changed the script from the wiki for grids of shots for cylindrical panoramas. :)
The previous thread had enough to roughly get an idea for what settimer was doing. Assumed c/c++-ish, which I can read, for the rest. Not sure how to make camera turn in external views :?: . Walker is an option for landed craft (also allows moving view away independently of zoom).

Replace the line "<command>screen-capture</command>" in /data/keyboard.xml ..
Code (): Select all
<key n="259">
<desc>Capture screen</desc>

..with a quick cut/paste of:

v0.7 - Cylindrical panorama screenshot script

Code (): Select all
<!-- =====Start Panorama script===== -->


# Parameter constants==
var columns=3;
var rows=1;
var autosetfov=1;
# NB: All angles are in deg
var panohfov=225;
var panovfov=50;
#Horizontal and vertical overlap fraction
#e.g. 0.33 = 1/3rd overlap between screens
# on either side or above/below
var overlapfrac=0.0;
var displayaspectratio = 16/9;
#Time delay between shots - to avoid catching tool tip
var tick_time=3;
# ====================

#if ((0.0 >= panohfov)||(0.0 >= panovfov)) {print("Error: panorama field of view must be > 0");}

#read sim state to restore at end
var oldfreezemaster=((getprop("/sim/freeze/master")==1)?'true':'false');
var oldfreezeclock=((getprop("/sim/freeze/clock")==1)?'true':'false');
var oldmenuvis=((getprop("/sim/menubar/visibility")==1)?'true':'false');
var oldFOV=getprop("/sim/current-view/field-of-view");

var oldviewgho=getprop("/sim/current-view/goal-heading-offset-deg");
var oldviewho=getprop("/sim/current-view/heading-offset-deg");
var oldviewgpo=getprop("/sim/current-view/goal-pitch-offset-deg");
var oldviewpo=getprop("/sim/current-view/pitch-offset-deg");
var oldviewgro=getprop("/sim/current-view/goal-roll-offset-deg");
var oldviewro=getprop("/sim/current-view/roll-offset-deg");

var heading_deg=oldviewho;
var pitch_deg=oldviewpo;
var fov=oldFOV;

# Calc hori/vert dimensions of the 'grid'
# made from the centers of screenshots.
# It's smaller than screen FOVs because of overlap
hor_gridsize = fov/(1.0+2.0*overlapfrac);
var eq_vfov = 2.0*R2D*math.atan2(math.tan(fov*D2R/2.0)/displayaspectratio, 1.0);
var vert_gridsize = eq_vfov/(1.0+2.0*overlapfrac);

var fov=oldFOV;

if (autosetfov==1.0)
hor_gridsize = (panohfov/columns);
vert_gridsize = (panovfov/rows);
var hfov=hor_gridsize*(1.0+2.0*overlapfrac);
var vfov=vert_gridsize*(1.0+2.0*overlapfrac);
var hfoveq = 2.0*R2D*math.atan2(math.tan(vfov*D2R/2.0)*displayaspectratio, 1.0);
fov = math.max(hfov,hfoveq);
fov = math.clamp(fov, 1.0, 120.0);

#Start at center of lower left screenshot in grid of shots
var heading_offset= -((columns/2) - 0.5)*hor_gridsize;
var start_heading = heading_deg-heading_offset;
pitch_offset = -((rows/2) - 0.5)*vert_gridsize;
var start_pitch = pitch_deg+pitch_offset;

var headingdelta = hor_gridsize;
var pitchdelta = vert_gridsize;

setprop("/sim/screenshots/panoramas/prevstatebackup/visibility", oldmenuvis);
setprop("/sim/screenshots/panoramas/prevstatebackup/field-of-view", oldFOV);
setprop("/sim/screenshots/panoramas/prevstatebackup/goal-heading-offset-deg", heading_deg);
setprop("/sim/screenshots/panoramas/prevstatebackup/heading-offset-deg", oldviewho);
setprop("/sim/screenshots/panoramas/prevstatebackup/goal-pitch-offset-deg", pitch_deg);
setprop("/sim/screenshots/panoramas/prevstatebackup/pitch-offset-deg", oldviewpo);
setprop("/sim/screenshots/panoramas/prevstatebackup/goal-roll-offset-deg", oldviewro);
setprop("/sim/screenshots/panoramas/prevstatebackup/roll-offset-deg", oldviewro);
setprop("/sim/screenshots/panoramas/prevstatebackup/master", oldfreezemaster);
setprop("/sim/screenshots/panoramas/prevstatebackup/clock", oldfreezeclock);

var restoreoldstate = func()
setprop("/sim/menubar/visibility", getprop("/sim/screenshots/panoramas/prevstatebackup/visibility") );
setprop("/sim/current-view/field-of-view", getprop("/sim/screenshots/panoramas/prevstatebackup/field-of-view"));
setprop("/sim/current-view/goal-heading-offset-deg", getprop("/sim/screenshots/panoramas/prevstatebackup/goal-heading-offset-deg"));
setprop("/sim/current-view/heading-offset-deg", getprop("/sim/screenshots/panoramas/prevstatebackup/heading-offset-deg"));
setprop("/sim/current-view/goal-pitch-offset-deg", getprop("/sim/screenshots/panoramas/prevstatebackup/goal-pitch-offset-deg"));
setprop("/sim/current-view/pitch-offset-deg", getprop("/sim/screenshots/panoramas/prevstatebackup/pitch-offset-deg"));
setprop("/sim/current-view/goal-roll-offset-deg", getprop("/sim/screenshots/panoramas/prevstatebackup/goal-roll-offset-deg"));
setprop("/sim/current-view/roll-offset-deg", getprop("/sim/screenshots/panoramas/prevstatebackup/roll-offset-deg"));
setprop("/sim/freeze/master", getprop("/sim/screenshots/panoramas/prevstatebackup/master"));
setprop("/sim/freeze/clock", getprop("/sim/screenshots/panoramas/prevstatebackup/clock"));

setprop("/sim/menubar/visibility", 'false');
setprop("/sim/current-view/field-of-view", fov);

var rotatescreen = func(heading_deg, pitch_deg)
setprop("/sim/current-view/goal-heading-offset-deg", heading_deg);
setprop("/sim/current-view/heading-offset-deg", heading_deg);
setprop("/sim/current-view/goal-pitch-offset-deg", pitch_deg);
setprop("/sim/current-view/pitch-offset-deg", pitch_deg);

var takescreen = func(heading_deg, pitch_deg)
print ("Taking screen with heading= ", heading_deg, " and pitch= ", pitch_deg);
var success = fgcommand("screen-capture");
if (success)
print ("screen taken with heading= ", heading_deg, " and pitch= ", pitch_deg);
print("Error: screen not taken");

var scr_ticks = func(i, columns, rows, start_heading, start_pitch, headingdelta, pitchdelta)
#Start from lower left corner (i=0) and
#work right through each row. Do rows from bottom to top
#i: from 0 to (num shots-1)
var numscreens=columns*rows;
var r=math.floor(i/columns);
var c=i-(r*columns);
var h=start_heading-(headingdelta*c);
var p=start_pitch+(pitchdelta*r);

if (numscreens > i)
rotatescreen(h, p);
takescreen(h, p);
settimer(func {var success = scr_ticks(i, columns, rows, start_heading, start_pitch, headingdelta, pitchdelta); }, 5.0, 5.0);

print("Taking ", (rows*columns), " screens of h-FOV=", fov, " deg each for a Panorama centered on current view. Grid: ", rows, " rows, ", columns, " columns.");
scr_ticks(0, columns, rows, start_heading, start_pitch, headingdelta, pitchdelta);


<!-- =====End panorama script===== -->

I tried to match the sytle of variable naming etc. as closely as possible to the wiki example. It seems to work fine, quick test only.

Possibilities: It would be ideal to have panoramas assigned to ctrl+f3, and turn off FPS counter (not done). Maybe f3 should turn off menu, FPS, frame spacing before shot and then restore. alt+f3 could be used for when trying to capture peroformance in shots. It's possible to modify it to set the parameters via a UI&property tree.
It's theoretically possible to save each set of shots to a separate folder, and maybe(?) even output a custom Hugin stitch file for a perfect stitch.

Panorama is centered around current center of view. The script takes screenshots with centers arranged in a grid.
- Changeable parameters are separated at top
- Overlap fraction roughly determines horizontal/vertical overlap between images. Guides recommend 0.3-0.5 for feature recognition. e.g. Overlap=0.5 means screenshot FOV will be 2x distance between grid centers to get 50% overlap either side. Overlap=0 can be used with a custom stitch file.
- autoSetFov=1: panoHFov&PanoVFov in degrees set length and height of grid formed from centers of screens. Fov per screenshot is auto-calculated to be large enough to overlap horizontally&vertically.
- autoSetFov=0: Use current views' h-FOV and v-Fov. A grid is created with dimensions from current zoom. Increasing overlap shrinks the grid size.
- Columns=3, rows=1: Row of 3 screens. Columns=3,rows=3 grid of 9 screenshots. [1,1]&0 overlap=normal screenshot. Odd number = 1 screenshot centered around interest pointed by view. Hugin will handle the rest.
- View and zoom are restored after use. Tick_time changes pause between shots.

Kind regards
Posts: 285
Joined: Thu Apr 02, 2015 12:29 am

Re: Making panoramas from screenshots very easily with Hugin

Postby GinGin » Mon Sep 09, 2019 4:51 pm

Very interesting, I will give it a try :)
Pictures above are breath taking.

Try your chance in the SOTM
Posts: 1363
Joined: Wed Jul 05, 2017 10:41 am
Location: Paris
Callsign: Gingin

Re: Making panoramas from screenshots very easily with Hugin

Postby vnts » Wed Sep 11, 2019 1:52 pm

It's a bit harsh to compare normal screens to panoramas :?: . I think panoramas would count as post-processed for SOTM, in terms of altering by projection.

Maybe there should be a panorama version of SOTM every 6 months, or when ever there's 15 submissions? :D

I realised it's possible to just move the cockpit camera out of the aircraft to take panoramas (ctrl+RMB).
Posts: 285
Joined: Thu Apr 02, 2015 12:29 am

Re: Making panoramas from screenshots very easily with Hugin

Postby legoboyvdlp » Wed Sep 11, 2019 8:35 pm

Technically it is composed of unprocessed source images if I understood it right; regardless perhaps it would be somewhat unfair in competition so might be best not to include them ;)
User avatar
Posts: 7859
Joined: Sat Jul 26, 2014 1:28 am
Location: Northern Ireland
Callsign: G-LEGO
Version: next
OS: Windows 10 HP

Re: Making panoramas from screenshots very easily with Hugin

Postby wkitty42 » Thu Sep 12, 2019 3:31 am

"You get more air close to the ground," said Angalo. "I read that in a book. You get lots of air low down, and not much when you go up."
"Why not?" said Gurder.
"Dunno. It's frightened of heights, I guess."
User avatar
Posts: 7051
Joined: Fri Feb 20, 2015 3:46 pm
Location: central NC, USA
Callsign: wk42
Version: git next
OS: Kubuntu 20.04

Return to Media

Who is online

Users browsing this forum: No registered users and 0 guests