Hi Robert,
I was working with stereo photography a few weeks back and needed a program
to view the stereo pairs. I wrote this very quick app to serve the purpose.
I realise that there is already a stereo image viewer example but this is a
more complete example. It resizes images to fit the screen and doesn't have
the geometrical distortion found in the osgStereoImage example.
Given that OSG is used so widely for stereo rendering I thought it might be
a useful addition to the OSG source both as an example and a utility.
Let me know what you think,
Regards,
Kim.
/* OpenSceneGraph example, osgStereoImageViewer.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// This code is originally Copyright (c) 2011 Kim Bale but can be used under the
// terms described in the OSGPL.
// This example is designed to illustrate how to use the OSG as a means of displaying
// stereo images. The program accepts stereo pairs both as individual image files
// (one for the left eye and one for right) and as a single image containing side by side
// stereo pairs.
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Group>
#include <osg/Camera>
#include <osg/TextureRectangle>
#include <osg/Geometry>
#include <osg/Image>
#include <osgDB/ReadFile>
class ResizeHandler: public osgGA::GUIEventHandler
{
public:
ResizeHandler(osg::Camera* camera, bool stretchToFill)
:_camera(camera)
,_stretchToFill(stretchToFill)
,_screenWidth(0)
,_screenHeight(0)
,_imageRatio(0)
,_imgWidth(0)
,_imgHeight(0)
{};
virtual bool handle(
const osgGA::GUIEventAdapter& gea,
osgGA::GUIActionAdapter& gaa,
osg::Object* obj,
osg::NodeVisitor* nv
)
{
osgGA::GUIEventAdapter::EventType ev = gea.getEventType();
if(ev != osgGA::GUIEventAdapter::RESIZE) return false;
_screenWidth = gea.getWindowWidth();
_screenHeight = gea.getWindowHeight();
resize();
return true;
};
void setImageDims( unsigned imgWidth, unsigned imgHeight ){
_imgWidth = imgWidth;
_imgHeight = imgHeight;
_imageRatio = (float)_imgHeight / (float)_imgWidth;
resize();
};
void resize(void)
{
unsigned quadWidth = 0;
unsigned quadHeight = 0;
if(_stretchToFill)
{
quadWidth = _screenWidth;
quadHeight = _screenHeight;
}
else if(_imgWidth > _imgHeight)
{
if( _screenWidth*_imageRatio > _screenHeight ){
quadHeight = _screenHeight;
quadWidth = quadHeight/_imageRatio;
}
else{
quadWidth = _screenWidth;
quadHeight = quadWidth*_imageRatio;
}
}
else{
if(_screenHeight/_imageRatio > _screenWidth){
quadWidth = _screenWidth;
quadHeight = quadWidth*_imageRatio;
}
else{
quadHeight = _screenHeight;
quadWidth = quadHeight/_imageRatio;
}
}
unsigned xOffset = (_screenWidth - quadWidth)/2;
unsigned yOffset = (_screenHeight - quadHeight)/2;
_camera->setProjectionMatrixAsOrtho(0,_imgWidth,0,_imgHeight,1,1000);
_camera->setViewport(xOffset,yOffset,quadWidth,quadHeight);
};
protected:
osg::ref_ptr<osg::Camera> _camera;
bool _stretchToFill;
unsigned _imgWidth;
unsigned _imgHeight;
unsigned _screenWidth;
unsigned _screenHeight;
float _imageRatio;
};
#define LEFT_EYE_MASK 0x1
#define RIGHT_EYE_MASK 0x2
enum ImageFormat{ Single, PairLeft, PairRight };
osg::Geode* createImagePlane( osg::TextureRectangle* texture, ImageFormat format )
{
int s = texture->getTextureWidth();
int t = texture->getTextureHeight();
int left,right,bottom,top;
if(format==Single){
left = 0;
right = s;
}
else if(format==PairLeft){
left = s/2;
right = s;
}
else if(format==PairRight){
left = 0;
right = s/2;
}
bottom = 0;
top = t;
osg::Geometry* planeGeometry = osg::createTexturedQuadGeometry(osg::Vec3(0,0,-1),osg::Vec3( (format==Single?s:s/2),0,-1),osg::Vec3(0,t,-1),left,bottom,right,top);
osg::Geode* planeGeode = new osg::Geode;
planeGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture);
planeGeode->addDrawable(planeGeometry);
return planeGeode;
}
void printUsage(void)
{
OSG_NOTICE << std::endl;
OSG_NOTICE << "Usage: osgStereoImageViewer [options] filename\n" <<
"Options:\n"
" --left Path to the left eye image\n" <<
" --right Path to the right eye image\n" <<
" --pair Path to a horizontal split stereo image e.g. jps" <<
" --fullscreen View the image full screen" <<
" --stretch Disregard image aspect ratio and stretch image to fill screen\n" <<
" --stereo <mode> Set the stereo mode \n" <<
" ANAGLYPHIC | QUAD_BUFFER |\n" <<
" HORIZONTAL_SPLIT | VERTICAL_SPLIT |\n" <<
" LEFT_EYE | RIGHT_EYE |\n" <<
" HORIZONTAL_INTERLACE |\n" <<
" VERTICAL_INTERLACE | CHECKERBOARD |\n" <<
" ON | OFF\n";
OSG_NOTICE << "\nExample: osgStereoImageViewer --left leftImg.jpg --right rightImg.jpg" << std::endl;
OSG_NOTICE << std::endl;
}
int main(int argc, char* argv[])
{
osg::ArgumentParser args(&argc,argv);
osg::DisplaySettings::instance()->readCommandLine(args);
osg::DisplaySettings::instance()->setEyeSeparation(0.0f);
std::string filePair;
while( args.read("--pair", filePair)){};
std::string fileLeft;
while( args.read("--left", fileLeft) ){};
std::string fileRight;
while( args.read("--right", fileRight) ){};
if( args.read("--help") || args.read("-h") )
{
printUsage();
return 1;
}
if( filePair.empty() && (fileLeft.empty() || fileRight.empty()))
{
OSG_NOTICE << "ERROR: Please specify a left and a right eye image" << std::endl;
printUsage();
return 1;
}
bool stretchImage = args.find("--stretch") != -1;
OSG_NOTICE << "Stretch Image: " << (stretchImage?"ON":"OFF") << std::endl;
unsigned screenWidth = 1024;
unsigned screenHeight = 768;
bool isFullScreen = args.find("--fullscreen") != -1;
OSG_NOTICE << "Fullscreen: " << (isFullScreen?"ON":"OFF") << std::endl;
if(isFullScreen)
{
// Get the screen resolution
osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
if (!wsi){
osg::notify(osg::NOTICE)<<"ERROR: no WindowSystemInterface available, cannot create windows."<<std::endl;
return 1;
}
osg::GraphicsContext::ScreenIdentifier si;
si.readDISPLAY();
wsi->getScreenResolution( si, screenWidth, screenHeight);
}
unsigned imgWidth, imgHeight;
osg::Geode* leftEyePlane = NULL;
osg::Geode* rightEyePlane = NULL;
if(!filePair.empty())
{
OSG_NOTICE << "Image pair: " << filePair << std::endl;
osg::ref_ptr<osg::Image> pairImage = osgDB::readImageFile(filePair);
if(!pairImage){
OSG_NOTICE << "ERROR: Could not load image: " << filePair << std::endl << std::endl;
return 1;
}
osg::TextureRectangle* pairTexture = new osg::TextureRectangle(pairImage);
pairTexture->setTextureWidth(pairImage->s());
pairTexture->setTextureHeight(pairImage->t());
leftEyePlane = createImagePlane(pairTexture, PairLeft);
rightEyePlane = createImagePlane(pairTexture, PairRight);
imgWidth = pairImage->s()/2;
imgHeight = pairImage->t();
}
else
{
OSG_NOTICE << "Left image: " << fileLeft << std::endl;
OSG_NOTICE << "Right image: " << fileRight << std::endl;
osg::ref_ptr<osg::Image> leftImage = osgDB::readImageFile(fileLeft);
osg::ref_ptr<osg::Image> rightImage = osgDB::readImageFile(fileRight);
if(!leftImage || !rightImage){
OSG_NOTICE << "ERROR: Could not load image: " << (!leftImage ? fileLeft : fileRight) << std::endl;
return 1;
}
if( (leftImage->s() != rightImage->s()) ||
(rightImage->t() != rightImage->t()) )
{
OSG_NOTICE << "ERROR: Left and right eye images must be the same dimension\n" << std::endl;
return 1;
}
osg::TextureRectangle* leftTexture = new osg::TextureRectangle(leftImage);
leftTexture->setTextureWidth(leftImage->s());
leftTexture->setTextureHeight(leftImage->t());
leftEyePlane = createImagePlane( leftTexture, Single);
osg::TextureRectangle* rightTexture = new osg::TextureRectangle(rightImage);
rightTexture->setTextureWidth(rightImage->s());
rightTexture->setTextureHeight(rightImage->t());
rightEyePlane = createImagePlane( rightTexture, Single);
imgWidth = leftImage->s();
imgHeight = leftImage->t();
}
osg::Camera* camera = new osg::Camera;
camera->setReferenceFrame( osg::Camera::ABSOLUTE_RF );
camera->setViewMatrix(osg::Matrix::identity());
camera->addChild( leftEyePlane );
camera->addChild( rightEyePlane );
leftEyePlane->setNodeMask(LEFT_EYE_MASK);
rightEyePlane->setNodeMask(RIGHT_EYE_MASK);
osg::Group* root = new osg::Group;
root->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
root->addChild( camera );
osg::ref_ptr< ResizeHandler > resizeHandler = new ResizeHandler(camera,stretchImage);
resizeHandler->setImageDims( imgWidth, imgHeight );
osgViewer::Viewer viewer;
viewer.getCamera()->setCullMaskLeft(LEFT_EYE_MASK);
viewer.getCamera()->setCullMaskRight(RIGHT_EYE_MASK);
viewer.getCamera()->setClearColor(osg::Vec4(0,0,0,1));
viewer.addEventHandler( new osgViewer::WindowSizeHandler );
if(!isFullScreen){
viewer.setUpViewInWindow(50,50,1024,768);
}
viewer.addEventHandler( resizeHandler );
viewer.setSceneData( root );
viewer.realize();
// Push a resize event onto the queue
osgGA::GUIEventAdapter* event = new osgGA::GUIEventAdapter();
event->setEventType(osgGA::GUIEventAdapter::RESIZE);
event->setWindowRectangle(0,0,screenWidth,screenHeight);
viewer.getEventQueue()->addEvent( event );
while( !viewer.done() )
{
viewer.frame();
}
return 0;
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org