Rev 36 | Rev 42 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "SMScanner.h"
#include "ui_SMScanner.h"
#include <QMetaObject>
#include <QFileDialog>
SMScanner::SMScanner(QWidget *parent) :QMainWindow(parent), ui(new Ui::SMScanner),
calibrationReviewMode(false), captureReviewMode(false){
ui->setupUi(this);
// Register metatypes
qRegisterMetaType<cv::Mat>("cv::Mat");
qRegisterMetaType< std::vector<cv::Mat> >("std::vector<cv::Mat>");
qRegisterMetaType< std::vector<float> >("std::vector<float>");
qRegisterMetaType<SMCalibrationSet>("SMCalibrationSet");
qRegisterMetaType<SMCalibrationParameters>("SMCalibrationParameters");
qRegisterMetaTypeStreamOperators<SMCalibrationParameters>("SMCalibrationParameters");
qRegisterMetaType< std::vector<SMCalibrationSet> >("std::vector<SMCalibrationSet>");
qRegisterMetaType<SMFrameSequence>("SMFrameSequence");
qRegisterMetaType<pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr>("pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr");
// Restore geometry
this->restoreGeometry(settings.value("geometry/mainwindow").toByteArray());
this->restoreState(settings.value("state/mainwindow").toByteArray());
// Set up threads
captureWorker = new SMCaptureWorker;
captureWorkerThread = new QThread(this);
captureWorkerThread->setObjectName("captureWorkerThread");
captureWorker->moveToThread(captureWorkerThread);
captureWorkerThread->start();
// Connections
connect(captureWorker, SIGNAL(newFrame(unsigned int, cv::Mat)), this, SLOT(onReceiveFrame(unsigned int, cv::Mat)));
connect(captureWorker, SIGNAL(newCalibrationSet(SMCalibrationSet)), this, SLOT(onReceiveCalibrationSet(SMCalibrationSet)));
connect(captureWorker, SIGNAL(newFrameSequence(SMFrameSequence)), this, SLOT(onReceiveFrameSequence(SMFrameSequence)));
connect(captureWorker, SIGNAL(rotatedTo(float)), this, SLOT(onReceiveRotatedTo(float)));
// Start capturing
QMetaObject::invokeMethod(captureWorker, "setup");
QMetaObject::invokeMethod(captureWorker, "doWork");
}
void SMScanner::onReceiveFrame(unsigned int camId, cv::Mat frame){
if(camId == 0){
if(!calibrationReviewMode)
ui->calibrationCamera0Widget->showImageCV(frame);
if(!captureReviewMode)
ui->captureCamera0Widget->showImageCV(frame);
} else if(camId == 1){
if(!calibrationReviewMode)
ui->calibrationCamera1Widget->showImageCV(frame);
if(!captureReviewMode)
ui->captureCamera1Widget->showImageCV(frame);
}
}
void SMScanner::on_actionPreferences_triggered(){
preferenceDialog.show();
}
void SMScanner::closeEvent(QCloseEvent *event){
// Stop capturing thread
connect(captureWorker, SIGNAL(finished()), captureWorker, SLOT(deleteLater()));
connect(captureWorker, SIGNAL(finished()), captureWorkerThread, SLOT(quit()));
QMetaObject::invokeMethod(captureWorker, "stopWork");
captureWorkerThread->quit();
captureWorkerThread->wait();
// Save window geometry
settings.setValue("geometry/mainwindow", this->saveGeometry());
settings.setValue("state/mainwindow", this->saveState());
event->accept();
}
SMScanner::~SMScanner(){
delete ui;
}
void SMScanner::on_singleCalibrationButton_clicked(){
// If in review mode, go back to aquisition mode
if(calibrationReviewMode){
ui->calibrationListWidget->clearSelection();
ui->singleCalibrationButton->setText("Single Aquisition");
ui->batchCalibrationButton->setText("Batch Aquisition");
calibrationReviewMode = false;
return;
}
QMetaObject::invokeMethod(captureWorker, "acquireCalibrationSet", Q_ARG(float, -1.0));
}
void SMScanner::on_batchCalibrationButton_clicked(){
// If in review mode, go back to aquisition mode
if(calibrationReviewMode){
ui->calibrationListWidget->clearSelection();
ui->singleCalibrationButton->setText("Single Aquisition");
ui->batchCalibrationButton->setText("Batch Aquisition");
calibrationReviewMode = false;
return;
}
// Construct vector of angles
int angleStart = ui->calibrationBatchStartSpinBox->value();
int angleEnd = ui->calibrationBatchEndSpinBox->value();
int angleStep = ui->calibrationBatchStepSpinBox->value();
if(angleStart > angleEnd)
angleEnd += 360;
std::vector<float> angles;
for(int i=angleStart; i<=angleEnd; i+=angleStep)
angles.push_back(i % 360);
QMetaObject::invokeMethod(captureWorker, "acquireCalibrationSets", Q_ARG(std::vector<float>, angles));
std::cout << "Aquiring sets at ";
for(int i=0; i<angles.size(); i++)
std::cout << angles[i] << " ";
std::cout << " degrees" <<std::endl;
}
void SMScanner::on_calibrationRotationDial_sliderReleased(){
float angle = ui->calibrationRotationDial->value();
std::cout << "Rotation stage target: " << angle << std::endl;
QMetaObject::invokeMethod(captureWorker, "rotateTo", Q_ARG(float, angle));
ui->captureRotationDial->setValue(ui->calibrationRotationDial->value());
}
void SMScanner::onReceiveCalibrationSet(SMCalibrationSet calibrationSet){
calibrationData.push_back(calibrationSet);
// Add identifier to list
QListWidgetItem* item = new QListWidgetItem(QString("Calibration Set %1 -- %2 deg").arg(ui->calibrationListWidget->count()).arg(calibrationSet.rotationAngle), ui->calibrationListWidget);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
ui->calibrationListWidget->addItem(item);
// Set enabled checkmark
if(calibrationData.size() >= 2)
ui->calibrateButton->setEnabled(true);
}
void SMScanner::on_calibrateButton_clicked(){
// disable ui elements
ui->calibrateButton->setEnabled(false);
ui->calibrationFrame->setEnabled(false);
// set checked flags
for(int i=0; i<calibrationData.size(); i++){
calibrationData[i].checked = (ui->calibrationListWidget->item(i)->checkState() == Qt::Checked);
}
// SMCalibrationWorker calibrationWorker;
// calibrationWorker.performCalibration(calibrationData);
// Set up calibration thread
calibrationWorker = new SMCalibrationWorker;
calibrationWorkerThread = new QThread(this);
calibrationWorkerThread->setObjectName("calibrationWorkerThread");
calibrationWorker->moveToThread(captureWorkerThread);
calibrationWorkerThread->start();
// Connections
connect(calibrationWorker, SIGNAL(newSetProcessed(int)), this, SLOT(onCalibrationSetProcessed(int)));
connect(calibrationWorker, SIGNAL(newFrameResult(int,int,bool,cv::Mat)), this, SLOT(onCalibrationFrameResult(int,int,bool,cv::Mat)));
connect(calibrationWorker, SIGNAL(done()), this, SLOT(onCalibrationDone()));
connect(calibrationWorker, SIGNAL(done()), ui->pointCloudWidget, SLOT(updateCalibrationParameters()));
connect(calibrationWorker, SIGNAL(done()), calibrationWorkerThread, SLOT(quit()));
connect(calibrationWorker, SIGNAL(done()), calibrationWorker, SLOT(deleteLater()));
// Start calibration
QMetaObject::invokeMethod(calibrationWorker, "performCalibration", Q_ARG(std::vector<SMCalibrationSet>, calibrationData));
}
void SMScanner::onCalibrationSetProcessed(int idx){
ui->calibrationListWidget->setCurrentRow(idx);
}
void SMScanner::onCalibrationDone(){
std::cout << "Calibration done!" << std::endl;
ui->calibrateButton->setEnabled(true);
ui->calibrationFrame->setEnabled(true);
}
void SMScanner::on_calibrationListWidget_itemSelectionChanged(){
// if selection is just cleared
if(ui->calibrationListWidget->selectedItems().empty())
return;
int currentRow = ui->calibrationListWidget->currentRow();
calibrationReviewMode = true;
ui->singleCalibrationButton->setText("Live View");
ui->batchCalibrationButton->setText("Live View");
if(!calibrationData[currentRow].frame0Result.empty())
ui->calibrationCamera0Widget->showImageCV(calibrationData[currentRow].frame0Result);
else
ui->calibrationCamera0Widget->showImageCV(calibrationData[currentRow].frame0);
if(!calibrationData[currentRow].frame1Result.empty())
ui->calibrationCamera1Widget->showImageCV(calibrationData[currentRow].frame1Result);
else
ui->calibrationCamera1Widget->showImageCV(calibrationData[currentRow].frame1);
// std::cout << "on_calibrationListWidget_itemSelectionChanged" << std::endl;
}
void SMScanner::onCalibrationFrameResult(int idx, int camID, bool success, cv::Mat result){
// std::cout << "onCalibrationFrameResult " << idx << camID << std::endl;
if(!success)
ui->calibrationListWidget->item(idx)->setCheckState(Qt::Unchecked);
else {
if(camID == 0)
calibrationData[idx].frame0Result = result;
else if(camID == 1)
calibrationData[idx].frame1Result = result;
}
}
void SMScanner::on_singleCaptureButton_clicked(){
// If in review mode, go back to aquisition mode
if(captureReviewMode){
ui->captureTreeWidget->clearSelection();
ui->singleCaptureButton->setText("Single Aquisition");
ui->batchCaptureButton->setText("Batch Aquisition");
captureReviewMode = false;
return;
}
QMetaObject::invokeMethod(captureWorker, "acquireFrameSequence", Q_ARG(float, -1.0));
}
void SMScanner::on_batchCaptureButton_clicked(){
// If in review mode, go back to aquisition mode
if(captureReviewMode){
ui->captureTreeWidget->clearSelection();
ui->singleCaptureButton->setText("Single Aquisition");
ui->batchCaptureButton->setText("Batch Aquisition");
captureReviewMode = false;
return;
}
// Construct vector of angles
int angleStart = ui->captureBatchStartSpinBox->value();
int angleEnd = ui->captureBatchEndSpinBox->value();
int angleStep = ui->captureBatchStepSpinBox->value();
if(angleStart > angleEnd)
angleEnd += 360;
std::vector<float> angles;
for(int i=angleStart; i<=angleEnd; i+=angleStep)
angles.push_back(i % 360);
std::cout << "Aquiring sequences at ";
for(int i=0; i<angles.size(); i++)
std::cout << angles[i] << " ";
std::cout << " degrees" <<std::endl;
QMetaObject::invokeMethod(captureWorker, "acquireFrameSequences", Q_ARG(std::vector<float>, angles));
}
void SMScanner::on_captureRotationDial_sliderReleased(){
float angle = ui->captureRotationDial->value();
std::cout << "Rotation stage target: " << angle << std::endl;
QMetaObject::invokeMethod(captureWorker, "rotateTo", Q_ARG(float, angle));
ui->calibrationRotationDial->setValue(ui->captureRotationDial->value());
}
void SMScanner::onReceiveFrameSequence(SMFrameSequence frameSequence){
captureData.push_back(frameSequence);
int idx = captureData.size()-1;
// Add identifier to list
QTreeWidgetItem* item = new QTreeWidgetItem(ui->captureTreeWidget);
item->setText(0, QString("Frame Sequence %1 -- %2 deg").arg(idx).arg(frameSequence.rotationAngle));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setData(0, Qt::UserRole, QPoint(idx, -1));
item->setCheckState(0, Qt::Checked);
//ui->captureTreeWidget->addItem(item);
for(int i=0; i<frameSequence.frames0.size(); i++){
QTreeWidgetItem* subItem = new QTreeWidgetItem(item);
subItem->setText(0, QString("frames %1").arg(i));
subItem->setData(0, Qt::UserRole, QPoint(idx, i));
}
ui->reconstructButton->setEnabled(true);
}
void SMScanner::on_captureTreeWidget_itemSelectionChanged(){
// if selection is just cleared
if(ui->captureTreeWidget->selectedItems().empty())
return;
QTreeWidgetItem *item = ui->captureTreeWidget->currentItem();
captureReviewMode = true;
ui->singleCaptureButton->setText("Live View");
ui->batchCaptureButton->setText("Live View");
QPoint idx = item->data(0, Qt::UserRole).toPoint();
if(idx.y() != -1){
ui->captureCamera0Widget->showImageCV(captureData[idx.x()].frames0[idx.y()]);
ui->captureCamera1Widget->showImageCV(captureData[idx.x()].frames1[idx.y()]);
}
// std::cout << "on_captureTreeWidget_itemSelectionChanged" << std::endl;
}
void SMScanner::onReceiveRotatedTo(float angle){
// update ui with new position
ui->calibrationRotationDial->setValue(angle);
ui->captureRotationDial->setValue(angle);
}
void SMScanner::on_actionExport_Sets_triggered(){
QString dirName = QFileDialog::getExistingDirectory(this, "Export calibration sets", QString());
for(int i=0; i<calibrationData.size(); i++){
QString fileName = QString("%1/frame0_%2.png").arg(dirName).arg(i);
cv::Mat frameBGR;
cv::cvtColor(calibrationData[i].frame0, frameBGR, CV_RGB2BGR);
cv::imwrite(fileName.toStdString(), frameBGR);
fileName = QString("%1/frame1_%2.png").arg(dirName).arg(i);
cv::cvtColor(calibrationData[i].frame1, frameBGR, CV_RGB2BGR);
cv::imwrite(fileName.toStdString(), frameBGR);
}
}
void SMScanner::on_actionExport_Sequences_triggered(){
QString dirName = QFileDialog::getExistingDirectory(this, "Export frame sequences", QString());
for(int i=0; i<captureData.size(); i++){
QString seqDirName = QString("%1/sequence_%2").arg(dirName).arg(i);
if(!QDir().mkdir(seqDirName))
std::cerr << "Could not create directory " << seqDirName.toStdString() << std::endl;
for(int j=0; j<captureData[i].frames0.size(); j++){
QString fileName = QString("%1/frames0_%2.png").arg(seqDirName).arg(j);
cv::Mat frameBGR;
cv::cvtColor(captureData[i].frames0[j], frameBGR, CV_RGB2BGR);
cv::imwrite(fileName.toStdString(), frameBGR);
}
for(int j=0; j<captureData[i].frames1.size(); j++){
QString fileName = QString("%1/frames1_%2.png").arg(seqDirName).arg(j);
cv::Mat frameBGR;
cv::cvtColor(captureData[i].frames1[j], frameBGR, CV_RGB2BGR);
cv::imwrite(fileName.toStdString(), frameBGR);
}
}
}
void SMScanner::on_reconstructButton_clicked(){
// // set checked flags
// for(int i=0; i<captureData.size(); i++){
// captureData[i].checked = (ui->captureTreeWidget->item(i)->checkState() == Qt::Checked);
// }
// Set up reconstruction thread
reconstructionWorker = new SMReconstructionWorker;
reconstructionWorkerThread = new QThread(this);
reconstructionWorkerThread->setObjectName("reconstructionWorkerThread");
reconstructionWorker->moveToThread(reconstructionWorkerThread);
reconstructionWorkerThread->start();
// Connections
connect(reconstructionWorker, SIGNAL(newPointCloud(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr)), this, SLOT(onNewPointCloud(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr)));
connect(reconstructionWorker, SIGNAL(done()), reconstructionWorkerThread, SLOT(quit()));
connect(reconstructionWorker, SIGNAL(done()), reconstructionWorker, SLOT(deleteLater()));
// Start reconstructing
QMetaObject::invokeMethod(reconstructionWorker, "reconstruct", Q_ARG(std::vector<SMFrameSequence>, captureData));
}
void SMScanner::onNewPointCloud(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr pc){
SMPointCloud smCloud;
//smCloud.pointCloud = pc;
pointCloudData.push_back(smCloud);
ui->pointCloudWidget->updatePointCloud(pc);
}