Subversion Repositories seema-scanner

Rev

Rev 22 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

#include "MVCalibrationDialog.h"
#include "ui_MVCalibrationDialog.h"

#include <QListWidgetItem>
#include <QSettings>
#include <QCloseEvent>


MVCalibrationDialog::MVCalibrationDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MVCalibrationDialog), reviewMode(false){
    ui->setupUi(this);


    // Set up capture worker on separate thread
    captureWorker = new MVCaptureWorker();
    captureWorkerThread = new QThread(this);
    captureWorkerThread->setObjectName("captureWorkerThread");

    captureWorker->moveToThread(captureWorkerThread);
    captureWorkerThread->start();


    // Connect program logic
    qRegisterMetaType<cv::Mat>("cv::Mat");
    qRegisterMetaType< std::vector<cv::Mat> >("std::vector<cv::Mat>");

    connect(captureWorker, SIGNAL(newFrameSet(std::vector<cv::Mat>)), this, SLOT(receiveNewFrameSet(std::vector<cv::Mat>)));

    // Start capturing
    QMetaObject::invokeMethod(captureWorker, "setup", Q_ARG(bool, false));
    QMetaObject::invokeMethod(captureWorker, "doWork");

}

void MVCalibrationDialog::receiveNewFrameSet(std::vector<cv::Mat> frameSet){
    ui->videoWidget0->showImageCV(frameSet[0]);
    ui->videoWidget1->showImageCV(frameSet[1]);
}

void MVCalibrationDialog::addFrameSetToCalibration(std::vector<cv::Mat> frameSet){

    // Disconnect from further frame sets
    disconnect(captureWorker, SIGNAL(newFrameSet(std::vector<cv::Mat>)), this, SLOT(addFrameSetToCalibration(std::vector<cv::Mat>)));

    calibrationSets.push_back(frameSet);

    // Add identifier to list
    QListWidgetItem* item = new QListWidgetItem(QString("Set %1").arg(ui->listWidget->count()), ui->listWidget);
    item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
    item->setCheckState(Qt::Checked);

    // Enable calibration
    if(calibrationSets.size() >= 2)
        ui->calibrateButton->setEnabled(true);

}

void MVCalibrationDialog::on_snapButton_clicked(){

    if(reviewMode){
        ui->snapButton->setText("Snap");
        connect(captureWorker, SIGNAL(newFrameSet(std::vector<cv::Mat>)), this, SLOT(receiveNewFrameSet(std::vector<cv::Mat>)));
        reviewMode = false;
        return;
    }

    // Redirect one frame set into calibration
    connect(captureWorker, SIGNAL(newFrameSet(std::vector<cv::Mat>)), this, SLOT(addFrameSetToCalibration(std::vector<cv::Mat>)));

}

void MVCalibrationDialog::on_listWidget_clicked(const QModelIndex &index){



}

void MVCalibrationDialog::on_calibrateButton_clicked(){

    reviewMode = true;
    ui->snapButton->setText("Capture");
    QCoreApplication::processEvents();

    // Disconnect form live feed for review mode
    disconnect(captureWorker, SIGNAL(newFrameSet(std::vector<cv::Mat>)), this, SLOT(receiveNewFrameSet(std::vector<cv::Mat>)));

    // Number of saddle points on calibration pattern
    cv::Size patternSize(10, 9);

    int nSets = calibrationSets.size();

    std::vector< std::vector< std::vector<cv::Point2f> > > qc(2);

    calibrationResults = calibrationSets;

    // Loop through calibration sets
    for(int i=0; i<nSets; i++){

        if(ui->listWidget->item(i)->checkState() != Qt::Checked){
            continue;
        }

        std::vector<cv::Mat> calibrationSetI = calibrationSets[i];
        std::vector< std::vector<cv::Point2f> > qci;

        // Loop through cameras
        for(unsigned int j=0; j<calibrationSetI.size(); j++){
            std::vector<cv::Point2f> qcij;
            // Extract checker corners
            bool success = cv::findChessboardCorners(calibrationSetI[j], patternSize, qcij, cv::CALIB_CB_ADAPTIVE_THRESH);
            if(success){
                cv::cornerSubPix(calibrationSetI[j], qcij, cv::Size(5, 5), cv::Size(-1, -1),cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 100, 0.001));
                // Draw colored chessboard
                cv::Mat calibrationSetIJColor;
                cv::cvtColor(calibrationSetI[j], calibrationSetIJColor, CV_GRAY2RGB);
                cv::drawChessboardCorners(calibrationSetIJColor, patternSize, qcij, success);
                calibrationResults[i][j] = calibrationSetIJColor;
            } else {
                ui->listWidget->item(i)->setCheckState(Qt::Unchecked);
            }
            qci.push_back(qcij);
        }

        // Add to whole set
        if(!qci[0].empty() && !qci[1].empty() ){
            qc[0].push_back(qci[0]);
            qc[1].push_back(qci[1]);
        }

        // Show results
        ui->listWidget->setCurrentRow(i);
        QCoreApplication::processEvents();
    }

    int nValidSets = qc[0].size();
    if(nValidSets <= 2){
        std::cerr << "Not enough valid calibration sequences!" << std::endl;
        return;
    }

    // Generate world object coordinates [mm]
    std::vector<cv::Point3f> Qi;
    for (int h=0; h<patternSize.height; h++)
        for (int w=0; w<patternSize.width; w++)
            Qi.push_back(cv::Point3f(5 * w, 5* h, 0.0)); // 5mm chess field size
    std::vector< std::vector<cv::Point3f> > Q;
    for(int i=0; i<qc[0].size(); i++)
        Q.push_back(Qi);

    // calibrate the cameras
