#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include "image.h"
#include "image_op.h"
#include "rgba.h"
#include "calibrateWidget.hh"
#include "crossWidget.hh"
#include "mandoWizard.hh"
#include "selectRectWidget.hh"
#include "tools.hh"
#include "videoWidget.hh"
#include "image_funcs.h"
#define CALIBDELAY 900
using namespace mimas;
using namespace std;
MandoWizard::MandoWizard( QWidget *parent, Qt::WFlags f ):
QDialog( parent, f ), videoTimer(0), calibrateWidget(NULL), calibTimer(0),
segmentationTimer(0), pointerTimer(0)
{
ui.setupUi( this );
// Finalise GUI.
videoWidget = new VideoWidget;
ui.videoDisplay->setWidget( videoWidget );
searchPatternWidget = new VideoWidget;
ui.searchPatternDisplay->setWidget( searchPatternWidget );
projectedPatternWidget = new VideoWidget;
ui.projectedPatternDisplay->setWidget( projectedPatternWidget );
calibFrameWidget = new CrossWidget;
ui.calibFrameDisplay->setWidget( calibFrameWidget );
referenceImageWidget = new SelectRectWidget;
ui.referenceImageDisplay->setWidget( referenceImageWidget );
segmentedWidget = new CrossWidget;
ui.segmentedDisplay->setWidget( segmentedWidget );
// Connect signals and slots.
connect( ui.brightnessSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.hueSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.colourSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.contrastSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.shutterSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.gainSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.balanceSlider, SIGNAL(valueChanged(int)),
this, SLOT(setCameraParameters()) );
connect( ui.searchPatternButton, SIGNAL(clicked()),
this, SLOT(loadSearchPattern()) );
connect( ui.projectedPatternButton, SIGNAL(clicked()),
this, SLOT(loadProjectedPattern()) );
connect( ui.previousButton, SIGNAL(clicked()),
this, SLOT(previousPage()) );
connect( ui.nextButton, SIGNAL(clicked()),
this, SLOT(nextPage()) );
connect( ui.cancelButton, SIGNAL(clicked()),
this, SLOT(reject()) );
connect( ui.finishButton, SIGNAL(clicked()),
this, SLOT(accept()) );
connect( ui.reconnectButton, SIGNAL(clicked()),
this, SLOT(reconnectCamera()) );
connect( ui.calibrateButton, SIGNAL(clicked()),
this, SLOT(calibrate()) );
connect( ui.calibrateSlider, SIGNAL(valueChanged(int)),
this, SLOT(displayCalibFrame(int)) );
connect( ui.differenceCheckBox, SIGNAL(toggled(bool)),
this, SLOT(displayDifference(bool)) );
connect( ui.grabButton, SIGNAL(clicked()),
this, SLOT(grabReferenceImage()) );
connect( referenceImageWidget, SIGNAL(rectDefined(bool)),
this, SLOT(updateEnabled()) );
connect( ui.thresholdSlider, SIGNAL(valueChanged(int)),
this, SLOT(setThreshold(int)) );
connect( ui.sigmaSpinBox, SIGNAL(valueChanged(double)),
this, SLOT(setSigma(double)) );
connect( ui.rangeSlider, SIGNAL(valueChanged(int)),
this, SLOT(setRange(int)) );
// Load default patterns.
searchPatternWidget->setImage
( qImageToMimasImage
( QImage( ":/images/searchPattern.png" ) ) );
projectedPatternWidget->setImage
( qImageToMimasImage
( QImage( ":/images/projectedPattern.png" ) ) );
showPage( 0 );
}
void MandoWizard::previousPage()
{
showPage( ui.stackedWidget->currentIndex() - 1 );
}
void MandoWizard::nextPage()
{
showPage( ui.stackedWidget->currentIndex() + 1 );
}
void MandoWizard::showPage( int index )
{
// Check index.
assert( index >= 0 );
assert( index < ui.stackedWidget->count() );
// Update title.
switch ( index ) {
case 0:
ui.titleLabel->setText( "Adjust camera position" );
break;
case 1:
ui.titleLabel->setText( "Select calibration patterns" );
break;
case 2:
ui.titleLabel->setText( "Perform calibration" );
break;
case 3:
ui.titleLabel->setText( "Capture image and select reference image for "
"colour segmentation and select" );
break;
case 4:
ui.titleLabel->setText( "Set parameters for pointer recognition" );
break;
case 5:
ui.titleLabel->setText( "Set parameters for mouse-clicks" );
break;
default:
assert( false );
};
// Show page.
ui.stackedWidget->setCurrentIndex( index );
// Enable/disable controls.
updateEnabled();
// Set up video display.
setupVideo();
}
void MandoWizard::setupVideo(void)
{
int index = ui.stackedWidget->currentIndex();
if ( index == 0 ) {
if ( videoTimer == 0 )
videoTimer = startTimer( 0 );
} else
if ( videoTimer != 0 ) {
killTimer( videoTimer );
videoTimer = 0;
};
if ( index == 4 ) {
if ( segmentationTimer == 0 )
segmentationTimer = startTimer( 0 );
} else
if ( segmentationTimer != 0 ) {
killTimer( segmentationTimer );
segmentationTimer = 0;
};
if ( index == 5 ) {
if ( pointerTimer == 0 )
pointerTimer = startTimer( 0 );
} else
if ( pointerTimer != 0 ) {
killTimer( pointerTimer );
pointerTimer = 0;
};
}
void MandoWizard::updateEnabled(void)
{
// Enable/disable controls.
int index = ui.stackedWidget->currentIndex();
ui.previousButton->setEnabled( index > 0 );
ui.finishButton->setEnabled( index == 5 );
ui.brightnessSlider->setEnabled( (bool)v4lInput );
ui.hueSlider->setEnabled( (bool)v4lInput );
ui.colourSlider->setEnabled( (bool)v4lInput );
ui.contrastSlider->setEnabled( (bool)v4lInput );
ui.shutterSlider->setEnabled( (bool)dc1394Input );
ui.gainSlider->setEnabled( (bool)dc1394Input );
ui.balanceSlider->setEnabled( (bool)dc1394Input );
switch ( index ) {
case 0:
ui.nextButton->setEnabled( (bool)input );
break;
case 1:
ui.nextButton->setEnabled
( searchPatternWidget->getImage().initialised() &&
projectedPatternWidget->getImage().initialised() );
break;
case 2: {
bool calibFinished;
if ( cameraProjectorCalibration )
calibFinished = cameraProjectorCalibration->getNumCalibFrames() >= 5;
else
calibFinished = false;
ui.nextButton->setEnabled( calibFinished );
ui.selectImageLabel->setEnabled( calibFinished );
ui.calibrateSlider->setEnabled( calibFinished );
ui.differenceCheckBox->setEnabled( calibFinished );
break;}
case 3:
ui.nextButton->setEnabled( referenceImageWidget->isRectDefined() );
if ( !referenceImageWidget->isRectDefined() )
pointerRecognition.reset();
break;
case 4:
ui.nextButton->setEnabled( true );
break;
case 5:
ui.nextButton->setEnabled( false );
break;
default:
assert( false );
};
}
void MandoWizard::loadSearchPattern()
{
image< rgba< unsigned char > > img;
if ( loadImage( "Load search pattern", img ) ) {
searchPatternWidget->setImage( img );
cameraProjectorCalibration.reset();
pointerRecognition.reset();
updateEnabled();
};
}
void MandoWizard::loadProjectedPattern()
{
image< rgba< unsigned char > > img;
if ( loadImage( "Load projected pattern", img ) ) {
projectedPatternWidget->setImage( img );
cameraProjectorCalibration.reset();
pointerRecognition.reset();
updateEnabled();
};
}
void MandoWizard::reconnectCamera()
{
input.reset();
v4lInput.reset();
dc1394Input.reset();
cameraProjectorCalibration.reset();
pointerRecognition.reset();
referenceImageWidget->clearSelection();
assert( ui.stackedWidget->currentIndex() == 0 );
setupVideo();
}
void MandoWizard::displayCalibFrame( int index )
{
displayCalibResult( index, ui.differenceCheckBox->isChecked() );
}
void MandoWizard::displayDifference( bool diff )
{
displayCalibResult( ui.calibrateSlider->value(), diff );
}
void MandoWizard::grabReferenceImage()
{
referenceImageWidget->setImage( grabColourFrame() );
updateEnabled();
}
void MandoWizard::setThreshold( int _value )
{
assert( pointerRecognition );
assert( ui.thresholdSlider->minimum() == 0 );
pointerRecognition->setThreshold
( 0.25 * _value / ui.thresholdSlider->maximum() );
}
void MandoWizard::setRange( int _value )
{
assert( pointerRecognition );
pointerRecognition->setTrackingRange( _value );
}
void MandoWizard::setSigma( double _value )
{
pointerRecognition->setSigma( _value );
}
void MandoWizard::displayCalibResult( int index, bool diff )
{
assert( cameraProjectorCalibration );
assert( index >= 0 &&
index < cameraProjectorCalibration->getNumCalibFrames() + 1 );
if ( index == 0 ) {
calibFrameWidget->setImage
( cameraProjectorCalibration->getBackgroundFrame() );
calibFrameWidget->clearCross();
} else {
if ( diff )
calibFrameWidget->setImage
( normalise( image< int >( cameraProjectorCalibration->
getCalibFrame( index - 1 ) ) -
image< int >( cameraProjectorCalibration->
getBackgroundFrame() ), 0, 255 ) );
else
calibFrameWidget->setImage
( cameraProjectorCalibration->getCalibFrame( index - 1 ) );
calibFrameWidget->setCross
( cameraProjectorCalibration->getCalibPointPair( index - 1 ).second );
};
}
bool MandoWizard::loadImage( const char *title,
image< rgba< unsigned char > > &img )
{
bool retVal = false;
try {
QString s = QFileDialog::getOpenFileName( this, title,
".",
"Images (*.png *.jpg);;"
"All Files (*)" );
if ( s != QString::null ) {
img = qImageToMimasImage( QImage( s ) );
MMERROR( img.initialised(), mimasexception, ,
"Error loading file \"" << (const char *)s.toLatin1()
<< "\"." );
retVal = true;
};
} catch ( exception &e ) {
QMessageBox::critical( this, "Error loading image", e.what() );
};
return retVal;
}
void MandoWizard::timerEvent( QTimerEvent *e )
{
if ( e->timerId() == videoTimer ) {
try {
videoWidget->setImage( grabColourFrame( false ) );
assert( ui.stackedWidget->currentIndex() == 0 );
} catch ( exception &e ) {
assert( videoTimer != 0 );
killTimer( videoTimer );
videoTimer = 0;
videoWidget->setImage( image< rgba< unsigned char > >() );
input.reset();
v4lInput.reset();
dc1394Input.reset();
ui.errorLabel->setText( e.what() );
updateEnabled();
};
} else if ( e->timerId() == calibTimer ) {
assert( calibrateWidget != NULL );
killTimer( calibTimer );
calibTimer = 0;
if ( !calibrateWidget->isVisible() ) {
delete calibrateWidget;
calibrateWidget = NULL;
cameraProjectorCalibration.reset();
pointerRecognition.reset();
} else {
switch ( calibState ) {
case Init:
calibrateWidget->setPattern( 0 );
calibState = First;
break;
case First:
cameraProjectorCalibration->setBackgroundFrame( grabFrame() );
calibrateWidget->setPattern( 1 );
calibState = Second;
break;
case Second:
cameraProjectorCalibration->addCalibFrame
( grabFrame(), calibrateWidget->getPos( 1 ) );
calibrateWidget->setPattern( 2 );
calibState = Third;
break;
case Third:
cameraProjectorCalibration->addCalibFrame
( grabFrame(), calibrateWidget->getPos( 2 ) );
calibrateWidget->setPattern( 3 );
calibState = Fourth;
break;
case Fourth:
cameraProjectorCalibration->addCalibFrame
( grabFrame(), calibrateWidget->getPos( 3 ) );
calibrateWidget->setPattern( 4 );
calibState = Fifth;
break;
case Fifth:
cameraProjectorCalibration->addCalibFrame
( grabFrame(), calibrateWidget->getPos( 4 ) );
calibrateWidget->setPattern( 5 );
calibState = Closing;
break;
case Closing:
cameraProjectorCalibration->addCalibFrame
( grabFrame(), calibrateWidget->getPos( 5 ) );
calibrateWidget->setPattern( 0 );
calibState = Finished;
break;
default:
calibState = Init;
displayCalibFrame( ui.calibrateSlider->value() );
// Get size of screen.
// This is only done at this point, because directly after
// initialisation of calibrateWidget, calibrateWidget->width() and
// ..->height() will not have the correct valuies.
screenW = calibrateWidget->width();
screenH = calibrateWidget->height();
delete calibrateWidget;
calibrateWidget = NULL;
updateEnabled();
};
if ( calibState != Init )
calibTimer=startTimer( CALIBDELAY );
};
} else if ( e->timerId() == segmentationTimer ) {
if ( !pointerRecognition ) {
assert( cameraProjectorCalibration );
pointerRecognition =
PointerRecognitionPtr
( new PointerRecognition( cameraProjectorCalibration,
referenceImageWidget->
selectedImage() ) );
setThreshold( ui.thresholdSlider->value() );
setSigma( ui.sigmaSpinBox->value() );
setRange( ui.rangeSlider->value() );
pointerRecognition->setClip( 0, 0, screenW, screenH );
};
Vector camPos( 3 ), pos( 3 );
image< rgba< unsigned char > > frame( grabColourFrame() );
if ( pointerRecognition->findPointer( frame, camPos, pos ) )
segmentedWidget->setCross( camPos );
else
segmentedWidget->clearCross();
segmentedWidget->setImage
( pointerRecognition->getMinX(),
pointerRecognition->getMinY(),
frame.getWidth(),
frame.getHeight(),
pointerRecognition->getSegmentedImage() );
} else if ( e->timerId() == pointerTimer ) {
Vector camPos( 3 ), pos( 3 );
image< rgba< unsigned char > > frame( grabColourFrame() );
if ( pointerRecognition->findPointer( frame, camPos, pos ) ) {
// Display *d = XOpenDisplay( NULL );
// XTestFakeMotionEvent( d, DefaultScreen( d ),
// (int)pos[0], (int)pos[1], 0 );
// XCloseDisplay( d );
ui.mouseXLCD->setSegmentStyle( QLCDNumber::Flat );
ui.mouseYLCD->setSegmentStyle( QLCDNumber::Flat );
ui.mouseXLCD->display( (int)pos[0] );
ui.mouseYLCD->display( (int)pos[1] );
} else {
ui.mouseXLCD->setSegmentStyle( QLCDNumber::Outline );
ui.mouseYLCD->setSegmentStyle( QLCDNumber::Outline );
};
} else
QDialog::timerEvent( e );
}
MandoWizard::VideoPtr MandoWizard::camera(void)
throw (mimas::mimasexception)
{
if ( !input ) {
if ( ui.cameraStack->currentIndex() == 0 ) {
__u16 norm;
switch ( ui.modeBox->currentIndex() ) {
case 0:
norm = VIDEO_MODE_PAL;
break;
case 1:
norm = VIDEO_MODE_NTSC;
break;
case 2:
norm = VIDEO_MODE_SECAM;
break;
case 3:
norm = VIDEO_MODE_AUTO;
break;
default:
norm = VIDEO_MODE_PAL;
};
v4lInput =
V4LInputPtr( new V4LInput( (const char *)ui.v4lDeviceEdit->
text().toLatin1(),
ui.v4lChannelSpinBox->value(),
-1, -1, norm ) );
input = v4lInput;
setCameraParameters();
ui.errorLabel->setText( "" );
updateEnabled();
} else {
dc1394Input =
DC1394InputPtr( new DC1394Input( (const char *)ui.dc1394DeviceEdit->
text().toLatin1(),
ui.dc1394NodeSpinBox->value(),
ui.dc1394ChannelSpinBox->value() ) );
input = dc1394Input;
dc1394_feature_info shutter = dc1394Input->
get_feature( FEATURE_SHUTTER );
ui.shutterSlider->setMinimum( shutter.min );
ui.shutterSlider->setMaximum( shutter.max );
ui.shutterSlider->setValue( shutter.value );
dc1394_feature_info gain = dc1394Input->
get_feature( FEATURE_GAIN );
ui.gainSlider->setMinimum( gain.min );
ui.gainSlider->setMaximum( gain.max );
ui.gainSlider->setValue( gain.value );
dc1394_feature_info balance = dc1394Input->
get_feature( FEATURE_WHITE_BALANCE );
ui.balanceSlider->setMinimum( balance.min );
ui.balanceSlider->setMaximum( balance.max );
ui.balanceSlider->setValue( ( balance.min + balance.max ) / 2 );
setCameraParameters();
ui.errorLabel->setText( "" );
updateEnabled();
};
};
return input;
}
void MandoWizard::setCameraParameters(void)
{
if ( v4lInput )
v4lInput->setSensivity( ui.brightnessSlider->value(),
ui.hueSlider->value(),
ui.colourSlider->value(),
ui.contrastSlider->value() );
if ( dc1394Input ) {
dc1394Input->set_feature_value( FEATURE_SHUTTER,
ui.shutterSlider->value() );
dc1394Input->set_feature_value( FEATURE_GAIN,
ui.gainSlider->value() );
dc1394Input->set_feature_value( FEATURE_WHITE_BALANCE,
ui.balanceSlider->value() );
};
}
image< unsigned char > MandoWizard::grabFrame(void)
throw (mimasexception)
{
return image< unsigned char >( grabColourFrame() );
}
image< rgba< unsigned char > > MandoWizard::grabColourFrame( bool twice )
throw (mimasexception)
{
image< rgba< unsigned char > > retVal;
// discard old frame.
if ( twice ) (*camera()) >> retVal;
(*camera()) >> retVal;
return retVal;
}
void MandoWizard::calibrate()
{
if ( calibrateWidget != NULL ) {
delete calibrateWidget;
calibrateWidget = NULL;
};
assert( projectedPatternWidget->getImage().initialised() );
calibrateWidget = new CalibrateWidget
( image< unsigned char >( projectedPatternWidget->getImage() ),
this );
assert( searchPatternWidget->getImage().initialised() );
cameraProjectorCalibration =
CameraProjectorCalibrationPtr
( new CameraProjectorCalibration( searchPatternWidget->getImage() ) );
pointerRecognition.reset();
calibrateWidget->setWindowFlags( Qt::Dialog );
// calibrateWidget->setWindowModality( Qt::WindowModal );
calibrateWidget->showFullScreen();
updateEnabled();
if ( calibTimer != 0 ) {
killTimer( calibTimer );
calibTimer = 0;
};
calibState = Init;
calibTimer = startTimer( CALIBDELAY );
}
void MandoWizard::startDrag()
{
ui.statusLabel->setText( "<b>button pressed</b>" );
}
void MandoWizard::stopDrag()
{
ui.statusLabel->setText( "button released" );
}