" +
+ row("left click drag","start outline and drag to other side of mirror")+
+ row("shift left click drag", "move outline")+
+ row("ctrl wheel"," increse \\ decrease outline diameter")+
+ row("wheel", "zoom igram image")+
+ "
"
+ "/ - Toggle to display\\hide this";
+ }
+
+ return "Hot Keys
No active hot keys for this view.
";
+}
int showmem(const QString &t);
void MainWindow::openWaveFrontonInit(QStringList args){
QProgressDialog pd(" Loading wavefronts in progress.", "Cancel", 0, 100);
@@ -868,7 +999,7 @@ void MainWindow::on_actionWavefront_triggered()
rad -= border;
cv::Mat result = zernikeProcess::get_Instance()->makeSurfaceFromZerns(border, false);
- m_surfaceManager->createSurfaceFromPhaseMap(result,
+ m_surfaceManager->createWaveFrontFromPhaseMap(result,
CircleOutline(QPointF(xcen,ycen),rad),
CircleOutline(QPointF(0,0),0),
QString("Simulated_Wavefront"), WavefrontOrigin::Simulation);
@@ -1978,7 +2109,6 @@ void MainWindow::on_autoOutlineHelp_clicked()
QString link = qApp->applicationDirPath() + "/res/Help/outlineing.html";
QDesktopServices::openUrl(QUrl(link));
}
-#include "transformwavefrontdlg.h"
void MainWindow::on_actionwave_front_transforms_triggered()
{
m_surfaceManager->transform();
@@ -2150,30 +2280,66 @@ void MainWindow::on_actionStop_auto_invert_triggered()
}
/*
- This is called iwth shortcut Qt::Key_U
+ This is called With shortcut Qt::Key_U or by the live view automation loop
It's a sort of easter egg for people using skysolve camera with their Bath setup.
*/
-void MainWindow::load_from_url(){
-
- // Construct the URL with IP address and port
- QUrl url;
- url.setScheme("http"); // or "https" if using SSL
- url.setHost("192.168.50.5"); // Replace with your IP address
- url.setPort(5000); // Specify the port
- url.setPath("/downloadImage");
- showMessage("Connecting to " + url.toString(),1);
- //downloader.startDownload(url);
-
- QEventLoop loop;
- QNetworkAccessManager nam;
- QNetworkRequest req(url);
- QNetworkReply *reply = nam.get(req);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- //connect(reply, &QNetworkReply::downloadProgress, this, [&](qint64 bytesReceived, qint64 bytesTotal){
- // showMessage(QString("byes received %1").arg( bytesReceived) + QString(" total %1").arg(bytesTotal),1);
- //});
- loop.exec();
- QByteArray buffer = reply->readAll();
+QString MainWindow::load_from_url(){
+ // if live view is open use it
+ QImage image;
+
+ //if live view is not available.
+ if (!m_viewDlg || !m_viewDlg->isVisible()){
+
+
+ // Construct the URL with IP address and port
+ QUrl url;
+ url.setScheme("http"); // or "https" if using SSL
+ url.setHost("192.168.50.5"); // Replace with your IP address
+ url.setPort(5000); // Specify the port
+ url.setPath("/downloadImage");
+ showMessage("Connecting to " + url.toString(),1);
+ //downloader.startDownload(url);
+
+ QEventLoop loop;
+ QNetworkAccessManager nam;
+ QNetworkRequest req(url);
+ QNetworkReply *reply = nam.get(req);
+ connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+ //connect(reply, &QNetworkReply::downloadProgress, this, [&](qint64 bytesReceived, qint64 bytesTotal){
+ // showMessage(QString("byes received %1").arg( bytesReceived) + QString(" total %1").arg(bytesTotal),1);
+ //});
+ loop.exec();
+ QByteArray buffer = reply->readAll();
+ QSettings set;
+ QString dirPath = set.value("importIgramPath",".").toString();
+ // Get the current date and time
+ QDateTime currentDateTime = QDateTime::currentDateTime();
+
+ // Format the date and time into a string suitable for a filename
+ // Using "yyyyMMdd_HHmmss" for a clear, sortable format without illegal characters
+ QString dateTimeString = currentDateTime.toString("yyyyMMdd_HHmmss");
+
+ // Construct the full filename
+ QString fileName = dirPath + "/" + dateTimeString +".jpg";
+ // Create a QImage from the QByteArray
+
+ if (image.loadFromData(buffer, "jpg")) { // Specify format if known, otherwise omit
+ showMessage("Image loaded successfully " + fileName,1);
+ // Now you can use the 'image' QImage object
+ // Example: Save it to another file
+ image.save(fileName, "JPG");
+ importIgram();
+ } else {
+ showMessage("Failed to load image!",2);
+ }
+ }
+ else {
+ image = m_viewDlg->getFrame();
+ if (image.isNull()){
+ return ("");
+
+ }
+ }
QSettings set;
QString dirPath = set.value("importIgramPath",".").toString();
// Get the current date and time
@@ -2185,17 +2351,515 @@ void MainWindow::load_from_url(){
// Construct the full filename
QString fileName = dirPath + "/" + dateTimeString +".jpg";
- qDebug() << "download filename" << fileName;
- // Create a QImage from the QByteArray
- QImage image;
- if (image.loadFromData(buffer, "jpg")) { // Specify format if known, otherwise omit
- showMessage("Image loaded successfully " + fileName,1);
- // Now you can use the 'image' QImage object
- // Example: Save it to another file
- image.save(fileName, "JPG");
- importIgram();
+
+ image.save(fileName, "JPG");
+ importIgram();
+ return fileName;
+}
+
+void MainWindow::on_actionLive_view_triggered()
+{
+ if (!m_viewDlg) {
+ if (!m_liveViewRMSTimer){
+ m_liveViewRMSTimer = new QTimer();
+ }
+
+ m_viewDlg = new LiveViewDialog(this);//("http://192.168.50.5:5000/video_feed");
+ // compute the size of the current filter circle and set it to the viewDLG
+ connect(m_liveViewRMSTimer, &QTimer::timeout, this, [this](){
+ if (m_viewDlg)
+ m_viewDlg->m_tmpShowLive = false;
+
+ });
+ connect(m_igramArea, &IgramArea::boundary, this, [this](CircleOutline outside, bool cropped, QPointF offset) {
+
+ if (cropped){
+ outside.translate(offset);
+ }
+
+ m_viewDlg->setOutsidecircle(outside.m_center, outside.m_radius);
+ });
+
+ // Automatically nullify the pointer when Qt deletes the dialog on close
+ connect(m_viewDlg, &QObject::destroyed, this, [this]() {
+ m_viewDlg = nullptr;
+ });
+
+ connect(m_viewDlg, &LiveViewDialog::igramCaptured, this, &MainWindow::load_from_url);
+ // Connect the Start button on the dialog to trigger your analysis loop
+ connect(m_viewDlg->startAnalysisBtn, &QPushButton::clicked, this, [this]() {
+ // Using single-shot timer ensures the click event finishes before entering the loop
+ QTimer::singleShot(0, this, &MainWindow::startLiveButton_clicked);
+ });
+
+ // Connect the Stop button to signal the loop to terminate
+ connect(m_viewDlg->stopAnalysisBtn, &QPushButton::clicked, this,&MainWindow::stopLiveButton_clicked);
+
+ // Connect the Stop button to signal the loop to terminate
+ connect(m_viewDlg, &LiveViewDialog::streamDisconnected, this, &MainWindow::stopLiveButton_clicked);
+ connect(m_viewDlg->pauseAnalyBtn, &QPushButton::clicked,this, &MainWindow::pauseLiveButton_clicked);
+
+ connect(m_viewDlg->imageLabel, &LiveImageView::mirrorDefined, this, [this](const QPointF center, double radius){
+
+ m_igramArea->blockSignals(true);
+ m_igramArea->setOutsideOutline(center,radius);
+ m_igramArea->blockSignals(false);
+
+ });
+ connect(m_viewDlg, &LiveViewDialog::blueCircle, m_dftArea,&DFTArea::mouseMovedExternal);
+ connect(m_dftArea, &DFTArea::filterSizeScale, m_viewDlg, &LiveViewDialog::setCenterFilter );
+ connect(m_viewDlg, &LiveViewDialog::autoOutLine, m_igramArea, &IgramArea::autoTraceOutline);
+ connect(m_viewDlg, &LiveViewDialog::outlineOk, this,[this](){
+ m_igramArea->nextStep();
+ });
+ m_viewDlg->show();
+ } else {
+ // If it's already open, just bring it to the front
+ m_viewDlg->raise();
+ m_viewDlg->activateWindow();
+ }
+
+ m_viewDlg->show();
+}
+
+void MainWindow::startLiveButton_clicked() {
+
+ // If already running, do nothing or treat as restart
+ if (m_liveState == State_Running) return;
+
+ if (m_liveState == State_Stopped) {
+ // Fresh start: clear previous average data
+ m_liveValidCount = 0;
+ m_liveSum.release();
+ if (!m_liveAverageWf) {
+ m_liveAverageWf = new wavefront;
+ }
+ }
+ setLiveViewMode(true);
+ m_liveState = State_Running;
+ m_viewDlg->startAnalysisBtn->hide();
+ m_viewDlg->pauseAnalyBtn->setText("Pause");
+ m_viewDlg->pauseAnalyBtn->show();
+ m_viewDlg->stopAnalysisBtn->show();
+ m_viewDlg->saveAverageBtn->hide();
+ m_viewDlg->grabButton->hide();
+ m_viewDlg->OutlineBtn->hide();
+ m_viewDlg->OutlineOkBtn->hide();
+
+
+
+ // If the loop was completely stopped, start the execution flow again
+ if (!m_liveLoopActive) {
+ runLiveAnalysisLoop();
+ }
+}
+
+void MainWindow::pauseLiveButton_clicked() {
+ if (m_liveState == State_Running) {
+ m_liveState = State_Paused;
+ m_viewDlg->pauseAnalyBtn->setText("Resume");
+ m_viewDlg->grabButton->show();
+ m_viewDlg->OutlineBtn->show();
+ m_viewDlg->OutlineOkBtn->show();
+ setLiveViewMode(false);
+
+
+ } else if (m_liveState == State_Paused) {
+ m_liveState = State_Running;
+ m_viewDlg->pauseAnalyBtn->setText("Pause");
+ m_viewDlg->saveAverageBtn->hide();
+ m_igramArea->hide();
+ m_dftArea->hide();
+ m_viewDlg->grabButton->hide();
+ m_viewDlg->OutlineBtn->hide();
+ m_viewDlg->OutlineOkBtn->hide();
+
+ setLiveViewMode(true);
+
+
+ }
+}
+
+void MainWindow::stopLiveButton_clicked() {
+ m_liveState = State_Stopped;
+ m_liveLoopActive = false;
+ m_viewDlg->pauseAnalyBtn->setText("Pause");
+ m_viewDlg->pauseAnalyBtn->hide();
+ m_viewDlg->stopAnalysisBtn->hide();
+ m_viewDlg->startAnalysisBtn->show();
+ m_viewDlg->saveAverageBtn->hide();
+ m_viewDlg->grabButton->show();
+ m_viewDlg->OutlineBtn->show();
+ m_viewDlg->OutlineOkBtn->show();
+ setLiveViewMode(false);
+
+}
+
+void MainWindow::setLiveViewMode(bool active) {
+ if (active) {
+ // --- LOCKDOWN: Enter Live Mode ---
+ ui->tabWidget->setCurrentIndex(2);
+ m_igramArea->setUpdatesEnabled(false);
+ scrollAreaDft->setUpdatesEnabled(false);
+
+ // Sever automatic tab-switching signals during loop execution
+ disconnect(m_igramArea, &IgramArea::showTab, ui->tabWidget, &QTabWidget::setCurrentIndex);
+ disconnect(m_surfaceManager, &SurfaceManager::showTab, ui->tabWidget, &QTabWidget::setCurrentIndex);
+
+ m_ogl->setLivePreviewMode(true);
} else {
- showMessage("Failed to load image!",2);
+ // --- RESTORATION: Exit Live Mode ---
+ m_igramArea->show();
+ connect(m_igramArea, &IgramArea::showTab, ui->tabWidget, &QTabWidget::setCurrentIndex);
+ connect(m_surfaceManager, &SurfaceManager::showTab, ui->tabWidget, &QTabWidget::setCurrentIndex);
+
+ m_dftArea->show();
+
+ ui->tabWidget->blockSignals(false);
+ ui->tabWidget->setUpdatesEnabled(true);
+
+ scrollAreaDft->setUpdatesEnabled(true);
+ m_igramArea->setUpdatesEnabled(true);
+
+ m_ogl->setLivePreviewMode(false);
+
+ }
+}
+void MainWindow::runLiveAnalysisLoop() {
+ if (m_liveLoopActive) return;
+ static mirrorDlg *md = mirrorDlg::get_Instance();
+ m_liveLoopActive = true;
+ double z8Null = md->z8 * md->cc;
+ cv::Scalar mean;
+ cv::Scalar stddev;
+ QFont tFont("Serif", 18);
+
+ // Reset average tracking for the start of the session
+ m_liveValidCount = 1;
+ m_liveSum.release();
+
+ int totalFrames = 1; // Local frame counter for non-averaged views
+ m_viewDlg->loopRunning = true;
+ // have the 3D view switch to a widget that does not blink
+
+ bool computeAverage = m_viewDlg->averageMode->isChecked();
+ while (m_liveState != State_Stopped && !m_viewDlg->m_stopRequested) {
+
+ computeAverage = m_viewDlg->averageMode->isChecked();
+ bool displayAverage = computeAverage;
+ QApplication::processEvents();
+
+ // ---------------------------------------------------------------------
+ // PAUSED STATE HANDLER
+ // ---------------------------------------------------------------------
+ if (m_liveState == State_Paused) {
+ QApplication::processEvents();
+
+ if (m_liveAverageWf && computeAverage) {
+ m_viewDlg->saveAverageBtn->show();
+ }
+
+ if (m_viewDlg->saveAverage && m_liveAverageWf) {
+ // Store a DEEP COPY snapshot so future frame accumulation won't alter this saved record
+ wavefront *savedAvg = new wavefront(*m_liveAverageWf);
+ savedAvg->name = QString("Average_%1").arg(m_liveValidCount - 1);
+
+ m_surfaceManager->m_wavefronts << savedAvg;
+ m_surfTools->addWaveFront(savedAvg->name);
+ m_viewDlg->saveAverage = false;
+ }
+ continue;
+ }
+
+ if (m_liveState == State_Stopped) break;
+
+
+
+ // 2. Open the saved image in the pipeline
+ QImage img = m_viewDlg->getFrame();
+
+ m_igramArea->openImage(img, true, QString("stream%1").arg(m_liveValidCount));
+ QApplication::processEvents();
+ if (m_liveState == State_Stopped) break;
+
+ // 3. Check outline validity
+ if (m_igramArea->m_outside.m_radius == 0) {
+ m_viewDlg->statusLeft->setText(
+ " The outline is NOT valid for this image "
+ );
+ break;
+ }
+
+ QApplication::processEvents();
+ if (m_liveState == State_Stopped) break;
+
+ // 4. Advance through processing steps
+ m_igramArea->nextStep();
+ QApplication::processEvents();
+ if (m_liveState == State_Stopped) break;
+
+ // 5. Switch to DFT tab and generate surface
+ ui->tabWidget->setCurrentIndex(2);
+ m_dftTools->wasPressed = true;
+
+ m_dftArea->makeSurface();
+
+ QApplication::processEvents();
+ if (!m_dftArea->success || m_surfaceManager->m_wavefronts.isEmpty()) {
+ qDebug() << "Warning: Surface generation failed or wavefront list empty. Skipping frame.";
+ continue;
+ }
+
+ // Deep copy the newly generated wavefront from m_wavefronts.back()
+ wavefront *wf = new wavefront(*m_surfaceManager->m_wavefronts.back());
+
+ if (!(m_viewDlg->FirstWaveFrontSeen) && m_viewDlg->autoRMSatStarup->isChecked()) {
+ m_viewDlg->maxRMS->setValue(wf->std * m_viewDlg->RMSMargin->value());
+ m_viewDlg->FirstWaveFrontSeen = true;
+ }
+
+ // Defensive check: Ensure matrix data is allocated
+ if (wf->workData.empty() || wf->workData.rows <= 0 || wf->workData.cols <= 0) {
+ qDebug() << "Error: Wavefront workData is empty or invalid!";
+ delete wf;
+ continue;
+ }
+
+
+ // ---------------------------------------------------------------------
+ // ACCUMULATION / AVERAGE CALCULATION
+ // ---------------------------------------------------------------------
+ m_viewDlg->m_tmpShowLive = false;
+ if (computeAverage) {
+ if (wf->std > m_viewDlg->maxRMS->value()) {
+ m_viewDlg->m_tmpShowLive = true;
+ m_liveViewRMSTimer->start(2000); // Display live for 2 seconds
+ } else {
+ // First valid frame in the averaging run
+ if (m_liveValidCount == 1 || m_liveSum.empty()) {
+ m_liveSum = cv::Mat::zeros(wf->workData.rows, wf->workData.cols, wf->workData.type());
+ m_liveDataSum = cv::Mat::zeros(wf->workData.rows, wf->workData.cols, wf->data.type());
+ if (m_liveAverageWf) {
+ delete m_liveAverageWf;
+ }
+ m_liveAverageWf = new wavefront(*wf); // Deep copy initial wavefront properties
+ m_liveAverageWf->m_origin = WavefrontOrigin::Average;
+ }
+
+ // Verify dimensions before accumulating
+ if (m_liveSum.rows == wf->workData.rows && m_liveSum.cols == wf->workData.cols) {
+ m_liveSum += wf->workData;
+ m_liveDataSum += wf->data;
+
+ cv::Mat result = m_liveSum / static_cast(m_liveValidCount);
+
+ // Compute statistics strictly inside the pupil mask
+ cv::meanStdDev(result, mean, stddev, m_liveAverageWf->mask);
+
+ // Update surface matrices and calculated statistics
+ m_liveAverageWf->workData = result.clone();
+ m_liveAverageWf->data = result.clone();
+ m_liveAverageWf->nulledData = result.clone();
+ m_liveAverageWf->std = stddev[0] * md->lambda / outputLambda;
+ m_liveAverageWf->mean = mean[0];
+ if (!m_viewDlg->m_tmpShowLive) {
+ m_ogl->m_surface->setSurface(m_liveAverageWf);
+ }
+
+ m_liveValidCount++;
+ } else {
+ m_viewDlg->statusRight->setText( "Error: Mismatched matrix dimensions during live accumulation!");
+ stopLiveButton_clicked(); // cancel loop
+ }
+ } // end RMS check
+ }
+
+ // If displaying current frame, update 3D view
+ if (!displayAverage || m_viewDlg->m_tmpShowLive) {
+ m_ogl->m_surface->setSurface(wf);
+ }
+
+ // 6. RENDER SURFACE
+ img = m_ogl->m_surface->render(1000, 1000);
+ m_ogl->m_liveLabel->setPixmap(QPixmap::fromImage(img));
+ // Update UI Status Bar
+ QString statusRightText;
+
+ if (computeAverage) {
+ QString liveMsg = "";
+ if (m_viewDlg->m_tmpShowLive) {
+ liveMsg = " RMS too big ";
+ }
+ statusRightText = QString("Averaged: %1 | Avg RMS: %2 %3")
+ .arg(m_liveValidCount - 1)
+ .arg(m_liveAverageWf->std, 0, 'f', 3)
+ .arg(liveMsg);
+ } else {
+ qDebug() << "rms" << wf->std;
+ statusRightText = QString("Frame: %1 | RMS: %2")
+ .arg(totalFrames)
+ .arg(wf->std, 0, 'f', 3);
+ }
+ double value = 1.;
+
+ int ndx = m_viewDlg->zernListWidget->currentRow() -1;
+
+ if (ndx == -1){ // do best fit conic
+ value = wf->InputZerns[8]/m_mirrorDlg->z8;
+ }
+ else value = wf->InputZerns[ndx];
+ if (ndx == 8){ // if primary SA then remove null if needed
+ if (m_mirrorDlg->doNull){
+ value -= z8Null;
+ qDebug() << "sa" << value;
+ }
+ }
+ m_viewDlg->history->addSample(m_liveAverageWf->std,value);
+ m_viewDlg->statusRight->setText(statusRightText);
+
+
+ // Wavefront list cleanup & memory management
+ bool discardFrame = (wf->std > m_viewDlg->maxRMS->value()) || m_viewDlg->deleteIntermidiateWaveFront->isChecked();
+ if (discardFrame) {
+ m_surfaceManager->m_wavefronts.removeLast();
+ m_surfTools->deleteLast();
+ m_surfaceManager->m_wf = nullptr;
+ }
+
+ // Free our local temporary wavefront
+ delete wf;
+
+ QThread::msleep(200);
+ totalFrames++;
}
+ // -------------------------------------------------------------------------
+ // LOOP EXIT: SAVE FINAL AVERAGE IF APPLICABLE
+ // -------------------------------------------------------------------------
+ if (computeAverage && m_liveAverageWf && m_liveValidCount > 1) {
+ wavefront *savedAvg = new wavefront(*m_liveAverageWf);
+ m_liveDataSum /= m_liveValidCount;
+ savedAvg->data = m_liveDataSum.clone();
+ savedAvg->m_origin = WavefrontOrigin::Average;
+ savedAvg->wasSmoothed = false;
+ savedAvg->dirtyZerns = true;
+
+ savedAvg->regions.clear();
+ m_surfaceManager->makeMask(savedAvg);
+ m_surfaceManager->generateSurfacefromWavefront(savedAvg);
+
+ savedAvg->name = QString("Average_%1").arg(m_liveValidCount - 1);
+
+ m_surfaceManager->m_wavefronts << savedAvg;
+ m_surfTools->addWaveFront(savedAvg->name);
+ m_viewDlg->saveAverage = false;
+ m_surfaceManager->m_currentNdx = m_surfaceManager->m_wavefronts.size()-1;
+
+ m_surfaceManager->m_surfaceTools->select(m_surfaceManager->m_currentNdx);
+ }
+
+ // Reset UI & Loop State Flags
+ m_liveLoopActive = false;
+ m_viewDlg->m_stopRequested = false;
+ m_viewDlg->loopRunning = false;
+ m_liveState = State_Stopped;
+
+}
+
+
+
+void MainWindow::on_actionReset_to_Factory_Defaults_triggered()
+{
+ // 1. Show the confirmation warning box
+ QMessageBox::StandardButton reply;
+ reply = QMessageBox::warning(this,
+ "Reset Settings",
+ "Do you really want to reset all settings to factory defaults?\nThis cannot be undone.",
+ QMessageBox::Yes | QMessageBox::No);
+
+ // 2. Proceed only if the user clicks "Yes"
+ if (reply == QMessageBox::Yes) {
+ QSettings settings;
+ QString filePath = settings.fileName();
+
+ // Clear all keys
+ settings.clear();
+ settings.sync();
+
+ // Optional: Delete the underlying file for a complete hard reset
+ if (QFile::exists(filePath)) {
+ QFile::remove(filePath);
+ }
+
+ qDebug() << "Settings have been reset to factory defaults.";
+
+ // Optional: Notify the user or prompt them to restart the app
+ QMessageBox::information(this, "Reset Complete", "Settings have been reset. Please restart the application.");
+ }
+}
+
+// Helper to copy keys between any two QSettings instances
+void copySettingsData(QSettings &src, QSettings &dest) {
+ QStringListIterator it(src.allKeys());
+ while (it.hasNext()) {
+ QString key = it.next();
+ dest.setValue(key, src.value(key));
+ }
}
+
+
+void MainWindow::on_actionSave_settomgs_to_a_file_triggered()
+{
+ QString filePath = QFileDialog::getSaveFileName(this,
+ "Export Settings",
+ "my_app_settings.ini",
+ "Settings Files (*.ini)");
+ if (filePath.isEmpty()) {
+ return; // User cancelled
+ }
+
+ QSettings liveSettings;
+ liveSettings.sync();
+
+ // Open an INI file target
+ QSettings exportFile(filePath, QSettings::IniFormat);
+ exportFile.clear(); // Clear old content in the target file if it exists
+
+ copySettingsData(liveSettings, exportFile);
+ exportFile.sync();
+
+ QMessageBox::information(this, "Export Successful", "Your settings have been successfully exported.");
+
+}
+
+
+void MainWindow::on_actionRestore_settings_from_a_file_triggered()
+{
+ QString filePath = QFileDialog::getOpenFileName(this,
+ "Import Settings",
+ "",
+ "Settings Files (*.ini)");
+ if (filePath.isEmpty()) {
+ return; // User cancelled
+ }
+
+ QMessageBox::StandardButton reply = QMessageBox::warning(this,
+ "Import Settings",
+ "Importing settings will overwrite your current configuration. Continue?",
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (reply != QMessageBox::Yes) {
+ return;
+ }
+
+ QSettings importFile(filePath, QSettings::IniFormat);
+ QSettings liveSettings;
+
+ // Clear current registry settings and replace with imported ones
+ liveSettings.clear();
+ copySettingsData(importFile, liveSettings);
+ liveSettings.sync();
+
+ QMessageBox::information(this, "Import Complete", "Settings have been restored. Please restart the application for all changes to take effect.");
+}
+
diff --git a/mainwindow.h b/mainwindow.h
index 7aa53a7d..32628247 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -47,6 +47,15 @@
#include "wavefrontfilterdlg.h"
#include "outlineplots.h"
#include "cameracalibwizard.h"
+#include "liveviewdialog.h"
+#include
+enum LiveLoopState {
+ State_Stopped,
+ State_Running,
+ State_Paused
+};
+
+
class regionEditTools;
class jitterOutlineDlg;
@@ -57,11 +66,12 @@ class MainWindow;
class MainWindow : public QMainWindow
{
Q_OBJECT
-
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
- void closeEvent(QCloseEvent *event);
+ void closeEvent(QCloseEvent *event) override;
void updateMetrics(wavefront& wf);
int addTab(QWidget* w, const QString &name);
mirrorDlg *m_mirrorDlg;
@@ -124,6 +134,7 @@ private slots:
void on_actionPreferences_triggered();
void mainTabChanged(int);
+ QString getCurrentHotKeyHelp() const ;
void on_actionMirror_triggered();
void on_saveOutline_clicked();
@@ -275,7 +286,15 @@ private slots:
void on_actionStop_auto_invert_triggered();
- void load_from_url();
+ QString load_from_url();
+
+ void on_actionLive_view_triggered();
+
+ void on_actionReset_to_Factory_Defaults_triggered();
+
+ void on_actionSave_settomgs_to_a_file_triggered();
+
+ void on_actionRestore_settings_from_a_file_triggered();
private:
@@ -287,8 +306,10 @@ private slots:
void updateRecentFileActions();
QString strippedName(const QString &fullFileName);
QMenu* m_view_menu;
+ QTimer *m_liveViewRMSTimer = NULL;
void createActions();
void createDockWindows();
+ void runLiveAnalysisLoop();
public:
IgramArea *m_igramArea;
DFTTools *m_dftTools;
@@ -330,14 +351,17 @@ private slots:
bool m_skipItem;
bool m_OutlineDoneInBatch;
bool m_batchMakeSurfaceReady;
+ bool m_liveLoopActive = false;
astigStatsDlg *m_astigStatsDlg;
-
+ bool m_hasShownHelp = false;
+ QDialog *m_helpOverlay = nullptr;
enum { MaxRecentFiles = 5 };
QAction *recentFileActs[MaxRecentFiles];
ColorChannelDisplay *m_colorChannels;
igramIntensity *m_intensityPlot;
vortexDebug *m_vortexDebugTool;
+ QPointer m_viewDlg;
batchIgramWizard *batchWiz;
QStringList m_igramsToProcess;
QWidget *oglFv;
@@ -347,6 +371,20 @@ private slots:
QVector > batchZerns;
QPointer m_cameraCalibWizard;
+
+ // Live view variables
+ // Average tracking variables (persists across pause/resume, resets on fresh start)
+ wavefront *m_liveAverageWf = nullptr;
+ cv::Mat m_liveSum; //Sum used to make the live display
+ cv::Mat m_liveDataSum; // The sum used to make the actual wavefront.
+ int m_liveValidCount = 0;
+ LiveLoopState m_liveState = State_Stopped;
+ void setLiveViewMode(bool active);
+public slots:
+ void startLiveButton_clicked();
+ void pauseLiveButton_clicked();
+ void stopLiveButton_clicked();
+
};
#endif // MAINWINDOW_H
diff --git a/mainwindow.ui b/mainwindow.ui
index 9caf7c09..c9902a48 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -59,7 +59,7 @@
0
0
667
- 22
+ 24
diff --git a/oglview.cpp b/oglview.cpp
index b929f071..17d70618 100644
--- a/oglview.cpp
+++ b/oglview.cpp
@@ -66,10 +66,16 @@ OGLView::OGLView(QWidget *parent, ContourTools *m_tool) :
QLabel *colorLedgend = new QLabel(this);
+ m_liveLabel = new QLabel(this);
+ m_liveLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ m_liveLabel->setScaledContents(true);
+ m_liveLabel->hide(); // Hidden by default
+
m_surface->setLegend(colorLedgend);
+
+
QGroupBox *heightMapGroupBox = new QGroupBox(QStringLiteral("Waves 550nm"));
- //heightMapGroupBox->setStyleSheet({"background: grey;"});
QVBoxLayout *colorMapVBox = new QVBoxLayout;
colorMapVBox->addWidget(colorLedgend);
heightMapGroupBox->setLayout(colorMapVBox);
@@ -91,6 +97,7 @@ OGLView::OGLView(QWidget *parent, ContourTools *m_tool) :
connect(m_fullScreenPb, &QAbstractButton::pressed, this, &OGLView::fullScreenPressed);
lh->addStretch();
lv->addWidget(m_container);
+ lv->addWidget(m_liveLabel); // will show when live view is looping analysis
topH->addLayout(lv,10);
topH->addLayout(rightcontrols,1);
setLayout(topH);
@@ -232,3 +239,13 @@ void OGLView::showSelected() // show all selected wavefronts as 3D plots
w->show();
QApplication::restoreOverrideCursor();
}
+
+void OGLView::setLivePreviewMode(bool liveActive) {
+ if (liveActive) {
+ m_container->hide();
+ m_liveLabel->show();
+ } else {
+ m_liveLabel->hide();
+ m_container->show();
+ }
+}
diff --git a/oglview.h b/oglview.h
index 3d5dad65..9d442736 100644
--- a/oglview.h
+++ b/oglview.h
@@ -45,9 +45,12 @@ class OGLView : public QWidget
QPushButton *backgroundPb;
QColor m_background;
QWidget *m_container;
+ QLabel *m_liveLabel;
QSize sizeHint() const;
int m_spinRate;
+
+ void setLivePreviewMode(bool liveActive);
signals:
void fullScreen();
public slots:
diff --git a/profileplot.cpp b/profileplot.cpp
index 6b97bbd1..41ba28a4 100644
--- a/profileplot.cpp
+++ b/profileplot.cpp
@@ -656,12 +656,12 @@ void ProfilePlot::populate()
if (m_wf == 0)
return;
QSettings settings;
- double smoothing = settings.value("GBValue", 20).toInt();
m_plot->detachItems(QwtPlotItem::Rtti_PlotTextLabel);
+ SurfaceManager *sm = SurfaceManager::get_instance();
+ double smoothing = sm->getGaussBlurOfSelectedWavefronts();
-
- if (m_wf->m_outside.m_radius > 0 && settings.value("GBlur", false).toBool()){
+ if (smoothing > 0){
double val = .01 * (m_wf->diameter) * smoothing;
QString t = QString("Surface Smoothing diameter %1% of surface diameter %2 mm")
.arg(smoothing, 6, 'f', 2)
@@ -794,7 +794,6 @@ void ProfilePlot::populate()
g_angle = startAngle;
}
if (m_showAvg){
- qDebug() << "inside avg" << y_offset;
QColor penColor = QColor(Settings2::m_profile->getColor(i%10));
// plot the average profile
std::vector avgRadius = compute_average_radial_profile(wf->workData,
@@ -868,7 +867,7 @@ void ProfilePlot::populate()
muY->setLabel( QString::fromLatin1( " 1/8 wave on wavefront" ) );
muY->setLabelAlignment( Qt::AlignRight | Qt::AlignTop );
muY->setLineStyle( QwtPlotMarker::HLine );
- muY->setLinePen( Qt::red, 0, Qt::DashDotLine );
+ muY->setLinePen( Qt::red, 3, Qt::DashDotLine );
muY->setYValue( 0.125 * mul );
muY->attach( m_plot );
@@ -877,7 +876,7 @@ void ProfilePlot::populate()
mlY->setLabel( QString::fromLatin1( " -1/8 wave on wavefront" ) );
mlY->setLabelAlignment( Qt::AlignRight | Qt::AlignTop );
mlY->setLineStyle( QwtPlotMarker::HLine );
- mlY->setLinePen( Qt::red, 0, Qt::DashDotLine );
+ mlY->setLinePen( Qt::red, 3, Qt::DashDotLine );
mlY->setYValue( -0.125 * mul );
mlY->attach( m_plot );
@@ -982,7 +981,7 @@ void ProfilePlot::saveXscaleSettings(){
}
void ProfilePlot::toggleShow16(){
m_show_16_diameters = !m_show_16_diameters;
-qDebug() << "show 16";
+
populate();
m_plot->replot();
}
@@ -994,7 +993,7 @@ void ProfilePlot::toggleShowAvg(){
}
void ProfilePlot::toggleOneAngle(){
m_show_oneAngle = !m_show_oneAngle;
-qDebug() << "show one";
+
populate();
m_plot->replot();
}
@@ -1179,7 +1178,7 @@ void ProfilePlot::CreateWaveFrontFromAverage(){
}
cv::Mat result = createInterpolatedCircularSurface(avgRadius);
SurfaceManager *sm = SurfaceManager::get_instance();
- sm->createSurfaceFromPhaseMap(result,
+ sm->createWaveFrontFromPhaseMap(result,
m_wf->m_outside,
m_wf->m_inside,
QString(m_wf->name.replace(".wft","") + "_avg"), WavefrontOrigin::Average);
diff --git a/surfaceanalysistools.cpp b/surfaceanalysistools.cpp
index f8c7aa08..c22a82c1 100644
--- a/surfaceanalysistools.cpp
+++ b/surfaceanalysistools.cpp
@@ -61,7 +61,6 @@ surfaceAnalysisTools::surfaceAnalysisTools(QWidget *parent) :
}
void surfaceAnalysisTools::enableControls(bool flag){
-
setEnabled(flag);
}
@@ -78,6 +77,11 @@ void surfaceAnalysisTools::setGaussianControls(bool enabled, double value){
ui->surfaceSmoothGausianBlurr->setEnabled(enabled);
}
+void surfaceAnalysisTools::setGaussianStateText(const QString & str){
+ ui->lblGaussState->setText(str);
+}
+
+
void surfaceAnalysisTools::addWaveFront(const QString &name){
QStringList list = name.split('/');
@@ -367,4 +371,15 @@ void surfaceAnalysisTools::closeDefocus(int /*result*/){
}
+void surfaceAnalysisTools::deleteLast(){
+ if (ui->wavefrontList->count() > 0) {
+ // Calculate the index of the last item
+ int lastRow = ui->wavefrontList->count() - 1;
+ // Remove the item from the widget (returns a pointer to the item)
+ QListWidgetItem *item = ui->wavefrontList->takeItem(lastRow);
+
+ // Explicitly delete the item to prevent memory leaks
+ delete item;
+ }
+}
diff --git a/surfaceanalysistools.h b/surfaceanalysistools.h
index 9abc25c3..69ad9db3 100644
--- a/surfaceanalysistools.h
+++ b/surfaceanalysistools.h
@@ -49,8 +49,10 @@ class surfaceAnalysisTools : public QDockWidget
double m_defocusInmm;
void setBlurText(const QString &txt);
void setGaussianControls(bool enabled, double value);
+ void setGaussianStateText(const QString & str);
void nameChangedN(int, const QString&);
void select(int item);
+ void deleteLast();
signals:
void doxform(QList);
diff --git a/surfaceanalysistools.ui b/surfaceanalysistools.ui
index 4e5de216..39f2d6cb 100644
--- a/surfaceanalysistools.ui
+++ b/surfaceanalysistools.ui
@@ -19,7 +19,7 @@
250
- 497
+ 501
@@ -265,6 +265,17 @@
Surface Filter
+ -
+
+
-
+
+
+ Currently 20.00%
+
+
+
+
+
-
-
@@ -313,7 +324,7 @@
-
-
+ --
diff --git a/surfacegraph.cpp b/surfacegraph.cpp
index 076e4819..4edf513d 100644
--- a/surfacegraph.cpp
+++ b/surfacegraph.cpp
@@ -333,7 +333,7 @@ void SurfaceGraph::yOffsetChanged(int val){
void SurfaceGraph::scaleY(int d){
m_yGridHeight = maxHeightSelections[d];
- qDebug() << "yscale" << m_yGridHeight << m_gradientScale << d;
+
m_graph->axisY()->setRange(0.0f, m_yGridHeight);
setSurface(m_wf);
}
@@ -386,7 +386,7 @@ void SurfaceGraph::adjustXMin(int min)
// m_axisMaxSliderX->setValue(max);
// }
-qDebug() << "range" << minX << maxX;
+
m_graph->axisX()->setRange(minX, maxX);
}
@@ -483,11 +483,6 @@ QSize SurfaceGraph::Size(){
return m_graph->size();
}
QImage SurfaceGraph::render(int width, int height){
- QSize orgsize = m_graph->size();
- if (orgsize.width() < width){
- m_graph->resize(width, height);
- }
- QImage result = m_graph->renderToImage(0, QSize(width,height));
- m_graph->resize(orgsize);
- return result;
+ // Render directly at the widget's current display size without resizing the live view
+ return m_graph->renderToImage(0, m_graph->size());
}
diff --git a/surfacemanager.cpp b/surfacemanager.cpp
index bad80138..6caf6c87 100644
--- a/surfacemanager.cpp
+++ b/surfacemanager.cpp
@@ -344,7 +344,6 @@ void SurfaceManager::generateSurfacefromWavefront(int wavefrontNdx) {
wavefront *wf = m_wavefronts[wavefrontNdx];
generateSurfacefromWavefront(wf);
-
surfaceGenFinished();
}
@@ -355,8 +354,10 @@ void SurfaceManager::generateSurfacefromWavefront(wavefront * wf){
} else if (wf->zernEnablesApplied.size() != zernEnables.size()) {
wf->zernEnablesApplied.resize(zernEnables.size(), true);
}
- m_GB_enabled = wf->gbEnabled;
- m_gbValue = wf->gbValue;
+
+ wf->gbEnabled = m_GB_enabled;
+ wf->gbValue = m_gbValue;
+
if (wf->dirtyZerns){
if (mirrorDlg::get_Instance()->isEllipse()){
wf->nulledData = wf->data.clone();
@@ -381,7 +382,6 @@ void SurfaceManager::generateSurfacefromWavefront(wavefront * wf){
return;
}
-
//compute zernike values
mirrorDlg *md = mirrorDlg::get_Instance();
@@ -438,10 +438,7 @@ void SurfaceManager::generateSurfacefromWavefront(wavefront * wf){
zp.fillVoid(*wf);
makeMask(wf, true);
// null out desired terms.
- //cv::Mat tiltremoved = zp.null_unwrapped(*wf, wf->InputZerns, zernEnables, 0,3);
- //wf->data = tiltremoved;
- //zp.unwrap_to_zernikes(*wf);
- wf->nulledData = zp.null_unwrapped(*wf, wf->InputZerns, wf->zernEnablesApplied,0,Z_TERMS );
+ wf->nulledData = zp.null_unwrapped(*wf, wf->InputZerns, zernEnables,0,Z_TERMS );
wf->dirtyZerns = false;
}
@@ -549,7 +546,6 @@ SurfaceManager::SurfaceManager(QObject *parent, surfaceAnalysisTools *tools,
m_inverseMode(invNOTSET),m_ignoreInverse(false), m_standAstigWizard(nullptr), workToDo(0), m_applyFloatingZernEnables(false), m_wftStats(0)
{
- okToUpdateSurfacesOnGenerateComplete = true;
m_simView = SimulationsView::getInstance(0);
pd = new QProgressDialog();
@@ -745,6 +741,7 @@ void SurfaceManager::wftNameChanged(int ndx, const QString &name){
}
void SurfaceManager::sendSurface(wavefront* wf){
+//qDebug() << "sending surface" << wf->name;
emit currentNdxChanged(m_currentNdx);
computeMetrics(wf);
@@ -753,6 +750,7 @@ void SurfaceManager::sendSurface(wavefront* wf){
m_SurfaceGraph->setSurface(wf);
m_simView->setSurface(wf);
foucaultView::get_Instance()->setSurface(wf);
+ updateBlurText();
QFile fn(wf->name);
QFileInfo fileInfo(fn.fileName());
@@ -823,7 +821,7 @@ void SurfaceManager::useDemoWaveFront(){
- createSurfaceFromPhaseMap(result,
+ createWaveFrontFromPhaseMap(result,
CircleOutline(QPointF(xcen,ycen),rad),
CircleOutline(QPointF(0,0),0),
QString("Demo"), WavefrontOrigin::Demo);
@@ -833,7 +831,6 @@ void SurfaceManager::waveFrontClickedSlot(int ndx)
{
m_currentNdx = ndx;
- syncGaussianStateForWavefront(m_wavefronts[ndx]);
QString msg = QString(" %1x%2 ").arg(m_wavefronts[ndx]->data.cols).arg(m_wavefronts[ndx]->data.rows);
((MainWindow*)parent())->statusBar()->showMessage(msg);
sendSurface(m_wavefronts[ndx]);
@@ -853,13 +850,48 @@ void SurfaceManager::wavefrontDClicked(const QString & name){
for (int i = 0; i < m_wavefronts.size(); ++i){
if (m_wavefronts[i]->name.endsWith(name)){ //TODO JST 2023/09/11 this does not work on some name combinations. To be fixed
m_currentNdx = i;
- syncGaussianStateForWavefront(m_wavefronts[i]);
sendSurface(m_wavefronts[i]);
break;
}
}
}
+double SurfaceManager::getGaussBlurOfSelectedWavefronts(){
+ // returns:
+ // blur value if all selected wavefronts are the same blur value
+ // 0 if all wavefronts have gauss blur disabled
+ // -1 different wavefronts have different amounts of blur
+ // -2 if nothing selected
+ QList doThese = m_surfaceTools->SelectedWaveFronts();
+ double blur = -2;
+ foreach(int ndx , doThese){
+ wavefront * wf = m_wavefronts[ndx];
+ double val = (wf->gbEnabled ? wf->gbValue : 0);
+ if (blur < 0) {
+ blur = val;
+ continue;
+ }
+ if (blur != val)
+ return -1;
+ }
+ return blur;
+}
+
+void SurfaceManager::updateBlurText(){
+ if (m_surfaceTools == nullptr)
+ return;
+
+ double blur = getGaussBlurOfSelectedWavefronts();
+ if (blur == -2) // nothing selected
+ m_surfaceTools->setGaussianStateText("");
+ else if (blur == -1) // mixed
+ m_surfaceTools->setGaussianStateText("Multiple Blur Amounts");
+ else if (blur == 0) // Gaussian blur disabled
+ m_surfaceTools->setGaussianStateText("Currently No Blurring");
+ else
+ m_surfaceTools->setGaussianStateText("Current Gaussian Blur "+QString("%1").arg(blur, 5, 'f', 1)+" %");
+}
+
void SurfaceManager::surfaceSmoothGBValue(double value){
QSettings settings;
@@ -903,19 +935,6 @@ void SurfaceManager::surfaceSmoothGBEnabled(bool b){
m_waveFrontTimer->start(500);
}
-void SurfaceManager::syncGaussianStateForWavefront(wavefront *wf){
- if (wf == nullptr) {
- return;
- }
-
- m_GB_enabled = wf->gbEnabled;
- m_gbValue = wf->gbValue;
- m_surfaceTools->setGaussianControls(wf->gbEnabled, wf->gbValue);
-
- mirrorDlg *md = mirrorDlg::get_Instance();
- m_surfaceTools->setBlurText(QString("%1 mm").arg(.01 * wf->gbValue * md->diameter, 6, 'f', 2));
-}
-
void SurfaceManager::computeMetrics(wavefront *wf){
mirrorDlg *md = mirrorDlg::get_Instance();
cv::Scalar mean,std;
@@ -1117,7 +1136,7 @@ void SurfaceManager::SaveWavefronts(bool saveNulled){
}
QApplication::restoreOverrideCursor();
}
-void SurfaceManager::createSurfaceFromPhaseMap(cv::Mat phase, CircleOutline outside,
+void SurfaceManager::createWaveFrontFromPhaseMap(cv::Mat phase, CircleOutline outside,
CircleOutline center,
const QString &name, WavefrontOrigin origin,
QVector > polyArea){
@@ -1176,9 +1195,10 @@ void SurfaceManager::createSurfaceFromPhaseMap(cv::Mat phase, CircleOutline outs
m_currentNdx = m_wavefronts.size()-1;
makeMask(m_currentNdx);
-
+ //qDebug() << "surface manager create wave front from phasee map calling generate surface from wave front";
generateSurfacefromWavefront(m_currentNdx);
- loadComplete();
+
+ //loadComplete();
m_surfaceTools->select(m_currentNdx);
emit showTab(2);
}
@@ -1549,7 +1569,7 @@ void SurfaceManager::deleteCurrent(){
if (m_currentNdx > 0)
--m_currentNdx;
}
- showmem("After delete");
+
if (m_wavefronts.length() > 0) {
sendSurface(m_wavefronts[m_currentNdx]);
@@ -1589,7 +1609,6 @@ void SurfaceManager::next(){
++m_currentNdx;
else
m_currentNdx = 0;
- syncGaussianStateForWavefront(m_wavefronts[m_currentNdx]);
sendSurface(m_wavefronts[m_currentNdx]);
@@ -1604,7 +1623,6 @@ void SurfaceManager::previous(){
else
m_currentNdx = m_wavefronts.length()-1;
- syncGaussianStateForWavefront(m_wavefronts[m_currentNdx]);
sendSurface(m_wavefronts[m_currentNdx]);
}
QVector histo(const std::vector &data, int bins, double min, double max){
@@ -1646,22 +1664,22 @@ void SurfaceManager::surfaceGenFinished() {
emit progress(++workProgress);
m_surface_finished = true;
- if (okToUpdateSurfacesOnGenerateComplete){
- loadComplete();
- }
+ loadComplete();
+
if (workProgress == workToDo)
workToDo = 0;
}
void SurfaceManager::loadComplete(){
+
m_toolsEnableTimer->start(1000);
if (m_wavefronts.size() > 0){
sendSurface(m_wavefronts[m_currentNdx]);
computeMetrics(m_wavefronts[m_currentNdx]);
}
}
-
+int cnt = 0;
// Update all surfaces since some control has changed. Skip current surface it has already been done
void SurfaceManager::backGroundUpdate(){
@@ -1676,6 +1694,7 @@ void SurfaceManager::backGroundUpdate(){
const bool applyFloatingZerns = m_applyFloatingZernEnables;
m_applyFloatingZernEnables = false;
foreach (int i, doThese){
+qDebug() << "updating these" << doThese << i << ++cnt;
if (applyFloatingZerns) {
m_wavefronts[i]->zernEnablesApplied = zernEnables;
}
@@ -1691,8 +1710,8 @@ void SurfaceManager::backGroundUpdate(){
break;
}
}
+qDebug() << "done with these";
m_ignoreInverse = false;
- loadComplete();
}
diff --git a/surfacemanager.h b/surfacemanager.h
index bf719734..b57627e0 100644
--- a/surfacemanager.h
+++ b/surfacemanager.h
@@ -77,7 +77,9 @@ class SurfaceManager : public QObject
void initWaveFrontLoad();
void averageWavefrontFiles(const QStringList &files);
void downSizeWf(wavefront *wf);
- void syncGaussianStateForWavefront(wavefront *wf);
+ void updateBlurText();
+ double getGaussBlurOfSelectedWavefronts();
+
void process(int wavefront_index, SurfaceManager *sm);
wavefront *readWaveFront(const QString &fileName);
inline wavefront *getCurrent(){
@@ -118,7 +120,6 @@ class SurfaceManager : public QObject
configRESPONSE rocResp;
configRESPONSE lambdResp;
int okToContinue;
- bool okToUpdateSurfacesOnGenerateComplete;
void makeMask(wavefront* wf, bool useInsideCircle = true);
void generateSurfacefromWavefront(int ndx);
void generateSurfacefromWavefront(wavefront *wf);
@@ -177,7 +178,7 @@ private slots:
public slots:
void rotateThese(double angle, QList list);
- void createSurfaceFromPhaseMap(cv::Mat phase, CircleOutline outside,
+ void createWaveFrontFromPhaseMap(cv::Mat phase, CircleOutline outside,
CircleOutline center, const QString &name, WavefrontOrigin origin,
QVector > polyArea= QVector >());
void invert(QList list);
diff --git a/usercolormapdlg.cpp b/usercolormapdlg.cpp
index ed627550..ae6b20d9 100644
--- a/usercolormapdlg.cpp
+++ b/usercolormapdlg.cpp
@@ -31,6 +31,7 @@
#include
#include "colormapviewerdlg.h"
#include "plotcolor.h"
+#include
/*
m_listeWidget->setViewMode(QListWidget::IconMode);
diff --git a/videostreamworker.cpp b/videostreamworker.cpp
new file mode 100644
index 00000000..dbba8ffb
--- /dev/null
+++ b/videostreamworker.cpp
@@ -0,0 +1,578 @@
+#include "videostreamworker.h"
+#include
+#include
+#include
+#include "spdlog/spdlog.h"
+
+VideoStreamWorker::VideoStreamWorker(const QString &source, QObject *parent)
+ : QObject(parent), m_source(source) {}
+
+VideoStreamWorker::~VideoStreamWorker() {
+ stop();
+}
+
+void VideoStreamWorker::startStream() {
+ QMutexLocker locker(&m_controlMutex);
+
+ if (m_running) return;
+
+ bool isInt = false;
+ int camId = m_source.toInt(&isInt);
+ qputenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", "stimeout;2000000;");
+
+ if (m_cap.isOpened()) {
+ m_cap.release();
+ }
+
+ if (isInt) {
+#if defined(_WIN32) || defined(_WIN64)
+ m_cap.open(camId, cv::CAP_DSHOW);
+#else
+ m_cap.open(camId);
+#endif
+ } else {
+ m_cap.open(m_source.toStdString());
+ }
+
+ if (!m_cap.isOpened()) {
+ emit streamError("Failed to open stream source: " + m_source);
+ m_running = false;
+ return;
+ }
+
+ spdlog::get("logger")->trace("Stream started successfully");
+
+ m_running = true;
+
+ // Launch the background capture loop in a dedicated std::thread
+ m_captureThread = std::unique_ptr(new std::thread(&VideoStreamWorker::captureLoop, this));
+
+ emit streamStarted();
+}
+
+
+
+void VideoStreamWorker::captureLoop() {
+ int errorCount = 0;
+ QSettings set;
+ int maxErrorLimit = set.value("liveViewConnectFrames",15).toInt(); // Number of failed attempts before giving up
+
+ while (m_running) {
+ cv::Mat frame;
+ bool success = false;
+
+ {
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened()) {
+
+ success = m_cap.read(frame) && !frame.empty();
+
+ }
+ }
+
+ if (success) {
+ // Successful read: reset error counter and update live frame
+ errorCount = 0;
+ QMutexLocker locker(&m_frameMutex);
+ m_latestFrame = frame.clone();
+
+ cv::Scalar meanValue = cv::mean(frame);
+ double averagePixel = meanValue[0];
+ //qDebug() << "avg: " << averagePixel;
+ if (averagePixel > 0)
+ continue;
+
+ // error! - we need to reconnect
+ spdlog::get("logger")->error("resetting connection because frame is blank");
+ {
+ QMutexLocker locker2(&m_controlMutex);
+ m_cap.release();
+ bool isInt = false;
+ int camId = m_source.toInt(&isInt);
+ m_source.toInt(&isInt);
+ if (isInt) {
+ #if defined(_WIN32) || defined(_WIN64)
+ m_cap.open(camId, cv::CAP_DSHOW);
+ #else
+ m_cap.open(camId);
+ #endif
+ } else {
+ m_cap.open(m_source.toStdString());
+ }
+ }
+ if (m_cap.isOpened()) {
+ setResolution(savedResolutionWidth, savedResolutionHeight);
+ }
+
+ } else {
+ errorCount++;
+
+ spdlog::get("logger")->trace("errorCount {}", errorCount);
+ // Create a status/error image canvas (e.g., 800x600 dark background)
+ cv::Mat errorFrame(600, 800, CV_8UC3, cv::Scalar(30, 30, 30));
+
+ std::string msg = "Connecting / Waiting for Stream...";
+ std::string countMsg = "Attempt: " + std::to_string(errorCount) + " of " + std::to_string(maxErrorLimit);
+
+ int baseline = 0;
+ double fontScale = 0.8;
+ int thickness = 2;
+
+ // Draw primary warning message (Centered, Orange/Yellow)
+ cv::Size textSize = cv::getTextSize(msg, cv::FONT_HERSHEY_SIMPLEX, fontScale, thickness, &baseline);
+ cv::Point textOrg((errorFrame.cols - textSize.width) / 2, (errorFrame.rows / 2) - 20);
+ cv::putText(errorFrame, msg, textOrg, cv::FONT_HERSHEY_SIMPLEX, fontScale, cv::Scalar(0, 165, 255), thickness);
+
+ // Draw error count below it (Centered, White)
+ double countScale = 0.6;
+ cv::Size countSize = cv::getTextSize(countMsg, cv::FONT_HERSHEY_SIMPLEX, countScale, 1, &baseline);
+ cv::Point countOrg((errorFrame.cols - countSize.width) / 2, (errorFrame.rows / 2) + 25);
+ cv::putText(errorFrame, countMsg, countOrg, cv::FONT_HERSHEY_SIMPLEX, countScale, cv::Scalar(255, 255, 255), 1);
+
+ // Publish the status frame so the client UI can show it
+ {
+ QMutexLocker locker(&m_frameMutex);
+ m_latestFrame = errorFrame;
+ }
+
+ // If we've exceeded the limit, give up and close the stream
+ if (errorCount >= maxErrorLimit) {
+ if (m_running) {
+ emit streamError("Stream connection failed after multiple attempts.");
+ m_running = false;
+ }
+ break;
+ }
+
+ // Sleep briefly between retry attempts so it doesn't spin at 100% CPU
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ }
+}
+
+void VideoStreamWorker::fetchNextFrame() {
+ if (!m_running) return;
+ static cv::Mat testpattern;
+ if (false) { // use a file for the test patttern.
+ if (testpattern.empty()){
+ // 1. Open a file dialog to select an image
+ QString filePath = QFileDialog::getOpenFileName(
+ nullptr,
+ "Open Image",
+ "",
+ "Image Files (*.png *.jpg *.jpeg *.bmp)"
+ );
+
+ if (filePath.isEmpty()) {
+ return; // User canceled
+ }
+ testpattern = cv::imread(filePath.toStdString(), cv::IMREAD_COLOR);
+ }
+ emit frameReady(testpattern.clone());
+ return;
+ }
+ if (false){// for noraml op make this false. This creates a calibration target for debug. THe first side lobe will be at bin 32.
+
+
+ if (testpattern.empty()){ // that will be 32 cycles per mirror diameter.
+ int innerWidth = 800;
+ int stripeWidth = 16;
+ int borderWidth = 10; // Extra room for the outline/crop boundary
+ int totalSize = innerWidth + (borderWidth * 2);
+
+ // Create a larger canvas filled with black (the border background)
+ cv::Mat img(totalSize, totalSize, CV_8UC1, cv::Scalar(0));
+
+ // Define the region for the 1024x1024 striped circle, offset by the border
+ cv::Rect roiRect(borderWidth, borderWidth, innerWidth, innerWidth);
+ cv::Mat innerRegion = img(roiRect);
+
+ // Draw the 16-pixel stripes inside the inner region
+ for (int x = 0; x < innerWidth; x += stripeWidth) {
+ int currentWidth = std::min(stripeWidth, innerWidth - x);
+ if ((x / stripeWidth) % 2 == 1) {
+ cv::Mat stripeRoi = innerRegion(cv::Rect(x, 0, currentWidth, innerWidth));
+ stripeRoi.setTo(cv::Scalar(255));
+ }
+ }
+
+ // Apply a circular mask to the inner region so it matches the expected mirror outline
+ cv::Mat circularMask(innerWidth, innerWidth, CV_8UC1, cv::Scalar(0));
+ cv::Point center(innerWidth / 2.0, innerWidth / 2.0);
+ double radius = innerWidth / 2.0;
+ cv::circle(circularMask, center, radius, cv::Scalar(255), -1);
+
+ cv::Mat maskedInner;
+ innerRegion.copyTo(maskedInner, circularMask);
+ maskedInner.copyTo(innerRegion); // Copy back into the bordered canvas
+ testpattern = img.clone();
+ }
+ emit frameReady(testpattern.clone());
+ return;
+ }
+ cv::Mat frameToSend;
+ {
+ QMutexLocker locker(&m_frameMutex);
+ if (m_latestFrame.empty()) return;
+ frameToSend = m_latestFrame.clone();
+ }
+
+ emit frameReady(frameToSend);
+}
+
+
+void VideoStreamWorker::stop() {
+ m_running = false;
+
+ // Wait for the background capture thread to finish cleanly
+ if (m_captureThread && m_captureThread->joinable()) {
+ m_captureThread->join();
+ }
+
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened()) {
+ m_cap.release();
+ }
+}
+
+void VideoStreamWorker::changeSource(QString newSource) {
+ // Stop current stream and join thread safely
+ stop();
+
+ {
+ QMutexLocker locker(&m_controlMutex);
+ spdlog::get("logger")->trace("Changing source to: {}", newSource.toStdString());
+
+ m_source = newSource;
+ }
+
+ // Restart with the new source
+ startStream();
+}
+
+void VideoStreamWorker::setResolution(int width, int height) {
+
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() && width > 0 && height > 0) {
+ bool isInt = false;
+ m_source.toInt(&isInt);
+
+ // Only apply resolution changes to local USB cameras, not URL streams
+ if (isInt) {
+ m_cap.set(cv::CAP_PROP_FRAME_WIDTH, width);
+ m_cap.set(cv::CAP_PROP_FRAME_HEIGHT, height);
+ spdlog::get("logger")->trace("VideoStreamWorker: Resolution requested to set -> {} x {}", width, height);
+ savedResolutionWidth = width;
+ savedResolutionHeight = height;
+ }
+ }
+}
+
+void VideoStreamWorker::requestCameraSetting(QString prop , double val) {
+ spdlog::get("logger")->trace("setting camera property");
+
+ QMutexLocker locker(&m_controlMutex);
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false)
+ return;
+
+ if (prop == "Exposure") {
+ setAutoExpSetting(false, false);
+ m_cap.set(cv::CAP_PROP_EXPOSURE, val);
+ }
+
+ if (prop == "Gain") {
+ setAutoExpSetting(false, false);
+ int prop_id = cv::CAP_PROP_GAIN;
+ if (m_gain_mode == false)
+ prop_id = cv::CAP_PROP_BRIGHTNESS;
+ m_cap.set(prop_id, val);
+ }
+}
+
+void VideoStreamWorker::dumpAll() {
+ // send all camera properties to debug stream
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened()) {
+ bool isInt = false;
+ m_source.toInt(&isInt);
+
+ // Only apply resolution changes to local USB cameras, not URL streams
+ if (isInt) {
+ struct PropertyMap {
+ cv::VideoCaptureProperties id;
+ const char* name;
+ };
+ const std::vector properties = {
+ { cv::CAP_PROP_POS_MSEC, "CAP_PROP_POS_MSEC" },
+ { cv::CAP_PROP_POS_FRAMES, "CAP_PROP_POS_FRAMES" },
+ { cv::CAP_PROP_POS_AVI_RATIO, "CAP_PROP_POS_AVI_RATIO" },
+ { cv::CAP_PROP_FRAME_WIDTH, "CAP_PROP_FRAME_WIDTH" },
+ { cv::CAP_PROP_FRAME_HEIGHT, "CAP_PROP_FRAME_HEIGHT" },
+ { cv::CAP_PROP_FPS, "CAP_PROP_FPS" },
+ { cv::CAP_PROP_FOURCC, "CAP_PROP_FOURCC" },
+ { cv::CAP_PROP_FRAME_COUNT, "CAP_PROP_FRAME_COUNT" },
+ { cv::CAP_PROP_FORMAT, "CAP_PROP_FORMAT" },
+ { cv::CAP_PROP_MODE, "CAP_PROP_MODE" },
+ { cv::CAP_PROP_BRIGHTNESS, "CAP_PROP_BRIGHTNESS" },
+ { cv::CAP_PROP_CONTRAST, "CAP_PROP_CONTRAST" },
+ { cv::CAP_PROP_SATURATION, "CAP_PROP_SATURATION" },
+ { cv::CAP_PROP_HUE, "CAP_PROP_HUE" },
+ { cv::CAP_PROP_GAIN, "CAP_PROP_GAIN" },
+ { cv::CAP_PROP_EXPOSURE, "CAP_PROP_EXPOSURE" },
+ { cv::CAP_PROP_CONVERT_RGB, "CAP_PROP_CONVERT_RGB" },
+ { cv::CAP_PROP_WHITE_BALANCE_BLUE_U, "CAP_PROP_WHITE_BALANCE_BLUE_U" },
+ { cv::CAP_PROP_RECTIFICATION, "CAP_PROP_RECTIFICATION" },
+ { cv::CAP_PROP_MONOCHROME, "CAP_PROP_MONOCHROME" },
+ { cv::CAP_PROP_SHARPNESS, "CAP_PROP_SHARPNESS" },
+ { cv::CAP_PROP_AUTO_EXPOSURE, "CAP_PROP_AUTO_EXPOSURE" },
+ { cv::CAP_PROP_GAMMA, "CAP_PROP_GAMMA" },
+ { cv::CAP_PROP_TEMPERATURE, "CAP_PROP_TEMPERATURE" },
+ { cv::CAP_PROP_TRIGGER, "CAP_PROP_TRIGGER" },
+ { cv::CAP_PROP_TRIGGER_DELAY, "CAP_PROP_TRIGGER_DELAY" },
+ { cv::CAP_PROP_WHITE_BALANCE_RED_V, "CAP_PROP_WHITE_BALANCE_RED_V" },
+ { cv::CAP_PROP_ZOOM, "CAP_PROP_ZOOM" },
+ { cv::CAP_PROP_FOCUS, "CAP_PROP_FOCUS" },
+ { cv::CAP_PROP_GUID, "CAP_PROP_GUID" },
+ { cv::CAP_PROP_ISO_SPEED, "CAP_PROP_ISO_SPEED" },
+ { cv::CAP_PROP_BACKLIGHT, "CAP_PROP_BACKLIGHT" },
+ { cv::CAP_PROP_PAN, "CAP_PROP_PAN" },
+ { cv::CAP_PROP_TILT, "CAP_PROP_TILT" },
+ { cv::CAP_PROP_ROLL, "CAP_PROP_ROLL" },
+ { cv::CAP_PROP_IRIS, "CAP_PROP_IRIS" },
+ { cv::CAP_PROP_SETTINGS, "CAP_PROP_SETTINGS" },
+ { cv::CAP_PROP_BUFFERSIZE, "CAP_PROP_BUFFERSIZE" },
+ { cv::CAP_PROP_AUTOFOCUS, "CAP_PROP_AUTOFOCUS" }
+ };
+
+ for (const auto& prop : properties) {
+ double value = m_cap.get(prop.id);
+
+ // OpenCV returns -1 or 0 for unsupported properties depending on the backend
+ if (value == -1) {
+ qDebug().noquote() << QString("%1: NOT SUPPORTED").arg(prop.name, -30);
+ } else {
+ qDebug().noquote() << QString("%1: %2").arg(prop.name, -30).arg(value);
+ }
+ }
+
+ }
+ }
+}
+
+bool VideoStreamWorker::getCameraConfiguration(CameraConfiguration &config) {
+ // meant to be called only from Ui thread - return false if config is invalid
+
+ spdlog::get("logger")->trace("getCameraConfiguration");
+
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() == false)
+ return false;
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false)
+ return false;
+ config.auto_exposure = m_cap.get(cv::CAP_PROP_AUTO_EXPOSURE);
+ config.exposure = m_cap.get(cv::CAP_PROP_EXPOSURE);
+ config.brightness = m_cap.get(cv::CAP_PROP_BRIGHTNESS);
+ config.gain = m_cap.get(cv::CAP_PROP_GAIN);
+ config.resolution.setHeight(m_cap.get(cv::CAP_PROP_FRAME_HEIGHT));
+ config.resolution.setWidth(m_cap.get(cv::CAP_PROP_FRAME_WIDTH));
+
+ return true;
+}
+
+void findMinMaxBinarySearch(cv::VideoCapture &m_cap, int propid, double &min, double &max, double &val) {
+ double high = 10000;
+ double low = val;
+ double guess;
+ double actual;
+
+ //
+ // search for max
+ //
+
+ while(true) {
+ if (high-low <= 1) {
+ max = low;
+ break;
+ }
+
+ guess = (high+low)/2;
+ guess = std::round(guess);
+ m_cap.set(propid, guess);
+ actual = m_cap.get(propid);
+ if (guess == actual)
+ low = guess;
+ else
+ high = guess;
+
+ }
+
+ //
+ // search for min
+ //
+
+ high = val;
+ low = -10000;
+ while(true) {
+ if (high-low <= 1) {
+ min = high;
+ break;
+ }
+
+ guess = (high+low)/2;
+ guess = std::round(guess);
+ m_cap.set(propid, guess);
+ actual = m_cap.get(propid);
+ if (guess == actual)
+ high = guess;
+ else
+ low = guess;
+ }
+ m_cap.set(propid, val);
+}
+
+bool VideoStreamWorker::getCameraGainRange(bool &bGain, double &min, double &max, double &gain) {
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() == false)
+ return false;
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false)
+ return false;
+ spdlog::get("logger")->trace("getCameraGainRange");
+ bGain = true;
+ gain = m_cap.get(cv::CAP_PROP_GAIN);
+ if (gain == -1) {
+ bGain = false;
+ gain = m_cap.get(cv::CAP_PROP_BRIGHTNESS);
+ }
+ m_gain_mode = gain;
+
+ // now set to -10,000 and 10,000 to find min and max
+ int propid = cv::CAP_PROP_GAIN;
+ if (bGain == false)
+ propid = cv::CAP_PROP_BRIGHTNESS;
+
+ m_cap.set(propid,-10000.0);
+ min = m_cap.get(propid);
+ m_cap.set(propid,10000.0);
+ max = m_cap.get(propid);
+ m_cap.set(propid,gain);
+
+
+ if (max != min) {
+ return true;
+ }
+
+ // max is min. We need to do a binary search.
+ findMinMaxBinarySearch(m_cap, propid, min, max, gain);
+
+ return true;
+}
+
+void VideoStreamWorker::setCameraGain(bool bGain, double gain) {
+ spdlog::get("logger")->trace("setCameraGain");
+
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() == false)
+ return;
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false)
+ return;
+ int propid = cv::CAP_PROP_GAIN;
+ if (bGain == false)
+ propid = cv::CAP_PROP_BRIGHTNESS;
+ m_cap.set(propid,gain);
+}
+
+
+bool VideoStreamWorker::getCameraExposureRange(double &min, double &max, double &exposure) {
+ spdlog::get("logger")->trace("getCameraExposureRange");
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() == false)
+ return false;
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false)
+ return false;
+
+ exposure = m_cap.get(cv::CAP_PROP_EXPOSURE);
+
+ m_cap.set(cv::CAP_PROP_EXPOSURE,-10000.0);
+ min = m_cap.get(cv::CAP_PROP_EXPOSURE);
+ m_cap.set(cv::CAP_PROP_EXPOSURE,10000.0);
+ max = m_cap.get(cv::CAP_PROP_EXPOSURE);
+ m_cap.set(cv::CAP_PROP_EXPOSURE,exposure);
+
+ if (max != min) {
+ return true;
+ }
+
+ // max is min. We need to do a binary search.
+ findMinMaxBinarySearch(m_cap, cv::CAP_PROP_EXPOSURE, min, max, exposure);
+
+ return true;
+}
+
+void VideoStreamWorker::getAutoExpSetting(bool &bFeatureAvailable, bool &bAuto) {
+ // .75 auto on, .25 auto off alternately 3=auto on and 1 auto off; -1 means unavailable
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() == false)
+ return;
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false) {
+ bFeatureAvailable=false;
+ return;
+ }
+ double val = m_cap.get(cv::CAP_PROP_AUTO_EXPOSURE);
+ if (val == -1) {
+ bFeatureAvailable = false;
+ return;
+ }
+ bFeatureAvailable = true;
+ if (val == .25 || val == 1) {
+ bAuto = false;
+ return;
+ }
+ if (val == 0.75 || val == 3) {
+ bAuto = true;
+ return;
+ }
+ bFeatureAvailable = false;
+ spdlog::get("logger")->error("strange auto exp value: {}", val);
+
+}
+
+void VideoStreamWorker::setAutoExpSetting(bool bAuto, bool bLockMutex) {
+ if (bLockMutex)
+ QMutexLocker locker(&m_controlMutex);
+ if (m_cap.isOpened() == false)
+ return;
+ bool isInt = false;
+ m_source.toInt(&isInt);
+ if (isInt == false)
+ return;
+ double val = m_cap.get(cv::CAP_PROP_AUTO_EXPOSURE);
+ if (bAuto && val == .25) {
+ m_cap.set(cv::CAP_PROP_AUTO_EXPOSURE, .75);
+ return;
+ }
+ if (bAuto && val == 1) {
+ m_cap.set(cv::CAP_PROP_AUTO_EXPOSURE, 3);
+ return;
+ }
+ if (!bAuto && val == .75) {
+ m_cap.set(cv::CAP_PROP_AUTO_EXPOSURE, .25);
+ return;
+ }
+ if (!bAuto && val == 3) {
+ m_cap.set(cv::CAP_PROP_AUTO_EXPOSURE, 1);
+ return;
+ }
+ // if we get here, either we don't know what value to use or the feature is unavailable
+}
diff --git a/videostreamworker.h b/videostreamworker.h
new file mode 100644
index 00000000..5589d838
--- /dev/null
+++ b/videostreamworker.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class VideoStreamWorker : public QObject {
+ Q_OBJECT
+public:
+ explicit VideoStreamWorker(const QString &source, QObject *parent = nullptr);
+ ~VideoStreamWorker();
+ struct CameraConfiguration {
+ QSize resolution;
+ double exposure;
+ double auto_exposure;
+ double brightness;
+ double gain;
+ };
+ bool getCameraConfiguration(CameraConfiguration &config);
+ bool getCameraGainRange(bool &bGain, double &min, double &max, double &gain);
+ void setCameraGain(bool bGain, double gain);
+ bool getCameraExposureRange(double &min, double &max, double &exposure);
+ void getAutoExpSetting(bool &bFeatureAvailable, bool &bAuto);
+ void setAutoExpSetting(bool bAuto, bool bLockMutex = true);
+
+public slots:
+ void startStream();
+ void fetchNextFrame();
+ void stop();
+ void changeSource(QString newSource);
+ void setResolution(int width, int height);
+ void dumpAll();
+ void requestCameraSetting(QString prop , double val);
+
+
+signals:
+ void streamStarted();
+ void streamError(const QString &errorStr);
+ void frameReady(const cv::Mat &frame);
+
+private:
+ void captureLoop();
+
+ QString m_source;
+ cv::VideoCapture m_cap;
+ cv::Mat m_latestFrame;
+
+ QMutex m_frameMutex; // Protects m_latestFrame
+ QMutex m_controlMutex; // Protects m_cap and stream state changes
+
+ std::atomic m_running{false};
+ std::unique_ptr m_captureThread;
+
+ bool m_gain_mode=true; // this is set to false if gain is unavailable and we will be using brightness settings in lieu of gain
+ int savedResolutionWidth = 640;
+ int savedResolutionHeight = 480;
+
+};
diff --git a/wavefront.cpp b/wavefront.cpp
index 361e9577..9d125a39 100644
--- a/wavefront.cpp
+++ b/wavefront.cpp
@@ -30,3 +30,34 @@ void wavefront::cloneMatricesFrom(const wavefront &wf)
workData = wf.workData.clone();
workMask = wf.workMask.clone();
}
+
+wavefront::wavefront( const wavefront &wf):
+ data(wf.data.clone()),
+ nulledData(wf.nulledData.clone()),
+ mask(wf.mask.clone()),
+ workData(wf.workData.clone()),
+ workMask(wf.workMask.clone()),
+ InputZerns(wf.InputZerns),
+ regions(wf.regions),
+ zernEnablesApplied(wf.zernEnablesApplied),
+ gaussian_diameter(wf.gaussian_diameter),
+ m_outside(wf.m_outside),
+ m_inside(wf.m_inside),
+ GBSmoothingValue(wf.GBSmoothingValue),
+ gbValue(wf.gbValue),
+ m_origin(wf.m_origin),
+ name(wf.name),
+ lambda(wf.lambda),
+ diameter(wf.diameter),
+ roc(wf.roc),
+ min(wf.min),
+ max(wf.max),
+ std(wf.std),
+ mean(wf.mean),
+ dirtyZerns(wf.dirtyZerns),
+ regions_have_been_expanded(wf.regions_have_been_expanded),
+ gbEnabled(wf.gbEnabled),
+ wasSmoothed(wf.wasSmoothed),
+ useSANull(wf.useSANull),
+ m_manuallyInverted(wf.m_manuallyInverted)
+{}
diff --git a/wavefront.h b/wavefront.h
index 489c4797..24fe4e69 100644
--- a/wavefront.h
+++ b/wavefront.h
@@ -32,13 +32,11 @@ enum class WavefrontOrigin : std::uint8_t {
Zernikes,
Smoothed
};
-class wavefront
-{
-public:
+class wavefront { public:
wavefront();
~wavefront() = default;
- wavefront( const wavefront &wf) = default;
- wavefront( wavefront && ) = default;
+ wavefront( const wavefront &wf);
+ wavefront( wavefront && );
wavefront& operator=( const wavefront & ) = default;
wavefront& operator=(wavefront &&) = default;
void cloneMatricesFrom(const wavefront &wf);
@@ -49,20 +47,19 @@ class wavefront
cv::Mat_ workData;
cv::Mat_ workMask;
std::vector InputZerns;
+
+ QVector > regions;
+
std::vector zernEnablesApplied;
+
double gaussian_diameter;
- bool gbEnabled;
- double gbValue;
- bool wasSmoothed;
- bool useSANull;
+ CircleOutline m_outside;
+ CircleOutline m_inside;
double GBSmoothingValue;
+ double gbValue;
WavefrontOrigin m_origin;
- bool m_manuallyInverted; // true if user inverted this wavefront
-
QString name;
double lambda;
- CircleOutline m_outside;
- CircleOutline m_inside;
double diameter;
double roc;
double min;
@@ -70,10 +67,11 @@ class wavefront
double std;
double mean;
bool dirtyZerns;
- QVector > regions;
bool regions_have_been_expanded;
-
-
+ bool gbEnabled;
+ bool wasSmoothed;
+ bool useSANull;
+ bool m_manuallyInverted; // true if user inverted this wavefront
};
#endif // WAVEFRONT_H
diff --git a/zernikeeditdlg.cpp b/zernikeeditdlg.cpp
index f7815d2d..994c1a0c 100644
--- a/zernikeeditdlg.cpp
+++ b/zernikeeditdlg.cpp
@@ -84,7 +84,7 @@ void zernikeEditDlg::on_createSurface_clicked()
}
}
- m_sm->createSurfaceFromPhaseMap(result, CircleOutline(QPointF(xcen,ycen),rad),
+ m_sm->createWaveFrontFromPhaseMap(result, CircleOutline(QPointF(xcen,ycen),rad),
CircleOutline(QPointF(0,0),0),
QString("Zernike_Wavefront"), WavefrontOrigin::Zernikes);
}
diff --git a/zernikes.h b/zernikes.h
index 2a7667fa..d2121982 100644
--- a/zernikes.h
+++ b/zernikes.h
@@ -31,7 +31,7 @@ const char* const zernsNames[] =
"X Coma",
"Y Coma",
"Spherical",
- "X Trefoi",
+ "X Trefoil",
"Y Trefoil",
"X 2nd Astig",
"Y 2nd Astig",
diff --git a/zernikesmoothingdlg.cpp b/zernikesmoothingdlg.cpp
index 80f47aff..158b5917 100644
--- a/zernikesmoothingdlg.cpp
+++ b/zernikesmoothingdlg.cpp
@@ -133,7 +133,7 @@ void ZernikeSmoothingDlg::on_createWaveFront_clicked()
l.back().replace(".wft","");
l.back().append(QString("_sm%1").arg(m_noOfTerms));
- m_sm->createSurfaceFromPhaseMap(result, m_wf.m_outside, m_wf.m_inside
+ m_sm->createWaveFrontFromPhaseMap(result, m_wf.m_outside, m_wf.m_inside
,l.back(), WavefrontOrigin::Smoothed);
if (ui->showResidual->isChecked()){