commit 5efbdcbd6afdce392b270ab1c8e1a14328a60bf1 Author: uvos Date: Thu Jun 10 12:09:44 2021 +0200 Initial commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6c8b95f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.5) + +project(MAClient LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Concurrent REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Concurrent REQUIRED) +find_package(OpenCV REQUIRED) + +set(PROJECT_SOURCES + res/resources.qrc + src/main.cpp + src/cameras.cpp + src/cameras.h + src/camera.cpp + src/camera.h + src/profile.cpp + src/profile.h + src/imagepipeline.cpp + src/imagepipeline.h + src/ui/cameradialog.cpp + src/ui/cameradialog.h + src/ui/cameradialog.ui + src/ui/profiledialog.cpp + src/ui/profiledialog.h + src/ui/profiledialog.ui + src/ui/editprofiledialog.h + src/ui/editprofiledialog.cpp + src/ui/editprofiledialog.ui + src/ui/mainwindow.cpp + src/ui/mainwindow.h + src/ui/mainwindow.ui + src/ui/cvimageviewer.cpp + src/ui/cvimageviewer.h + src/ui/cvimageviewer.ui + src/ui/led.cpp + src/ui/led.h + src/ui/cameralistwidget.h + src/ui/cameralistwidget.cpp +) + + +add_executable(MAClient ${PROJECT_SOURCES}) + +target_link_libraries(MAClient PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Concurrent ${OpenCV_LIBS} -luvoscam -luvosled -luvosunwrap) +target_include_directories(${PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS} src src/ui) diff --git a/res/noimage.png b/res/noimage.png new file mode 100644 index 0000000..c8a0e5a Binary files /dev/null and b/res/noimage.png differ diff --git a/res/resources.qrc b/res/resources.qrc new file mode 100644 index 0000000..5739fb3 --- /dev/null +++ b/res/resources.qrc @@ -0,0 +1,6 @@ + + + noimage.png + splash.png + + diff --git a/res/splash.png b/res/splash.png new file mode 100644 index 0000000..88d9f32 Binary files /dev/null and b/res/splash.png differ diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 0000000..3640513 --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,18 @@ +#include "camera.h" +#include +#include +#include +#include + +Camera::Camera(cam::Camera::Description desc) +{ + cameraId_ = desc.getHash(); + std::function cb = [this](cv::Mat image){newImage(Image(image, cameraId_));}; + camera_ = new cam::Camera(cb); + camera_->openCamera(desc); +} + +Camera::~Camera() +{ + delete camera_; +} diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 0000000..3229f01 --- /dev/null +++ b/src/camera.h @@ -0,0 +1,46 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "profile.h" + +class Camera : public QObject +{ + Q_OBJECT +public: + class Image + { + public: + cv::Mat mat; + size_t cameraId; + Image(cv::Mat mati, size_t cameraIdi): mat(mati), cameraId(cameraIdi){} + Image() = default; + Image(const Image& img) = default; + }; +private: + cam::Camera* camera_ = nullptr; + + void callback(cv::Mat); + size_t cameraId_; + +signals: + void newImage(Image); + +public: + Camera(cam::Camera::Description); + ~Camera(); + cam::Camera* cam(){return camera_;} + size_t id() const {return cameraId_;} + bool operator==(const Camera& cam){return cam.id() == id();} + bool operator!=(const Camera& cam){return !operator==(cam);} +}; + +#endif // CAMERA_H diff --git a/src/cameras.cpp b/src/cameras.cpp new file mode 100644 index 0000000..54ad345 --- /dev/null +++ b/src/cameras.cpp @@ -0,0 +1,181 @@ +#include "cameras.h" +#include +#include +#include +#include + +Cameras::Cameras(uvosled* led): led_(led) +{ + +} + +bool Cameras::setCameras(const std::vector& descriptions) +{ + clear(); + bool ret = true; + for(auto& camera : descriptions) + { + if(!addCamera(camera)) + ret = false; + } + return ret; +} + + +void Cameras::clear() +{ + free_ = false; + for(auto& camera : cameras_) + cameraRemoved(camera); + cameras_.clear(); + if(led_) + uvosled_set_current(led_, 0xFF, 0); +} + +std::vector Cameras::getCameras() +{ + std::vector desc; + desc.reserve(cameras_.size()); + for(std::shared_ptr camera : cameras_) + desc.push_back(camera->cam()->getDescription()); + return desc; +} + +bool Cameras::addCamera(const cam::Camera::Description& desc) +{ + cameras_.push_back(std::shared_ptr(new Camera(desc))); + + if(!cameras_.back()->cam()->isOpen()) + { + cameras_.pop_back(); + return false; + } + + cameras_.back()->cam()->setTriggerMode(cam::Camera::TRIGGER_SOFTWARE); + cameras_.back()->cam()->setExposureTime(exposrueTime_*1000000); + setFree(false); + cameraAdded(cameras_.back()); + qDebug()<<"Using camera"<id(); + return true; +} + +void Cameras::trigger() +{ + for(auto& camera : cameras_) + camera->cam()->trigger(); + + if(led_ && !free_) + uvosled_capture(led_, lighting_.mask, lighting_.brightness, exposrueTime_*1.5, exposrueTime_*0.25); +} + +bool Cameras::start() +{ + bool ret = true; + for(auto& camera : cameras_) + { + if(!camera->cam()->startAcquisition()) + ret = false; + } + + if(free_) + uvosled_set_current(led_, lighting_.mask, lighting_.brightness); + + return ret; +} + +bool Cameras::stop() +{ + bool ret = true; + for(auto& camera : cameras_) + { + if(!camera->cam()->stopAcquisition()) + ret = false; + } + + if(led_) + uvosled_set_current(led_, 0xFF, 0); + return ret; +} + +bool Cameras::setFree(bool free) +{ + stop(); + free_ = free; + bool ret = true; + + for(auto& camera : cameras_) + { + if(!camera->cam()->setAcquisitionMode(free ? cam::Camera::MODE_FREE : cam::Camera::MODE_SINGLE)) + ret = false; + } + return ret; +} + +bool Cameras::setExposureTime(double exposureTime) +{ + stop(); + exposrueTime_ = exposureTime; + bool ret = true; + for(auto& camera : cameras_) + { + if(!camera->cam()->setExposureTime(exposrueTime_*1000000)) + ret = false; + } + return ret; +} + +void Cameras::imageRecived(Camera::Image img) +{ + bool allreadyUpdated = false; + for(auto& camera : cameras_) + { + for(auto& image : images_) + { + if(image.cameraId == camera->id()) + { + allreadyUpdated = true; + goto FOUND; + } + } + } + FOUND: + + if(!allreadyUpdated) + images_.push_back(img); + + if(images_.size() == cameras_.size()) + newImages(images_); + + images_.clear(); +} + +void Cameras::store(QSettings &settings) +{ + std::vector available = cam::Camera::getAvailableCameras(); + + settings.beginWriteArray(GROUP, cameras_.size()); + for(size_t i = 0; i < cameras_.size(); ++i) + { + settings.setArrayIndex(i); + settings.setValue("id", static_cast(cameras_[i]->id())); + } + settings.endArray(); +} + +void Cameras::load(QSettings &settings) +{ + std::vector available = cam::Camera::getAvailableCameras(); + + int size = settings.beginReadArray(GROUP); + for(int i = 0; i < size; ++i) + { + settings.setArrayIndex(i); + size_t hash = settings.value("id", 0).toULongLong(); + for(auto& camera : available) + { + if(camera.getHash() == hash) + addCamera(camera); + } + } + settings.endArray(); +} diff --git a/src/cameras.h b/src/cameras.h new file mode 100644 index 0000000..097b44b --- /dev/null +++ b/src/cameras.h @@ -0,0 +1,59 @@ +#ifndef CAMERAS_H +#define CAMERAS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "profile.h" +#include "camera.h" + +class Cameras : public QObject +{ + Q_OBJECT + + static constexpr const char* GROUP = "Cameras"; + +private: + + uvosled* led_; + bool free_ = false; + double exposrueTime_ = 1.0/60.0; + LightingSetup lighting_; + std::vector> cameras_; + std::vector images_; + +private slots: + void imageRecived(Camera::Image img); + +signals: + void cameraRemoved(std::shared_ptr camera); + void cameraAdded(std::shared_ptr camera); + void newImages(std::vector images_); + +public slots: + + bool setExposureTime(double exposureTime); + void trigger(); + bool start(); + bool stop(); + bool setFree(bool free); + void setLighting(const LightingSetup& lighting) {lighting_ = lighting;} + +public: + bool setCameras(const std::vector& descriptions); + bool addCamera(const cam::Camera::Description& desc); + std::vector getCameras(); + size_t numCameras(){return cameras_.size();} + void clear(); + Cameras(uvosled* led = nullptr); + void load(QSettings& settings); + void store(QSettings& settings); +}; + +#endif // CAMERAS_H diff --git a/src/imagepipeline.cpp b/src/imagepipeline.cpp new file mode 100644 index 0000000..53fe1ed --- /dev/null +++ b/src/imagepipeline.cpp @@ -0,0 +1,74 @@ +#include "imagepipeline.h" +#include +#include +#include + +ImagePipeline::ImagePipeline(Cameras* cameras, QObject *parent): QObject(parent), cameras_(cameras) +{ +} + +cv::Mat ImagePipeline::process(const Profile profile, std::vector images) +{ + std::vector remapedImages; + remapedImages.reserve(images.size()); + + for(Camera::Image& image : images) + { + for(auto& camera : profile.cameras) + { + if(camera.id == image.cameraId) + { + if(camera.darkmap.data) + image.mat = image.mat - camera.darkmap; + remapedImages.push_back(applyRemap(image.mat, camera.remapMap)); + break; + } + } + } + + cv::Mat output = simpleStich(remapedImages); + output.convertTo(output, CV_32FC1, 1.0/255.0, 0); + + if(profile.lightmap.data) + normalize(output, profile.lightmap); + return output; +} + +void ImagePipeline::apply(std::vector images) +{ + if(!invalid_) + { + futureImageWatchers_.push_back(new QFutureWatcher()); + connect(futureImageWatchers_.back(), &QFutureWatcher::finished, this, &ImagePipeline::imageFinished); + + QFuture futureImage = QtConcurrent::run(&ImagePipeline::process, profile_, images); + futureImageWatchers_.back()->setFuture(futureImage); + } +} + +void ImagePipeline::setProfile(const Profile& profile) +{ + profile_ = profile; + cameras_->setExposureTime(profile_.exposureTime); + cameras_->setLighting(profile_.lighting); + if(!profile_.camerasSufficant(cameras_->getCameras())) + { + invalid_ = true; + sigInvalidProfile("A camera required for this profile is not available"); + } +} + +void ImagePipeline::imageFinished() +{ + for(size_t i = 0; i < futureImageWatchers_.size(); ++i) + { + if(futureImageWatchers_[i]->isFinished()) + { + cv::Mat result = futureImageWatchers_[i]->result(); + sigResult(Camera::Image(result, 0)); + delete futureImageWatchers_[i]; + futureImageWatchers_.erase(futureImageWatchers_.begin()+i); + i--; + } + } +} diff --git a/src/imagepipeline.h b/src/imagepipeline.h new file mode 100644 index 0000000..5ca8d86 --- /dev/null +++ b/src/imagepipeline.h @@ -0,0 +1,41 @@ +#ifndef IMAGEPIPELINE_H +#define IMAGEPIPELINE_H + +#include +#include + +#include + +#include "profile.h" +#include "cameras.h" + +class ImagePipeline: public QObject +{ + Q_OBJECT + +private: + + Cameras* cameras_; + Profile profile_; + bool invalid_ = true; + std::vector*> futureImageWatchers_; + + static cv::Mat process(const Profile profile, std::vector images); + +private slots: + + void apply(std::vector images); + void imageFinished(); + +signals: + void sigInvalidProfile(QString message); + void sigResult(Camera::Image image); + +public slots: + void setProfile(const Profile& profile); + +public: + ImagePipeline(Cameras* cameras, QObject* parent = nullptr); +}; + +#endif // IMAGEPIPELINE_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..efbc8c5 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cameras.h" +#include "./ui/cameradialog.h" +#include "profile.h" +#include "./ui/mainwindow.h" +#include "imagepipeline.h" + +const char* organziation = "UVOS"; +const char* application = "UVOS"; +const char* version = "UVOS"; + +std::vector showCameraSelectionDialog() +{ + std::vector ret; + CameraDialog diag(cam::Camera::getAvailableCameras()); + + diag.show(); + if(diag.exec() == QDialog::Accepted) + ret = diag.getDescriptions(); + return ret; +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QCoreApplication::setOrganizationName("UVOS"); + QCoreApplication::setOrganizationDomain("uvos.xyz"); + QCoreApplication::setApplicationName("LubircantThiknessMapperUi"); + QCoreApplication::setApplicationVersion("0.1"); + + QSplashScreen splash(QPixmap(":/images/splash.png")); + splash.show(); + + QDir().mkpath(Profile::profileLocation()); + QDir().mkpath(CameraSetup::camerasLocation()); + + qRegisterMetaType("cv::Mat"); + qRegisterMetaType("size_t"); + qRegisterMetaType("Image"); + + QSettings settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()); + + uvosled led; + int uvosledRet = uvosled_connect(&led); + + Cameras cameras(uvosledRet < 0 ? nullptr : &led); + ImagePipeline pipe(&cameras); + + MainWindow w; + + QObject::connect(&cameras, &Cameras::cameraAdded, &w, &MainWindow::addCamera); + QObject::connect(&cameras, &Cameras::cameraRemoved, &w, &MainWindow::removeCamera); + QObject::connect(&w, &MainWindow::sigCapture, [&cameras](){cameras.start(); cameras.trigger();}); + QObject::connect(&w, &MainWindow::sigChooseCameras, [&cameras](){cameras.setCameras(showCameraSelectionDialog());}); + QObject::connect(&pipe, &ImagePipeline::sigResult, w.mainImageViewer(), &CvImageViewer::setImage, Qt::QueuedConnection); + + QObject::connect(&w, &MainWindow::sigProfile, [&pipe](QString name) + { + Profile profile; + profile.load(name); + pipe.setProfile(profile); + }); + + cameras.load(settings); + + splash.hide(); + w.show(); + + if(uvosledRet < 0) + QMessageBox::warning(&w, "UVOS LED", "Can not connect to the UVOS LED device"); + + if(cameras.getCameras().empty()) + { + QMessageBox::information(&w, "Cameras", "No cameras configured, please choose at least one camera."); + cameras.setCameras(showCameraSelectionDialog()); + } + + int ret = a.exec(); + + cameras.store(settings); + + return ret; +} diff --git a/src/profile.cpp b/src/profile.cpp new file mode 100644 index 0000000..0db67b5 --- /dev/null +++ b/src/profile.cpp @@ -0,0 +1,151 @@ +#include "profile.h" +#include +#include +#include +#include + +void CameraSetup::store() const +{ + cv::imwrite((camerasLocation() + QString::number(id) + ".darkmap.png").toStdString(), darkmap); +} + +void CameraSetup::load(size_t cameraId) +{ + id = cameraId; + darkmap = cv::imread((camerasLocation() + QString::number(id) + ".darkmap.png").toStdString()); +} + +QString CameraSetup::camerasLocation() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cameras/"; +} + +void LightingSetup::store(QSettings& settings) const +{ + settings.setValue(GROUP + QString("/brightness"), brightness); + settings.setValue(GROUP + QString("/mask"), mask); +} + +void LightingSetup::load(const QSettings& settings) +{ + brightness = settings.value(GROUP + QString("/brightness"), 0.25).toDouble(); + mask = settings.value(GROUP + QString("/maks"), 0).toUInt(); +} + +void Profile::store(QSettings& settings) const +{ + lighting.store(settings); + settings.setValue(GROUP + QString("/id"), static_cast(id)); + settings.setValue(GROUP + QString("/exposureTime"), exposureTime); + settings.setValue(GROUP + QString("/name"), name_); + + cv::imwrite((profileLocation() + QString::number(id) + ".lightmap.png").toStdString(), lightmap); + + settings.beginWriteArray(GROUP + QString("/cameras"), cameras.size()); + for(size_t i = 0; i < cameras.size(); ++i) + { + const CameraSetup& camera = cameras[i]; + settings.setArrayIndex(i); + camera.store(); + settings.setValue("id", static_cast(camera.id)); + } + settings.endArray(); +} + +void Profile::load(QSettings &settings) +{ + lighting.load(settings); + id = settings.value(GROUP + QString("/id")).toULongLong(); + exposureTime = settings.value(GROUP + QString("/exposureTime"), 1.0/60.0).toDouble(); + name_ = settings.value(GROUP + QString("/name"), "NULL").toString(); + + lightmap = cv::imread((profileLocation() + QString::number(id) + ".lightmap.png").toStdString()); + + int size = settings.beginReadArray(GROUP + QString("/cameras")); + for(int i = 0; i < size; ++i) + { + settings.setArrayIndex(i); + cameras.push_back(CameraSetup()); + cameras.back().load(settings.value("id", 0).toULongLong()); + } + settings.endArray(); +} + +void Profile::load(const QString& name) +{ + QSettings settings(profileLocation() + name + ".profile.ini"); + name_=name; + load(settings); +} + +void Profile::store(const QString& name) +{ + QSettings settings(profileLocation() + name + ".profile.ini"); + name_=name; + store(settings); +} + +void Profile::load() +{ + load(name_); +} + +void Profile::store() +{ + store(name_); +} + +void Profile::deleteProfile() +{ + QFile::remove(profileLocation() + name_ + ".profile.ini"); +} + +QList Profile::avaiableProfiles() +{ + QDir dir(profileLocation()); + dir.setFilter(QDir::Files); + QStringList filters; + filters<<"*.ini"; + dir.setNameFilters(filters); + QList ret; + QStringList list = dir.entryList(); + + for(QString string : list) + ret.push_back(string.split(".")[0]); + return ret; +} + +bool Profile::camerasSufficant(const std::vector& desc) const +{ + bool invalid = false; + for(auto& cameraProfile : cameras) + { + bool found = false; + for(auto& camera : desc) + { + if(camera.getHash() == cameraProfile.id) + { + found = true; + break; + } + } + + if(!found) + { + invalid = true; + break; + } + } + return !invalid; +} + + +QString Profile::profileLocation() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/profiles/"; +} + +Profile::Profile(const QString& name): name_(name) +{ + id = QRandomGenerator::system()->generate64(); +} diff --git a/src/profile.h b/src/profile.h new file mode 100644 index 0000000..52d2634 --- /dev/null +++ b/src/profile.h @@ -0,0 +1,67 @@ +#ifndef PROFILE_H +#define PROFILE_H +#include +#include +#include +#include +#include +#include + +#include "camera.h" + +class CameraSetup +{ +public: + size_t id; + RemapMap remapMap; + cv::Mat darkmap; + cv::Mat bgmask; + void store() const; + void load(size_t cameraId); + static QString camerasLocation(); + static QString remapMapLocation(size_t cameraId); + static QString bgmaskLocation(size_t cameraId); + static QString darkmapLocation(size_t cameraId); +}; + +class LightingSetup +{ + static constexpr const char* GROUP = "Lighting"; + +public: + double brightness = 0.25; + uint8_t mask = 0; + + void store(QSettings& settings) const; + void load(const QSettings& settings); +}; + +class Profile +{ + static constexpr const char* GROUP = "Profile"; + + QString name_ = "NULL"; + +public: + uint64_t id; + LightingSetup lighting; + double exposureTime = 1.0/60.0; + cv::Mat lightmap; + std::vector cameras; + + Profile(const QString& name = "NULL"); + void store(QSettings& settings) const; + void store(const QString& name); + void store(); + void load(QSettings& settings); + void load(const QString& name); + void load(); + void deleteProfile(); + void setName(const QString name){name_=name;} + QString getName() const {return name_;} + bool camerasSufficant(const std::vector& desc) const; + static QList avaiableProfiles(); + static QString profileLocation(); +}; + +#endif // PROFILE_H diff --git a/src/ui/cameradialog.cpp b/src/ui/cameradialog.cpp new file mode 100644 index 0000000..3d8f1a8 --- /dev/null +++ b/src/ui/cameradialog.cpp @@ -0,0 +1,21 @@ +#include "cameradialog.h" +#include "ui_cameradialog.h" +#include "../cameras.h" + +CameraDialog::CameraDialog(const std::vector& desc, QWidget *parent) : + QDialog(parent), + ui(new Ui::CameraDialog) +{ + ui->setupUi(this); + ui->listView->setCameras(desc); +} + +CameraDialog::~CameraDialog() +{ + delete ui; +} + +std::vector CameraDialog::getDescriptions() +{ + return ui->listView->getSelectedDescriptions(); +} diff --git a/src/ui/cameradialog.h b/src/ui/cameradialog.h new file mode 100644 index 0000000..04cfd58 --- /dev/null +++ b/src/ui/cameradialog.h @@ -0,0 +1,26 @@ +#ifndef CAMERADIALOG_H +#define CAMERADIALOG_H + +#include +#include +#include +#include + +namespace Ui { +class CameraDialog; +} + +class CameraDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CameraDialog(const std::vector& desc, QWidget *parent = nullptr); + ~CameraDialog(); + std::vector getDescriptions(); + +private: + Ui::CameraDialog *ui; +}; + +#endif // CAMERADIALOG_H diff --git a/src/ui/cameradialog.ui b/src/ui/cameradialog.ui new file mode 100644 index 0000000..96c452d --- /dev/null +++ b/src/ui/cameradialog.ui @@ -0,0 +1,91 @@ + + + CameraDialog + + + + 0 + 0 + 400 + 300 + + + + false + + + Select Cameras + + + + + + Select Cameras: + + + + + + + + + + Configure Cameras + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + CameraListWidget + QListView +
cameralistwidget.h
+
+
+ + + + buttonBox + accepted() + CameraDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CameraDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/ui/cameralistwidget.cpp b/src/ui/cameralistwidget.cpp new file mode 100644 index 0000000..4eadda9 --- /dev/null +++ b/src/ui/cameralistwidget.cpp @@ -0,0 +1,45 @@ +#include "cameralistwidget.h" +#include +#include +#include "../cameras.h" + +CameraListWidget::CameraListWidget(QWidget* parent): QTableWidget(parent) +{ + setColumnCount(1); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::MultiSelection); + setHorizontalHeaderItem(0, new QTableWidgetItem("Camera")); + setRowCount(desc_.size()); + horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); +} + +void CameraListWidget::setConfigured(std::vector configured) +{ + setColumnCount(2); + setHorizontalHeaderItem(1, new QTableWidgetItem("Configured")); + for(size_t i = 0; i < desc_.size() && i < configured.size(); ++i) + setItem(static_cast(i), 0, new QTableWidgetItem("No")); +} + +void CameraListWidget::setCameras(const std::vector& desc) +{ + desc_ = desc; + qDebug()<<"cameras: "<(desc_.size())); + for(size_t i = 0; i < desc_.size(); ++i) + { + setItem(static_cast(i), 0, new QTableWidgetItem((desc_[i].vendor + " " + desc_[i].model).c_str())); + } +} + +std::vector CameraListWidget::getSelectedDescriptions() +{ + QList selected = selectedIndexes(); + std::vector cameras; + for(auto& selection : selected) + { + if(selection.column() == 0) + cameras.push_back(desc_[selection.row()]); + } + return cameras; +} diff --git a/src/ui/cameralistwidget.h b/src/ui/cameralistwidget.h new file mode 100644 index 0000000..48e10cb --- /dev/null +++ b/src/ui/cameralistwidget.h @@ -0,0 +1,19 @@ +#ifndef CAMERALISTWIDGET_H +#define CAMERALISTWIDGET_H + +#include +#include +#include + +class CameraListWidget : public QTableWidget +{ +private: + std::vector desc_; +public: + CameraListWidget(QWidget* parent); + void setCameras(const std::vector& desc); + void setConfigured(std::vector configured); + std::vector getSelectedDescriptions(); +}; + +#endif // CAMERALISTWIDGET_H diff --git a/src/ui/cvimageviewer.cpp b/src/ui/cvimageviewer.cpp new file mode 100644 index 0000000..150161f --- /dev/null +++ b/src/ui/cvimageviewer.cpp @@ -0,0 +1,36 @@ +#include "cvimageviewer.h" +#include +#include + +CvImageViewer::CvImageViewer(QWidget *parent, size_t lastId) : + QWidget(parent), + lastId_(lastId) +{ + qimage_.load(":/images/noimage.png"); +} + +CvImageViewer::~CvImageViewer() +{ +} + +void CvImageViewer::setImage(Camera::Image img) +{ + image_ = img.mat; + if(image_.type() == CV_8UC3 || image_.type() == CV_8SC3) + qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_RGB888); + else if(image_.type() == CV_8UC1 || image_.type() == CV_8SC1) + qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_Grayscale8); + else if(image_.type() == CV_32FC1 || image_.type() == CV_64FC1) + img.mat.convertTo(image_, CV_8UC1, 255, 0); + update(); +} + +void CvImageViewer::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event) + QPainter painter(this); + if(!fixedOnWidth_) + painter.drawImage(QRect((rect().width()-rect().height())/2, rect().y(), rect().height(), rect().height()), qimage_); + else + painter.drawImage(QRect(rect().x(), (rect().height()-rect().width())/2, rect().width(), rect().width()), qimage_); +} diff --git a/src/ui/cvimageviewer.h b/src/ui/cvimageviewer.h new file mode 100644 index 0000000..58b0f77 --- /dev/null +++ b/src/ui/cvimageviewer.h @@ -0,0 +1,30 @@ +#ifndef CVIMAGEVIEWER_H +#define CVIMAGEVIEWER_H + +#include +#include +#include "../cameras.h" + +class CvImageViewer : public QWidget +{ + Q_OBJECT + + cv::Mat image_; + QImage qimage_; + bool fixedOnWidth_ = false; + size_t lastId_; + +protected: + virtual void paintEvent(QPaintEvent* event) override; + +public slots: + void setImage(Camera::Image img); + +public: + explicit CvImageViewer(QWidget *parent = nullptr, size_t lastId = 0); + void setFixedOnWidth(bool in){fixedOnWidth_ = in;} + size_t lastId(){return lastId_;} + ~CvImageViewer(); +}; + +#endif // CVIMAGEVIEWER_H diff --git a/src/ui/cvimageviewer.ui b/src/ui/cvimageviewer.ui new file mode 100644 index 0000000..4ecee63 --- /dev/null +++ b/src/ui/cvimageviewer.ui @@ -0,0 +1,28 @@ + + + CvImageViewer + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + + + + + + + + + diff --git a/src/ui/editprofiledialog.cpp b/src/ui/editprofiledialog.cpp new file mode 100644 index 0000000..a1776ec --- /dev/null +++ b/src/ui/editprofiledialog.cpp @@ -0,0 +1,16 @@ +#include "editprofiledialog.h" +#include "ui_editprofiledialog.h" + +EditProfileDialog::EditProfileDialog(Cameras* cameras, Profile* profile, QWidget *parent) : + QDialog(parent), + profile_(profile), + cameras_(cameras), + ui(new Ui::EditProfileDialog) +{ + ui->setupUi(this); +} + +EditProfileDialog::~EditProfileDialog() +{ + delete ui; +} diff --git a/src/ui/editprofiledialog.h b/src/ui/editprofiledialog.h new file mode 100644 index 0000000..3e4f858 --- /dev/null +++ b/src/ui/editprofiledialog.h @@ -0,0 +1,28 @@ +#ifndef EDDITPROFILEDIALOG_H +#define EDDITPROFILEDIALOG_H + +#include +#include +#include "../cameras.h" + +namespace Ui { +class EditProfileDialog; +} + +class EditProfileDialog : public QDialog +{ + Q_OBJECT + Profile* profile_; + Cameras* cameras_; + + +public: + explicit EditProfileDialog(Cameras* cameras, Profile* profile, QWidget *parent = nullptr); + Profile* getProfile(){return profile_;} + ~EditProfileDialog(); + +private: + Ui::EditProfileDialog *ui; +}; + +#endif // EDDITPROFILEDIALOG_H diff --git a/src/ui/editprofiledialog.ui b/src/ui/editprofiledialog.ui new file mode 100644 index 0000000..01d78d3 --- /dev/null +++ b/src/ui/editprofiledialog.ui @@ -0,0 +1,228 @@ + + + EditProfileDialog + + + + 0 + 0 + 539 + 485 + + + + Dialog + + + + + + + + Profile Name + + + + + + + + + + + + Cameras + + + + + + + + + Configure + + + + + + + 0 + + + + + + 0 + 0 + + + + Exposure Time + + + + + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + + 0 + 0 + + + + s + + + + + + + + + + + + Lights + + + + + + + + + 0 + 0 + + + + Brightness + + + + + + + + + + + 0 + 0 + + + + % + + + + + + + + + Channels: + + + + + + + + + CH1 + + + + + + + CH2 + + + + + + + CH3 + + + + + + + CH4 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + CameraListWidget + QListView +
./src/ui/cameralistwidget.h
+
+
+ + + + buttonBox + accepted() + EditProfileDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditProfileDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/ui/led.cpp b/src/ui/led.cpp new file mode 100644 index 0000000..378a6de --- /dev/null +++ b/src/ui/led.cpp @@ -0,0 +1,31 @@ +#include "led.h" +#include + +Led::Led(QWidget* parent): QWidget(parent) +{ +} + +bool Led::lit() const +{ + return lit_; +} + +void Led::setLit(bool lit) +{ + if(lit != lit_) + { + lit_ = lit; + stateChanged(lit_); + update(); + } +} + +void Led::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event) + QPainter ledPainter(this); + ledPainter.setPen(Qt::black); + if(lit_) ledPainter.setBrush(Qt::red); + else ledPainter.setBrush(Qt::NoBrush); + ledPainter.drawEllipse(rect().adjusted(0, 0, -1, -1)); +} diff --git a/src/ui/led.h b/src/ui/led.h new file mode 100644 index 0000000..f3674d6 --- /dev/null +++ b/src/ui/led.h @@ -0,0 +1,29 @@ +#ifndef LED_H +#define LED_H + +#include + +class Led : public QWidget +{ + Q_OBJECT + + bool lit_ = false; + + public: + + Led(QWidget* parent = nullptr); + bool lit() const; + + public slots: + + void setLit(bool lit); + + signals: + + void stateChanged(bool lit); + protected: + virtual void paintEvent(QPaintEvent* event) override; + + +}; +#endif // LED_H diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp new file mode 100644 index 0000000..b02ff82 --- /dev/null +++ b/src/ui/mainwindow.cpp @@ -0,0 +1,69 @@ +#include "mainwindow.h" +#include "./ui_mainwindow.h" +#include +#include +#include + +#include "../profile.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + ui->statusbar->showMessage("idle"); + connect(ui->actionQuit, &QAction::triggered, [this](bool checked){(void)checked; close();}); + connect(ui->actionCameras, &QAction::triggered, [this](bool checked){(void)checked; sigChooseCameras();}); + connect(ui->comboBox, &QComboBox::currentTextChanged, this, &MainWindow::sigProfile); + connect(ui->pushButtonCapture, &QPushButton::clicked, this, &MainWindow::sigCapture); + ui->widget->setLit(true); + refreshProfiles(); +} + +void MainWindow::addCamera(std::shared_ptr camera) +{ + viewers_.push_back(new CvImageViewer(this, camera->id())); + viewers_.back()->setFixedOnWidth(true); + connect(camera.get(), &Camera::newImage, viewers_.back(), &CvImageViewer::setImage, Qt::QueuedConnection); + ui->viewerLayout->addWidget(viewers_.back()); +} + +void MainWindow::removeCamera(std::shared_ptr camera) +{ + for(size_t i = 0; i < viewers_.size(); ++i) + { + if(viewers_[i]->lastId() == camera->id()) + { + ui->viewerLayout->removeWidget(viewers_[i]); + delete viewers_[i]; + viewers_.erase(viewers_.begin()+i); + --i; + } + } +} + +void MainWindow::refreshProfiles() +{ + ui->comboBox->clear(); + QList profiles = Profile::avaiableProfiles(); + for(const QString& string : profiles) + ui->comboBox->addItem(string); +} + +void MainWindow::profileInconpatible(QString message) +{ + QMessageBox::warning(this, "Profile Incompatible", message); +} + +CvImageViewer* MainWindow::mainImageViewer() +{ + return ui->mainViewer; +} + +MainWindow::~MainWindow() +{ + for(CvImageViewer* viewer : viewers_) + delete viewer; + delete ui; +} + diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h new file mode 100644 index 0000000..0fb0b3f --- /dev/null +++ b/src/ui/mainwindow.h @@ -0,0 +1,39 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "../cameras.h" +#include "cvimageviewer.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT +private: + std::vector viewers_; + +signals: + void sigCapture(); + void sigProfile(QString profileName); + void sigChooseCameras(); + +public slots: + + void addCamera(std::shared_ptr camera); + void refreshProfiles(); + void profileInconpatible(QString message); + void removeCamera(std::shared_ptr camera); + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + CvImageViewer* mainImageViewer(); + +private: + Ui::MainWindow *ui; +}; +#endif // MAINWINDOW_H diff --git a/src/ui/mainwindow.ui b/src/ui/mainwindow.ui new file mode 100644 index 0000000..f334112 --- /dev/null +++ b/src/ui/mainwindow.ui @@ -0,0 +1,230 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + Qt::Horizontal + + + + Cameras + + + + + + true + + + + + 0 + 0 + 278 + 483 + + + + + + + + + + + + + + + + + + Result + + + + + + true + + + + + 0 + 0 + 467 + 315 + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Controles + + + + + + Capture + + + + + + + 0 + + + + + + 0 + 0 + + + + Profile: + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + + + + Uncal + + + + + + + + + + + + + + + + + + 0 + 0 + 800 + 32 + + + + + File + + + + + + + Settings + + + + + + + + + + + Quit + + + + + Cameras + + + + + Profile + + + + + Save + + + + + + Led + QWidget +
./src/ui/led.h
+ 1 +
+ + CvImageViewer + QWidget +
./src/ui/cvimageviewer.h
+ 1 +
+
+ + +
diff --git a/src/ui/profiledialog.cpp b/src/ui/profiledialog.cpp new file mode 100644 index 0000000..7a29b77 --- /dev/null +++ b/src/ui/profiledialog.cpp @@ -0,0 +1,37 @@ +#include "profiledialog.h" +#include "ui_profiledialog.h" +#include "editprofiledialog.h" + +ProfileDialog::ProfileDialog(Cameras* cameras, QWidget *parent) : + QDialog(parent), + cameras_(cameras), + ui(new Ui::ProfileDialog) +{ + ui->setupUi(this); + ui->listWidget->addItems(Profile::avaiableProfiles()); +} + + +ProfileDialog::~ProfileDialog() +{ + delete ui; +} + +void ProfileDialog::editProfile() +{ + +} + + +void ProfileDialog::addProfile() +{ + Profile newProfile; + EditProfileDialog dialog(cameras_, &newProfile); + dialog.show(); + int ret = dialog.exec(); + if(ret == QDialog::Accepted) + { + + } +} + diff --git a/src/ui/profiledialog.h b/src/ui/profiledialog.h new file mode 100644 index 0000000..99be760 --- /dev/null +++ b/src/ui/profiledialog.h @@ -0,0 +1,29 @@ +#ifndef PROFILEDIALOG_H +#define PROFILEDIALOG_H + +#include +#include "../cameras.h" + +namespace Ui { +class ProfileDialog; +} + +class ProfileDialog : public QDialog +{ + Q_OBJECT + + Cameras* cameras_; + +private slots: + void addProfile(); + void editProfile(); + +public: + explicit ProfileDialog(Cameras* cameras, QWidget *parent = nullptr); + ~ProfileDialog(); + +private: + Ui::ProfileDialog *ui; +}; + +#endif // PROFILEDIALOG_H diff --git a/src/ui/profiledialog.ui b/src/ui/profiledialog.ui new file mode 100644 index 0000000..173b04b --- /dev/null +++ b/src/ui/profiledialog.ui @@ -0,0 +1,92 @@ + + + ProfileDialog + + + + 0 + 0 + 400 + 300 + + + + Profiles + + + + + + Profiles: + + + + + + + + + + + + Edit + + + + + + + Add + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ProfileDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProfileDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +