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"

Reply via email to