mpetroff / pannellum

Pannellum is a lightweight, free, and open source panorama viewer for the web.
https://pannellum.org/
MIT License
4.22k stars 717 forks source link

Cylindrical images as input #145

Closed DDvO closed 8 years ago

DDvO commented 8 years ago

I like the approach and implementation of this viewer very much, and I'd like to use it heavily for my photo gallery, but there is a blocking point: most my source images are not in equirectangular, but in cylindrical projection.

While several other pano viewers also accept cylindrical projection, this one does not, and AFAICS this is a matter of efficiency and also of development effort. Can you recommend any tool (ideally one that can be used in batch mode with all necessary parameters like field of view given as command line options) that can be used to transform JPEG images to equirectangular projection such that they can be used with Pannellum?

mpetroff commented 8 years ago

I don't know of any tools to do this out of the box, but it's fairly easy to do with Hugin's command line tools. There following set of commands should do the conversion, where 200 is the horizontal field of view:

pto_gen -p 1 -f 200 -o project.pto image.jpg
pano_modify --fov=AUTO --canvas=AUTO --crop=AUTO -o project.pto project.pto
nona -o image_equirectangular.jpg -m JPEG project.pto

It wouldn't be difficult to adapt this set of commands into a script.

DDvO commented 8 years ago

Thanks a lot for your swift and very helpful response - this is pretty much what I was looking for!

I should get a newer version of nona (which so far I can't find for Mac OS X) because the one of 2014 does not accept the -m JPEG option (and the related interpretation of the -z <percent> option). Thus my bash script currently looks like this:

base=`base $image`
pto_gen     >/dev/null -p 1 -f $hfov -o $base.pto $image 
pano_modify >/dev/null --fov=AUTO --canvas=AUTO --crop=AUTO -o $base.pto $base.pto
nona -o /tmp/${base}_equi $base.pto && #-m JPEG -z 80
convert -quality 80 /tmp/${base}_equi0000.tif ${base}_equi.jpeg

One question remains to me here. Do I need to handle the case that the horizon in the cylindrical input is not in the middle, but typically at around 40% of the image height (calculated from above), and if so, how to do this? If I try compensating to this giving --rotate 0,$pitch,0 to pano_modify, the horizon gets wrongly curved. If I try setting any of the x, y, or z values in --translate x,y,z, things go pretty wrong. If I instead simply give the pitch as the vOffset parameter to the viewer in the .html file, everything seems fine. Still it does not feel right because in the .pto file the shift of the horizon is not reflected at all, neither as an angle nor as an offset (e.g., in the crop data).

BTW, I do the image parameter calculation in a Perl script as follows.

if (`exiftool $image` =~ m/\nMake\s*: SONY.*?\nOrientation\s*: (.*)\nPanorama Frame Width\s*: (.*)\nPanorama Frame Height\s*: (.*)\nScene Mode\s*: Sweep Panorama.*?\n.*?\nImage Width\s*: (\d+).*?Image Height\s*: (\d+).*?Field Of View\s*: ([\d\.]+) deg.*?Focal Length\s*: [\d\.]+ mm \(35 mm equivalent: ([\d\.]+) mm\)/s) {
      print "Sony wide-angle ($7 mm) cylindrical Sweep Panorama"; 
      $projection = "cylindrical";
      $vertical = $2 != 800;   # || $2 == 3056 || $3 != 3056 || $3 == 512
      $width=$4;
      $height=$5;
      $vfov=$6;
      $horizon = 0.40; #default
      $vfov=$vfov*2/3 unless $vertical;
      $radius=$height/2/tan($vfov/2*pi/180);
      $hfov=$width/$radius*180/pi;
      $mintilt=(0-$horizon)*$vfov;
      $maxtilt=(1-$horizon)*$vfov;
      $pitch=-($maxtilt+$mintilt)/2;
}
mpetroff commented 8 years ago

To change the pitch of the image, you'll want to use pto_var, not pano_modify (pto_var modifies input image parameters, while pano_modify modifies the output parameters). Something along the lines of this should work: pto_var --set p0=20 -o project.pto project.pto.

DDvO commented 8 years ago

I tried pto_var --set p0=$pitch. It turns out that it has the same effect as pano_modify --rotate 0,$pitch,0. That is, as far as the pitch is concerned, both these commands modify the input image parameters. I wonder if this is intended.

Thus if I use any of these commands before invoking nona, the horizon (which was straight in the input image) gets bent. Instead, I now do the pitch modification after invoking nona. Thus the output image looks fine (although the elevated horizon is not taken into account for rendering, except that I set vOffset to -$pitch) and the horizon information is recorded (as the 'p0' parameter of the line starting with 'i') in the .pto file. Yet I'm not sure if this is the real solution.

DDvO commented 6 years ago

Meanwhile, after quite some trial and error (it's a shame that whoever invented or maintains the PTO file format does not provide online documentation for it; at least I searched for it several times with no results),

I managed to find the right way to do the transformation of images with cylindrical projection and a non-zero pitch: the key is the e parameter for the input file lines of the .pto file. This is usually 0, but should be the offset of the horizon in pixels (negative if the horizon is above the middle).

I adapted the generate.py script to take this value as an extra parameter, which I calculate in the Perl script calling it as follows (where -$mintilt is the field of view of the cylindrical image above the horizon:

$radius =  $width*360/$hfov/2/pi;
$horizon = int($spherical ? $tilt/$hfov * $width
                          : tan($mintilt*pi/180) * $radius + $height/2);

Here is an example partial cylindrical pano generated this way.

DDvO commented 6 years ago

The described solution is meanwhile implemented in PR #570.