I've been using the previously posted script with some success over the
past day or so, but I've found some shortcomings in it. Here is an updated
version.
Changes:
1. Automatically set crop parameters when the output image encompasses the
full FOV. When the crop actually includes the entire FOV, the pto does not
contain an S argument with crop information. The previous version would
break in the absence of that information.
2. Handle cylindrical projection min/max tilt correctly. This requires a
little more discussion.
Equirectangular images contain square pixels that, when viewed, are
projected into a sphere, the center of which is the default camera
position. Each pixel represents a section of the sphere with equal
delta-latitute and delta-longitude. Therefore, it is simple to calculate
the min/max tilt. The middle of the projection is the horizon. One must
simply count the number of pixels in the output image above or below this
line (projection height/2) and multiply by the FOV/pixel calculated from
the horizontal dimension.
Cylindrical projections do not preserve this equal delta-latitude and
delta-longitude condition. The projection surface is in fact a cylinder
instead of a sphere. While the horizontal field of view is easy to
calculate as a linear proportion of the 360 degree full view, the tilt is
not. One must take the width of the image (linearly scaled up to 360 degree
view if necessary) as the circumference of the cylinder. Dividing by 2pi
gives the radius of the cylinder in pixels. At this point, a triangle can
be constructed with one vertex being the center of the cylinder, the second
a point on the cylinder wall at the horizon, and the third being either the
top or bottom of the image's projection onto the cylinder vertically
aligned with the second vertex. The FOV angle of interest is between the
radius and the hypotenuse of this right triangle. Therefore, the angle can
be obtained by arctan(x/r) where x is the length of the triangle side
parallel to the cylinder wall. The length of the side x is, of course, very
important to the calculation.
The Immervision IVP Reference Guide
(http://www.immervision.com/en/multimedia/IVP_Reference_Guide/en/index.html)
provides the following formula for cylindrical FOV: max/minTilt =
+-arctan(h*pi/w)
This equation is correct for a vertically centered 360 degree HFOV
panorama, but it provides incorrect results if either of those to
assumptions is broken. In truth, the length of the side x in my equation is
dependent on the vertical centering of the panorama. It is equal to the
distance from the center of the projection to either the top or bottom of
the output image, depending on which tilt you wish to calculate, min or
max. I have mentioned that the widths of panoramas with less than 360
degree HFOV should be scaled linearly up to 360 degrees before performing
the calculation.
Accounting for partial HFOV and vertically off-center projections provides
excellent results in the viewer from my testing.
3. Added the code to output html and xml files required for display of the
panorama using PURE Player from Immersion. I've carefully reviewed the
EULA, and I don't believe that posting this code violates it. I've tested
it on full and partial equirectangular images and partial cylindrical
images with very satisfying results.
The next task is to write a companion script that will process all the
files in a directory and output a file structure with an index html file to
allow viewing of all images from one spot.
Enjoy the new version. It's been fun researching, experimenting, and
writing it.
Bill
--
--
You received this message because you are subscribed to the Google Groups
"Hugin and other free panoramic software" group.
A list of frequently asked questions is available at:
http://wiki.panotools.org/Hugin_FAQ
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at http://groups.google.com/group/hugin-ptx
---
You received this message because you are subscribed to the Google Groups
"hugin and other free panoramic software" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
#!/bin/bash
# This script pulls FOV information from a pto file to apply to an image.
# Note: the image must have been generated from the pto file. Changes to a
# pto file or resulting image's dimensions will result in inaccurate values.
# --- Pull Data from the PTO ---
# this is our test image.
#file="test.pto"
file=$1
# first grep the "p" line. Then split the data. Then grep only what
# we're interested in working with.
info=$(egrep -e '^p' $file | tr " " "\n" | egrep -e '^[fwhvS]')
# --- Parse Data from PTO ---
for item in $info
do
# the first character is the info type
mytype=${item:0:1}
# remaining characters are the value (not always 1 number)
myval=${item:1}
# pull each piece of information correctly
case $mytype in
f)
# we handle two projections for now: cylindrical(1), and
equirectangular(2)
#echo "Projection type is $myval"
case $myval in
1)
projection="Cylindrical"
;;
2)
projection="Equirectangular"
;;
esac
;;
w)
#echo "Image width is $myval"
width=$myval
;;
h)
#echo "Image height is $myval"
height=$myval
;;
v)
#echo "Image horizontal FOV is $myval"
fullFOV=$myval
;;
S)
# crop data must be parased again.
#echo "Image Crop string is $myval"
#cropParse=$(echo $myval | tr "," "\n")
OFS=$IFS
IFS=',' read -a cropParse <<< "$myval"
left=${cropParse[0]}
right=${cropParse[1]}
top=${cropParse[2]}
bottom=${cropParse[3]}
;;
esac
done
# --- Calculate Values of Interest ---
# if there is no crop information, then the whole FOV is used for the output
image
# Fill in artificial crop parameters
if [ -z $cropParse ]
then
left=0
right=$width
top=0
bottom=$height
fi
# width and height are pre-cropped values. If there's no crop, we have faked
it above
# Therefore fullFOV/width = degFOV/pixel. Use this to calculate minpan/maxpan
from center
# for both cylidrical and equirectangular
# Only calculate pans if fullFOV < 360
if [ "$fullFOV" -lt "360" ]
then
minPan=$(echo "scale=3; ($left - (($right - $left ) / 2 + $left ))*$fullFOV
/ $width" | bc -q 2>/dev/null)
maxPan=$(echo "scale=3; ($right - (($right - $left ) / 2 + $left ))*$fullFOV
/ $width" | bc -q 2>/dev/null)
fi
# if the panorama is created correctly, then the horizon of the image
# is exactly in the middle (height/2).
# Tilt angle depends on projection
case $projection in
"Cylindrical")
minTilt=$(echo "scale=3;
-180/3.14159*a(2*3.14159*${fullFOV}*(${bottom}-${height}/2)/(360*${width}))" |
bc -ql 2>/dev/null)
maxTilt=$(echo "scale=3;
180/3.14159*a(2*3.14159*${fullFOV}*(${height}/2-${top} )/(360*${width}))" |
bc -ql 2>/dev/null)
;;
"Equirectangular")
minTilt=$(echo "scale=3; (($height - $bottom ) - ($height / 2))*$fullFOV /
$width" | bc -q 2>/dev/null)
maxTilt=$(echo "scale=3; (($height - $top ) - ($height / 2))*$fullFOV /
$width" | bc -q 2>/dev/null)
;;
esac
# Only keep tilt values if they're less than +-90
if [ "0" -eq $(echo "${maxTilt} < 90" | bc -q 2>/dev/null) ]
then
unset minTilt
unset maxTilt
fi
# report on info
echo "Processed Panorama: $file"
echo "Projection: $projection"
echo "Projection Size: $width x $height"
echo "Horizontal FOV: $fullFOV"
echo "Cropping: l-$left, r-$right, t-$top, b-$bottom"
echo
if [ -n "$minPan" -o -n "$maxPan" ]
then
echo "Pan = {${minPan}:${maxPan}}"
else
echo "Pan = Full"
fi
if [ -n "$minTilt" -o -n "$maxTilt" ]
then
echo "Tilt = {${minTilt}:${maxTilt}}"
else
echo "Tilt = Full"
fi
echo
echo
# exit if no output prefix given.
[ $# -le 1 ] && exit
# ---- Generate HTML File ----
fprefix=$2
# get PurePlayer.jar path if provided, else use ../
if [ "$#" -lt "3" ]
then
playerPath="../"
else
playerPath=$3
fi
[ ${playerPath:-1} != "/" ] && playerpath="${playerPath}/"
cat > ${fprefix}.html <<EOF
<APPLET archive= "${playerPath}PurePlayer.jar" code= "PurePlayer" width= "400"
height= "300" >
<param name= "panorama" value= "${fprefix}.xml" >
<param name="optimizememory" value="true">
<param name="fullscreen" value="true">
</APPLET>
EOF
echo "Wrote HTML: ${fprefix}.html"
# ---- Generage XML File ----
# generate xml tag for projection
case $projection in
"Cylindrical")
panotag="panoCylinder"
;;
"Equirectangular")
panotag="panoSphere"
;;
esac
# modify minPan etc to contain XML text if they exist
# this lets us write all options easily to the XML in one pass
[ -n "$minPan" ] && minPan=" minPan=\"${minPan}\""
[ -n "$maxPan" ] && maxPan=" maxPan=\"${maxPan}\""
[ -n "$minTilt" ] && minTilt=" minTilt=\"${minTilt}\""
[ -n "$maxTilt" ] && maxTilt=" maxTilt=\"${maxTilt}\""
# write xml file initial part
cat > ${fprefix}.xml <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<panorama xmlns="http://www.immervision.com/panorama">
<${panotag}${minPan}${maxPan}${minTilt}${maxTilt}>
<image file="${fprefix}.jpeg"/>
</${panotag}>
EOF
# write camera limits if needed
if [ -n "$minPan" -o -n "$maxPan" -o -n "$minTilt" -o -n "$maxTilt" ]
then
cat >> ${fprefix}.xml <<EOF
<camera>
<limits${minPan}${maxPan}${minTilt}${maxTilt} />
</camera>
EOF
fi
# write closing tag
echo "</panorama>" >> ${fprefix}.xml
echo "Wrote XML: ${fprefix}.xml"