stop motion webcam calibration
Co-Authored-By: shun-iwasawa <shun.iwasawa@ghibli.jp>
This commit is contained in:
parent
7b4bc6c721
commit
39575f566d
8 changed files with 490 additions and 4 deletions
BIN
stuff/library/camera calibration/checkerboard.tif
Normal file
BIN
stuff/library/camera calibration/checkerboard.tif
Normal file
Binary file not shown.
19
stuff/library/camera calibration/readme.txt
Normal file
19
stuff/library/camera calibration/readme.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
### How to calibrate camera
|
||||
|
||||
1. Prepare the checkerboard
|
||||
|
||||
- Print out "checkerboard.tif" found in "tahomastuff/library/camera calibration".
|
||||
- It would be better to paste the checkerboard pattern on some flat panel like cardboard.
|
||||
|
||||
2. Capture the pattern
|
||||
|
||||
- In the Stop Motion Controller's "Settings" tab, enable "Calibration" box
|
||||
- Click "Start calibration".
|
||||
- Take 10 snapshots of the checkerboard pattern in various positions and angles.
|
||||
- Once the capturing is done, calibration will be automatically applied whenever the "Calibration" box is checked.
|
||||
|
||||
3. Export & load calibration settings
|
||||
|
||||
- Calibration settings will be saved under "tahomastuff/library/camera calibration" with name "[MACHINENAME]_[CAMERANAME]_[RESOLUTION].xml"
|
||||
- The settings file will be overwritten after finishing the new calibration.
|
||||
- You can export the file for backup, or load it afterwards.
|
|
@ -1391,8 +1391,19 @@ void StopMotion::onTimeout() {
|
|||
}
|
||||
#endif
|
||||
} else {
|
||||
bool success = m_webcam->getWebcamImage(m_liveViewImage);
|
||||
bool calibrateImage = !m_calibration.captureCue &&
|
||||
m_calibration.isValid && m_calibration.isEnabled;
|
||||
bool success =
|
||||
m_webcam->getWebcamImage(m_liveViewImage, calibrateImage,
|
||||
m_calibration.mapX, m_calibration.mapY);
|
||||
if (success) {
|
||||
// capture calibration reference
|
||||
if (m_calibration.captureCue) {
|
||||
m_calibration.captureCue = false;
|
||||
emit(calibrationImageCaptured());
|
||||
return;
|
||||
}
|
||||
|
||||
setLiveViewImage();
|
||||
} else {
|
||||
m_hasLiveViewImage = false;
|
||||
|
|
|
@ -131,6 +131,19 @@ public:
|
|||
// captured images.
|
||||
TPointD m_liveViewDpi = TPointD(0.0, 0.0);
|
||||
|
||||
struct CalibrationData {
|
||||
// Parameters
|
||||
QString filePath;
|
||||
bool captureCue = false;
|
||||
cv::Size boardSize = {10, 7};
|
||||
int refCaptured = 0;
|
||||
std::vector<std::vector<cv::Point3f>> obj_points;
|
||||
std::vector<std::vector<cv::Point2f>> image_points;
|
||||
cv::Mat mapX, mapY;
|
||||
bool isValid = false;
|
||||
bool isEnabled = false;
|
||||
} m_calibration;
|
||||
|
||||
// files and frames
|
||||
void setXSheetFrameNumber(int frameNumber);
|
||||
int getXSheetFrameNumber() { return m_xSheetFrameNumber; }
|
||||
|
@ -283,6 +296,9 @@ signals:
|
|||
|
||||
// test shots
|
||||
void updateTestShots();
|
||||
|
||||
// Calibration
|
||||
void calibrationImageCaptured();
|
||||
};
|
||||
|
||||
#endif // STOPMOTION_H
|
|
@ -32,6 +32,7 @@
|
|||
#include "flipbook.h"
|
||||
#include "iocommand.h"
|
||||
#include "tlevel_io.h"
|
||||
#include "filebrowser.h"
|
||||
|
||||
// TnzQt includes
|
||||
#include "toonzqt/filefield.h"
|
||||
|
@ -64,6 +65,8 @@
|
|||
#include <QToolTip>
|
||||
#include <QSerialPort>
|
||||
#include <QDomDocument>
|
||||
#include <QHostInfo>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <dshow.h>
|
||||
|
@ -82,6 +85,7 @@ TEnv::StringVar CamCapSaveInPopupScene("CamCapSaveInPopupScene", "1");
|
|||
TEnv::IntVar CamCapSaveInPopupAutoSubName("CamCapSaveInPopupAutoSubName", 1);
|
||||
TEnv::IntVar CamCapSaveInPopupCreateSceneInFolder(
|
||||
"CamCapSaveInPopupCreateSceneInFolder", 0);
|
||||
TEnv::IntVar CamCapDoCalibration("CamCapDoCalibration", 0);
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1233,6 +1237,26 @@ StopMotionController::StopMotionController(QWidget *parent) : QWidget(parent) {
|
|||
innerSettingsLayout->addWidget(m_dslrFrame);
|
||||
m_dslrFrame->hide();
|
||||
|
||||
// Calibration
|
||||
m_calibrationUI.groupBox = new QGroupBox(tr("Calibration"), this);
|
||||
m_calibrationUI.capBtn = new QPushButton(tr("Capture"), this);
|
||||
m_calibrationUI.cancelBtn = new QPushButton(tr("Cancel"), this);
|
||||
m_calibrationUI.newBtn = new QPushButton(tr("Start calibration"), this);
|
||||
m_calibrationUI.loadBtn = new QPushButton(tr("Load"), this);
|
||||
m_calibrationUI.exportBtn = new QPushButton(tr("Export"), this);
|
||||
m_calibrationUI.label = new QLabel(this);
|
||||
m_calibrationUI.groupBox->setCheckable(true);
|
||||
m_calibrationUI.groupBox->setChecked(CamCapDoCalibration);
|
||||
QAction *calibrationHelp =
|
||||
new QAction(tr("Open Readme.txt for Camera calibration..."));
|
||||
m_calibrationUI.groupBox->addAction(calibrationHelp);
|
||||
m_calibrationUI.groupBox->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
m_calibrationUI.capBtn->hide();
|
||||
m_calibrationUI.cancelBtn->hide();
|
||||
m_calibrationUI.label->hide();
|
||||
m_calibrationUI.exportBtn->setEnabled(false);
|
||||
connect(calibrationHelp, SIGNAL(triggered()), this, SLOT(onCalibReadme()));
|
||||
|
||||
QVBoxLayout *webcamSettingsLayout = new QVBoxLayout;
|
||||
webcamSettingsLayout->setSpacing(0);
|
||||
webcamSettingsLayout->setMargin(5);
|
||||
|
@ -1360,6 +1384,27 @@ StopMotionController::StopMotionController(QWidget *parent) : QWidget(parent) {
|
|||
imageFrame->setLayout(imageLay);
|
||||
webcamGridLay->addWidget(imageFrame, 6, 0, 1, 2);
|
||||
|
||||
// Calibration
|
||||
QGridLayout *calibLay = new QGridLayout();
|
||||
calibLay->setMargin(8);
|
||||
calibLay->setHorizontalSpacing(3);
|
||||
calibLay->setVerticalSpacing(5);
|
||||
{
|
||||
calibLay->addWidget(m_calibrationUI.newBtn, 0, 0);
|
||||
calibLay->addWidget(m_calibrationUI.loadBtn, 0, 1);
|
||||
calibLay->addWidget(m_calibrationUI.exportBtn, 0, 2);
|
||||
QHBoxLayout *lay = new QHBoxLayout();
|
||||
lay->setMargin(0);
|
||||
lay->setSpacing(5);
|
||||
lay->addWidget(m_calibrationUI.capBtn, 1);
|
||||
lay->addWidget(m_calibrationUI.label, 0);
|
||||
lay->addWidget(m_calibrationUI.cancelBtn, 1);
|
||||
calibLay->addLayout(lay, 1, 0, 1, 3);
|
||||
}
|
||||
calibLay->setColumnStretch(0, 1);
|
||||
m_calibrationUI.groupBox->setLayout(calibLay);
|
||||
webcamGridLay->addWidget(m_calibrationUI.groupBox, 7, 0, 1, 2);
|
||||
|
||||
webcamSettingsLayout->addLayout(webcamGridLay);
|
||||
|
||||
webcamSettingsLayout->addStretch();
|
||||
|
@ -1941,6 +1986,24 @@ StopMotionController::StopMotionController(QWidget *parent) : QWidget(parent) {
|
|||
ret = ret && connect(m_stopMotion->m_webcam, SIGNAL(updateHistogram(cv::Mat)),
|
||||
this, SLOT(onUpdateHistogramCalled(cv::Mat)));
|
||||
|
||||
// Calibration
|
||||
ret = ret && connect(m_calibrationUI.groupBox, &QGroupBox::toggled,
|
||||
[&](bool checked) {
|
||||
CamCapDoCalibration = checked;
|
||||
m_stopMotion->m_calibration.isEnabled = checked;
|
||||
resetCalibSettingsFromFile();
|
||||
});
|
||||
ret = ret && connect(m_calibrationUI.capBtn, SIGNAL(clicked()), this,
|
||||
SLOT(onCalibCapBtnClicked()));
|
||||
ret = ret && connect(m_calibrationUI.newBtn, SIGNAL(clicked()), this,
|
||||
SLOT(onCalibNewBtnClicked()));
|
||||
ret = ret && connect(m_calibrationUI.cancelBtn, SIGNAL(clicked()), this,
|
||||
SLOT(resetCalibSettingsFromFile()));
|
||||
ret = ret && connect(m_calibrationUI.loadBtn, SIGNAL(clicked()), this,
|
||||
SLOT(onCalibLoadBtnClicked()));
|
||||
ret = ret && connect(m_calibrationUI.exportBtn, SIGNAL(clicked()), this,
|
||||
SLOT(onCalibExportBtnClicked()));
|
||||
|
||||
// Lighting Connections
|
||||
ret = ret &&
|
||||
connect(m_screen1ColorFld, SIGNAL(colorChanged(const TPixel32 &, bool)),
|
||||
|
@ -2024,6 +2087,9 @@ StopMotionController::StopMotionController(QWidget *parent) : QWidget(parent) {
|
|||
ret = ret && connect(m_stopMotion, SIGNAL(updateTestShots()), this,
|
||||
SLOT(onRefreshTests()));
|
||||
|
||||
// Calibration
|
||||
ret = ret && connect(m_stopMotion, SIGNAL(calibrationImageCaptured()), this,
|
||||
SLOT(onCalibImageCaptured()));
|
||||
assert(ret);
|
||||
|
||||
m_placeOnXSheetCB->setChecked(
|
||||
|
@ -2051,6 +2117,8 @@ StopMotionController::StopMotionController(QWidget *parent) : QWidget(parent) {
|
|||
m_stopMotion->setToNextNewLevel();
|
||||
m_saveInFileFld->setPath(m_stopMotion->getFilePath());
|
||||
|
||||
m_stopMotion->m_calibration.isEnabled = m_calibrationUI.groupBox->isChecked();
|
||||
|
||||
#ifndef _WIN32
|
||||
m_directShowCB->hide();
|
||||
#endif
|
||||
|
@ -2773,6 +2841,12 @@ void StopMotionController::onCameraListComboActivated(int comboIndex) {
|
|||
|
||||
m_stopMotion->changeCameras(comboIndex);
|
||||
m_stopMotion->updateStopMotionControls(m_stopMotion->m_usingWebcam);
|
||||
|
||||
if (m_calibrationUI.groupBox->isChecked() && comboIndex > 0) {
|
||||
m_stopMotion->m_calibration.isValid = false;
|
||||
m_calibrationUI.exportBtn->setEnabled(false);
|
||||
if (m_stopMotion->m_usingWebcam) resetCalibSettingsFromFile();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -2862,6 +2936,10 @@ void StopMotionController::onNewWebcamResolutionSelected(int index) {
|
|||
|
||||
void StopMotionController::onResolutionComboActivated(const QString &itemText) {
|
||||
m_stopMotion->setWebcamResolution(itemText);
|
||||
|
||||
m_stopMotion->m_calibration.isValid = false;
|
||||
m_calibrationUI.exportBtn->setEnabled(false);
|
||||
if (m_stopMotion->m_usingWebcam) resetCalibSettingsFromFile();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -3909,3 +3987,312 @@ void StopMotionController::clearTests() {
|
|||
m_testHBoxes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::onCalibCapBtnClicked() {
|
||||
if (!m_stopMotion->m_hasLiveViewImage ||
|
||||
m_stopMotion->m_liveViewStatus !=
|
||||
m_stopMotion->LiveViewStatus::LiveViewOpen) {
|
||||
DVGui::warning(tr("Cannot capture image unless live view is active."));
|
||||
return;
|
||||
}
|
||||
m_stopMotion->m_calibration.captureCue = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::onCalibNewBtnClicked() {
|
||||
if (m_stopMotion->m_calibration.isValid) {
|
||||
QString question = tr("Do you want to restart camera calibration?");
|
||||
int ret =
|
||||
DVGui::MsgBox(question, QObject::tr("Restart"), QObject::tr("Cancel"));
|
||||
if (ret == 0 || ret == 2) return;
|
||||
}
|
||||
// initialize calibration parameter
|
||||
m_stopMotion->m_calibration.filePath = getCurrentCalibFilePath();
|
||||
m_stopMotion->m_calibration.captureCue = false;
|
||||
m_stopMotion->m_calibration.refCaptured = 0;
|
||||
m_stopMotion->m_calibration.obj_points.clear();
|
||||
m_stopMotion->m_calibration.image_points.clear();
|
||||
m_stopMotion->m_calibration.isValid = false;
|
||||
|
||||
// initialize label
|
||||
m_calibrationUI.label->setText(
|
||||
QString("%1/%2").arg(m_stopMotion->m_calibration.refCaptured).arg(10));
|
||||
// swap UIs
|
||||
m_calibrationUI.newBtn->hide();
|
||||
m_calibrationUI.loadBtn->hide();
|
||||
m_calibrationUI.exportBtn->hide();
|
||||
m_calibrationUI.label->show();
|
||||
m_calibrationUI.capBtn->show();
|
||||
m_calibrationUI.cancelBtn->show();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::resetCalibSettingsFromFile() {
|
||||
if (m_calibrationUI.capBtn->isVisible()) {
|
||||
// swap UIs
|
||||
m_calibrationUI.label->hide();
|
||||
m_calibrationUI.capBtn->hide();
|
||||
m_calibrationUI.cancelBtn->hide();
|
||||
m_calibrationUI.newBtn->show();
|
||||
m_calibrationUI.loadBtn->show();
|
||||
m_calibrationUI.exportBtn->show();
|
||||
}
|
||||
if (m_calibrationUI.groupBox->isChecked() &&
|
||||
!m_stopMotion->m_calibration.isValid) {
|
||||
QString calibFp = getCurrentCalibFilePath();
|
||||
std::cout << calibFp.toStdString() << std::endl;
|
||||
if (!calibFp.isEmpty() && QFileInfo(calibFp).exists()) {
|
||||
cv::Mat intrinsic, distCoeffs, new_intrinsic;
|
||||
cv::FileStorage fs(calibFp.toStdString(), cv::FileStorage::READ);
|
||||
if (!fs.isOpened()) return;
|
||||
std::string identifierStr;
|
||||
fs["identifier"] >> identifierStr;
|
||||
if (identifierStr != "OpenToonzCameraCalibrationSettings") return;
|
||||
cv::Size resolution;
|
||||
QSize currentResolution(m_stopMotion->m_webcam->getWebcamWidth(),
|
||||
m_stopMotion->m_webcam->getWebcamHeight());
|
||||
fs["resolution"] >> resolution;
|
||||
if (currentResolution != QSize(resolution.width, resolution.height))
|
||||
return;
|
||||
fs["instrinsic"] >> intrinsic;
|
||||
fs["distCoeffs"] >> distCoeffs;
|
||||
fs["new_intrinsic"] >> new_intrinsic;
|
||||
fs.release();
|
||||
|
||||
cv::Mat mapR = cv::Mat::eye(3, 3, CV_64F);
|
||||
cv::initUndistortRectifyMap(
|
||||
intrinsic, distCoeffs, mapR, new_intrinsic,
|
||||
cv::Size(currentResolution.width(), currentResolution.height()),
|
||||
CV_32FC1, m_stopMotion->m_calibration.mapX,
|
||||
m_stopMotion->m_calibration.mapY);
|
||||
|
||||
m_stopMotion->m_calibration.isValid = true;
|
||||
m_stopMotion->m_calibration.filePath = calibFp;
|
||||
m_calibrationUI.exportBtn->setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::captureCalibrationRefImage(cv::Mat &image) {
|
||||
cv::cvtColor(image, image, cv::COLOR_RGB2GRAY);
|
||||
std::vector<cv::Point2f> corners;
|
||||
bool found = cv::findChessboardCorners(
|
||||
image, m_stopMotion->m_calibration.boardSize, corners,
|
||||
cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FILTER_QUADS);
|
||||
if (found) {
|
||||
// compute corners in detail
|
||||
cv::cornerSubPix(
|
||||
image, corners, cv::Size(11, 11), cv::Size(-1, -1),
|
||||
cv::TermCriteria(cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER, 30,
|
||||
0.1));
|
||||
// count up
|
||||
m_stopMotion->m_calibration.refCaptured++;
|
||||
// register corners
|
||||
m_stopMotion->m_calibration.image_points.push_back(corners);
|
||||
// register 3d points in real world space
|
||||
std::vector<cv::Point3f> obj;
|
||||
for (int i = 0; i < m_stopMotion->m_calibration.boardSize.width *
|
||||
m_stopMotion->m_calibration.boardSize.height;
|
||||
i++)
|
||||
obj.push_back(cv::Point3f(i / m_stopMotion->m_calibration.boardSize.width,
|
||||
i % m_stopMotion->m_calibration.boardSize.width,
|
||||
0.0f));
|
||||
m_stopMotion->m_calibration.obj_points.push_back(obj);
|
||||
|
||||
// needs 10 references
|
||||
if (m_stopMotion->m_calibration.refCaptured < 10) {
|
||||
// update label
|
||||
m_calibrationUI.label->setText(
|
||||
QString("%1/%2")
|
||||
.arg(m_stopMotion->m_calibration.refCaptured)
|
||||
.arg(10));
|
||||
} else {
|
||||
// swap UIs
|
||||
m_calibrationUI.label->hide();
|
||||
m_calibrationUI.capBtn->hide();
|
||||
m_calibrationUI.cancelBtn->hide();
|
||||
m_calibrationUI.newBtn->show();
|
||||
m_calibrationUI.loadBtn->show();
|
||||
m_calibrationUI.exportBtn->show();
|
||||
|
||||
cv::Mat intrinsic = cv::Mat(3, 3, CV_32FC1);
|
||||
intrinsic.ptr<float>(0)[0] = 1.f;
|
||||
intrinsic.ptr<float>(1)[1] = 1.f;
|
||||
cv::Mat distCoeffs;
|
||||
std::vector<cv::Mat> rvecs;
|
||||
std::vector<cv::Mat> tvecs;
|
||||
cv::calibrateCamera(m_stopMotion->m_calibration.obj_points,
|
||||
m_stopMotion->m_calibration.image_points,
|
||||
image.size(), intrinsic, distCoeffs, rvecs, tvecs);
|
||||
|
||||
cv::Mat mapR = cv::Mat::eye(3, 3, CV_64F);
|
||||
cv::Mat new_intrinsic = cv::getOptimalNewCameraMatrix(
|
||||
intrinsic, distCoeffs, image.size(),
|
||||
0.0); // setting the last argument to 1.0 will include all source
|
||||
// pixels in the frame
|
||||
cv::initUndistortRectifyMap(
|
||||
intrinsic, distCoeffs, mapR, new_intrinsic, image.size(), CV_32FC1,
|
||||
m_stopMotion->m_calibration.mapX, m_stopMotion->m_calibration.mapY);
|
||||
|
||||
// save calibration settings
|
||||
QString calibFp = getCurrentCalibFilePath();
|
||||
cv::FileStorage fs(calibFp.toStdString(), cv::FileStorage::WRITE);
|
||||
if (!fs.isOpened()) {
|
||||
DVGui::warning(
|
||||
tr("Failed to save calibration settings to %1.").arg(calibFp));
|
||||
return;
|
||||
}
|
||||
fs << "identifier"
|
||||
<< "OpenToonzCameraCalibrationSettings";
|
||||
fs << "resolution" << cv::Size(m_stopMotion->m_webcam->getWebcamWidth(),
|
||||
m_stopMotion->m_webcam->getWebcamHeight());
|
||||
fs << "instrinsic" << intrinsic;
|
||||
fs << "distCoeffs" << distCoeffs;
|
||||
fs << "new_intrinsic" << new_intrinsic;
|
||||
fs.release();
|
||||
|
||||
m_stopMotion->m_calibration.isValid = true;
|
||||
m_calibrationUI.exportBtn->setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
QString StopMotionController::getCurrentCalibFilePath() {
|
||||
QString cameraName = m_cameraListCombo->currentText();
|
||||
if (cameraName.isEmpty()) return QString();
|
||||
QString resolution = m_resolutionCombo->currentText();
|
||||
QString hostName = QHostInfo::localHostName();
|
||||
TFilePath folderPath = ToonzFolder::getLibraryFolder() + "camera calibration";
|
||||
return folderPath.getQString() + "\\" + hostName + "_" + cameraName + "_" +
|
||||
resolution + ".xml";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::onCalibLoadBtnClicked() {
|
||||
LoadCalibrationFilePopup popup(this);
|
||||
|
||||
QString fp = popup.getPath().getQString();
|
||||
if (fp.isEmpty()) return;
|
||||
try {
|
||||
cv::FileStorage fs(fp.toStdString(), cv::FileStorage::READ);
|
||||
if (!fs.isOpened())
|
||||
throw TException(fp.toStdWString() + L": Can't open file");
|
||||
|
||||
std::string identifierStr;
|
||||
fs["identifier"] >> identifierStr;
|
||||
if (identifierStr != "OpenToonzCameraCalibrationSettings")
|
||||
throw TException(fp.toStdWString() + L": Identifier does not match");
|
||||
cv::Size resolution;
|
||||
QSize currentResolution(m_stopMotion->m_webcam->getWebcamWidth(),
|
||||
m_stopMotion->m_webcam->getWebcamHeight());
|
||||
fs["resolution"] >> resolution;
|
||||
if (currentResolution != QSize(resolution.width, resolution.height))
|
||||
throw TException(fp.toStdWString() + L": Resolution does not match");
|
||||
} catch (const TException &se) {
|
||||
DVGui::warning(QString::fromStdWString(se.getMessage()));
|
||||
return;
|
||||
} catch (...) {
|
||||
DVGui::error(tr("Couldn't load %1").arg(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_stopMotion->m_calibration.isValid) {
|
||||
QString question = tr("Overwriting the current calibration. Are you sure?");
|
||||
int ret = DVGui::MsgBox(question, QObject::tr("OK"), QObject::tr("Cancel"));
|
||||
if (ret == 0 || ret == 2) return;
|
||||
m_stopMotion->m_calibration.isValid = false;
|
||||
}
|
||||
|
||||
QString calibFp = getCurrentCalibFilePath();
|
||||
TSystem::copyFile(TFilePath(calibFp), TFilePath(fp), true);
|
||||
resetCalibSettingsFromFile();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::onCalibExportBtnClicked() {
|
||||
// just in case
|
||||
if (!m_stopMotion->m_calibration.isValid) return;
|
||||
if (!QFileInfo(getCurrentCalibFilePath()).exists()) return;
|
||||
|
||||
ExportCalibrationFilePopup popup(this);
|
||||
|
||||
QString fp = popup.getPath().getQString();
|
||||
if (fp.isEmpty()) return;
|
||||
|
||||
try {
|
||||
{
|
||||
QFileInfo fs(fp);
|
||||
if (fs.exists() && !fs.isWritable()) {
|
||||
throw TSystemException(
|
||||
TFilePath(fp),
|
||||
L"The file cannot be saved: it is a read only file.");
|
||||
}
|
||||
}
|
||||
TSystem::copyFile(TFilePath(fp), TFilePath(getCurrentCalibFilePath()),
|
||||
true);
|
||||
} catch (const TSystemException &se) {
|
||||
DVGui::warning(QString::fromStdWString(se.getMessage()));
|
||||
} catch (...) {
|
||||
DVGui::error(tr("Couldn't save %1").arg(fp));
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::onCalibReadme() {
|
||||
TFilePath readmeFp =
|
||||
ToonzFolder::getLibraryFolder() + "camera calibration" + "readme.txt";
|
||||
if (!TFileStatus(readmeFp).doesExist()) return;
|
||||
if (TSystem::isUNC(readmeFp))
|
||||
QDesktopServices::openUrl(QUrl(readmeFp.getQString()));
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(readmeFp.getQString()));
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StopMotionController::onCalibImageCaptured() {
|
||||
cv::Mat camImage = m_stopMotion->m_webcam->getWebcamImage();
|
||||
captureCalibrationRefImage(camImage);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
ExportCalibrationFilePopup::ExportCalibrationFilePopup(QWidget *parent)
|
||||
: GenericSaveFilePopup(tr("Export Camera Calibration Settings")) {
|
||||
Qt::WindowFlags flags = windowFlags();
|
||||
setParent(parent);
|
||||
setWindowFlags(flags);
|
||||
m_browser->enableGlobalSelection(false);
|
||||
setFilterTypes(QStringList("xml"));
|
||||
}
|
||||
|
||||
void ExportCalibrationFilePopup::showEvent(QShowEvent *e) {
|
||||
FileBrowserPopup::showEvent(e);
|
||||
setFolder(ToonzFolder::getLibraryFolder() + "camera calibration");
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
LoadCalibrationFilePopup::LoadCalibrationFilePopup(QWidget *parent)
|
||||
: GenericLoadFilePopup(tr("Load Camera Calibration Settings")) {
|
||||
Qt::WindowFlags flags = windowFlags();
|
||||
setParent(parent);
|
||||
setWindowFlags(flags);
|
||||
m_browser->enableGlobalSelection(false);
|
||||
setFilterTypes(QStringList("xml"));
|
||||
}
|
||||
|
||||
void LoadCalibrationFilePopup::showEvent(QShowEvent *e) {
|
||||
FileBrowserPopup::showEvent(e);
|
||||
setFolder(ToonzFolder::getLibraryFolder() + "camera calibration");
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "tfilepath.h"
|
||||
#include "toonz/tproject.h"
|
||||
#include "filebrowserpopup.h"
|
||||
|
||||
// TnzQt includes
|
||||
#include "toonzqt/tabbar.h"
|
||||
|
@ -236,6 +237,17 @@ class StopMotionController final : public QWidget {
|
|||
QVBoxLayout *m_testsInsideLayout;
|
||||
int m_testsImagesPerRow;
|
||||
|
||||
// calibration feature
|
||||
struct CalibrationUI {
|
||||
QPushButton *capBtn, *newBtn, *loadBtn, *cancelBtn, *exportBtn;
|
||||
QLabel *label;
|
||||
QGroupBox *groupBox;
|
||||
} m_calibrationUI;
|
||||
|
||||
void captureCalibrationRefImage(cv::Mat &procImage);
|
||||
|
||||
QString getCurrentCalibFilePath();
|
||||
|
||||
public:
|
||||
StopMotionController(QWidget *parent = 0);
|
||||
~StopMotionController();
|
||||
|
@ -415,8 +427,38 @@ protected slots:
|
|||
void onRefreshTests();
|
||||
void clearTests();
|
||||
|
||||
void onCalibCapBtnClicked();
|
||||
void onCalibNewBtnClicked();
|
||||
void resetCalibSettingsFromFile();
|
||||
void onCalibLoadBtnClicked();
|
||||
void onCalibExportBtnClicked();
|
||||
void onCalibImageCaptured();
|
||||
void onCalibReadme();
|
||||
|
||||
public slots:
|
||||
void openSaveInFolderPopup();
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
class ExportCalibrationFilePopup final : public GenericSaveFilePopup {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportCalibrationFilePopup(QWidget *parent);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *) override;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
class LoadCalibrationFilePopup final : public GenericLoadFilePopup {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoadCalibrationFilePopup(QWidget *parent);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *) override;
|
||||
};
|
||||
|
||||
#endif // STOPMOTIONCONTROLLER_H
|
||||
|
|
|
@ -92,7 +92,8 @@ int Webcam::getIndexOfResolution() {
|
|||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
bool Webcam::getWebcamImage(TRaster32P& tempImage) {
|
||||
bool Webcam::getWebcamImage(TRaster32P& tempImage, bool useCalibration,
|
||||
cv::Mat calibrationMapX, cv::Mat calibrationMapY) {
|
||||
bool error = false;
|
||||
cv::Mat imgOriginal;
|
||||
cv::Mat imgCorrected;
|
||||
|
@ -146,6 +147,14 @@ bool Webcam::getWebcamImage(TRaster32P& tempImage) {
|
|||
cv::cvtColor(imgCorrected, imgCorrected, cv::COLOR_GRAY2BGRA);
|
||||
}
|
||||
|
||||
// perform calibration
|
||||
if (useCalibration) {
|
||||
cv::remap(imgCorrected, imgCorrected, calibrationMapX, calibrationMapY,
|
||||
cv::INTER_LINEAR);
|
||||
}
|
||||
|
||||
m_webcamImage = imgCorrected;
|
||||
|
||||
int width = m_cvWebcam.get(3);
|
||||
int height = m_cvWebcam.get(4);
|
||||
int size = imgCorrected.total() * imgCorrected.elemSize();
|
||||
|
|
|
@ -41,7 +41,9 @@ public:
|
|||
QCamera* getWebcam() { return m_webcam; }
|
||||
void setWebcam(QCamera* camera);
|
||||
bool initWebcam(int index = 0);
|
||||
bool getWebcamImage(TRaster32P& tempImage);
|
||||
bool getWebcamImage(TRaster32P& tempImage, bool useCalibration = false,
|
||||
cv::Mat calibrationMapX = cv::Mat(),
|
||||
cv::Mat calibrationMapY = cv::Mat());
|
||||
|
||||
bool translateIndex(int index);
|
||||
|
||||
|
|
Loading…
Reference in a new issue