//    cv::Size frameSize(calibrationSets[0][0].cols, calibrationSets[0][0].rows);
    cv::Size frameSize(640, 480);
    int flags = 0; //cv::CALIB_FIX_K3 + cv::CALIB_FIX_INTRINSIC;

    std::vector< std::vector<cv::Point2f> > qc0 = qc[0];
    std::vector< std::vector<cv::Point2f> > qc1 = qc[1];

    std::vector<cv::Mat> cam_rvecs0, cam_tvecs0;
    cal.cam0_error = cv::calibrateCamera(Q, qc0, frameSize, cal.K0, cal.k0, cam_rvecs0, cam_tvecs0);

    std::vector<cv::Mat> cam_rvecs1, cam_tvecs1;
    cal.cam1_error = cv::calibrateCamera(Q, qc1, frameSize, cal.K1, cal.k1, cam_rvecs1, cam_tvecs1);

    // stereo calibration (don't change K0, K1, k0, k1)
    int flags_stereo = flags + cv::CALIB_FIX_INTRINSIC;
    cv::Mat E, F, R1, T1;
    cal.stereo_error = cv::stereoCalibrate(Q, qc[0], qc[1], cal.K0, cal.k0, cal.K1, cal.k1,
                                              frameSize, R1, T1, E, F,
                                              cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 50, DBL_EPSILON),
                                              flags_stereo);

    cal.R1 = R1;
    cal.T1 = T1;
    cal.E = E;
    cal.F = F;

    cal.print(std::cout);

    ui->saveButton->setEnabled(true);

}


void MVCalibrationDialog::on_listWidget_currentRowChanged(int currentRow){

    reviewMode = true;
    ui->snapButton->setText("Capture");

    // Disconnect form live feed for review mode
    disconnect(captureWorker, SIGNAL(newFrameSet(std::vector<cv::Mat>)), this, SLOT(receiveNewFrameSet(std::vector<cv::Mat>)));

    unsigned int idx = ui->listWidget->currentRow();

    // Show result if available, otherwise captured image
    if(!calibrationResults[idx][0].empty())
        ui->videoWidget0->showImageCV(calibrationResults[idx][0]);
    else
        ui->videoWidget0->showImageCV(calibrationSets[idx][0]);

    if(!calibrationResults[idx][1].empty())
        ui->videoWidget1->showImageCV(calibrationResults[idx][1]);
    else
        ui->videoWidget1->showImageCV(calibrationSets[idx][1]);
}

void MVCalibrationDialog::closeEvent(QCloseEvent *event){

    connect(captureWorker, SIGNAL(finished()), captureWorker, SLOT(deleteLater()));
    QMetaObject::invokeMethod(captureWorker, "stopWorking");
    captureWorkerThread->quit();
    captureWorkerThread->wait();
//this->deleteLater();
    event->accept();
}

MVCalibrationDialog::~MVCalibrationDialog(){

//    QMetaObject::invokeMethod(captureWorker, "stopWorking");
//    captureWorkerThread->quit();
//    captureWorkerThread->wait();

//    delete captureWorker;
    delete captureWorkerThread;
    delete ui;
}


void MVCalibrationDialog::on_saveButton_clicked(){

//     Register as meta type and stream operators for use in QSettings
//    qRegisterMetaTypeStreamOperators<MVCalibrationData>("MVCalibrationData");
//    QSettings settings;
//    settings.setValue("Calibration", QVariant::fromValue<MVCalibrationData>(cal));

    cal.save("calibration.xml");

    this->close();
}