#include "main_widget.hpp"
#include <QVBoxLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QGroupBox>
#include <QMessageBox>
#include <QElapsedTimer>
#include <QGraphicsSceneMouseEvent>
#include <QRubberBand>
#include <QGraphicsRectItem>
#include <cvb/image.hpp>
#include <cvb/device_factory.hpp>
#include <cvb/device.hpp>
#include <cvb/async/single_stream_handler.hpp>
#include <cvb/driver/driver.hpp>
#include <cvb/shapefinder2/classifier_factory.hpp>
MainWidget::MainWidget()
: QSplitter()
{
imageScene_ = new CustomImageScene(this);
imageView_->SetUploadMode(Cvb::UI::UploadMode::Viewport);
imageView_->SetRenderEngine(Cvb::UI::RenderEngine::Raster);
imageView_->resize(800, 600);
imageView_->setDragMode(QGraphicsView::RubberBandDrag);
QObject::connect(imageScene_, SIGNAL(SelectionDone(QRectF)), this, SLOT(HandleSelection(QRectF)));
ctrlWidget_ = new ControlWidget(this);
addWidget(imageView_);
addWidget(ctrlWidget_);
imageView_->Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
streamHandler_ = CustomStreamHandler::Create(device_->Stream(), *this);
ctrlWidget_->EnableGrabCheckBox(true);
QObject::connect(this, SIGNAL(SearchDone()), this, SLOT(ShowSearchResult()));
resize(1000, 800);
device_->Stream()->GetSnapshot();
}
void MainWidget::HandleLoad()
{
imageScene_->ClearScene();
auto fileName = QFileDialog::getOpenFileName(this, "Load Image"
, "CVB Emulator Image Lists (*.emu);;CVB Drivers (*.vin);;Images (*.bmp *.png *.tif *.tiff *.jpg)");
if (fileName.isEmpty())
return;
if (fileName.contains(".emu", Qt::CaseInsensitive))
fileName = fileName.replace("/", "\\");
try
{
if (fileName.contains(".bmp", Qt::CaseInsensitive) || fileName.contains(".png", Qt::CaseInsensitive) || fileName.contains(".tif", Qt::CaseInsensitive) || fileName.contains(".jpg", Qt::CaseInsensitive))
{
imageView_->Refresh(image);
ctrlWidget_->EnableGrabCheckBox(false);
}
else
{
imageView_->Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
streamHandler_ = CustomStreamHandler::Create(device_->Stream(), *this);
ctrlWidget_->EnableGrabCheckBox(true);
}
}
catch (const std::exception& error)
{
QMessageBox::critical(this, "Error", QString(error.what()));
}
}
void MainWidget::HandleGrab(int state)
{
if (state == Qt::CheckState::Checked)
{
imageScene_->ClearScene();
streamHandler_->Run();
}
else
{
bool wait = imageView_->IsWaitForRepaintEnabled();
if (wait)
imageView_->SetWaitForRepaintEnabled(!wait);
streamHandler_->Finish();
imageView_->SetWaitForRepaintEnabled(wait);
}
}
void MainWidget::HandleSnap()
{
device_->Stream()->GetSnapshot();
if (continuedSearch_)
HandleSearch();
else
imageScene_->ClearScene();
}
void MainWidget::HandleSave()
{
auto fileName = QFileDialog::getSaveFileName(this, "Save Image"
, "Bitmap File (*.bmp);;CVB Emulator Image Lists (*.emu);;JPEG (*.jpg, *.jpeg);;Portable Network Graphic (*.png);;Tagged Image File Format (*.tif, *.tiff);;All Files (*.*)");
if (fileName.isEmpty())
return;
}
void MainWidget::HandleLoadModel()
{
imageScene_->ClearScene();
auto fileName = QFileDialog::getOpenFileName(this, "Load SF2 Model"
, "ShapeFinder2 models (*.sf2)");
if (fileName.isEmpty())
return;
}
void MainWidget::HandleSaveModel()
{
auto fileName = QFileDialog::getSaveFileName(this, "Save SF2 Model"
, "ShapeFinder2 models (*.sf2)");
if (fileName.isEmpty())
return;
}
void MainWidget::HandleTrain()
{
if (isSelected_)
{
ctrlWidget_->SetTrainView(*selection_);
imageScene_->ClearScene();
isSelected_ = false;
}
else
{
ctrlWidget_->SetTrainView(*imageView_->Image());
auto aoi = imageView_->Image()->Bounds();
selectionRect_ =
Cvb::Rect<double>(aoi.Left(), aoi.Top(), aoi.Right(), aoi.Bottom());
}
auto centerX = static_cast<int>(std::round(selectionRect_.Left() + ((selectionRect_.Right() - selectionRect_.Left()) / 2)));
auto centerY = static_cast<int>(std::round(selectionRect_.Top() + ((selectionRect_.Bottom() - selectionRect_.Top()) / 2)));
Cvb::Rect<int> teachWindow(selectionRect_.Left() - centerX, selectionRect_.Top() - centerY, selectionRect_.Right() - centerX, selectionRect_.Bottom() - centerY);
try
{
}
catch (const std::exception & e)
{
QMessageBox::critical(this, "Error", e.what());
}
}
void MainWidget::HandleSearch()
{
if (!classifier_)
{
QMessageBox::warning(this, "SF2 Search", "Please create (train) or load a model/classifier first.");
return;
}
Search();
}
void MainWidget::HandleContSearch(int state)
{
imageScene_->ClearScene();
if (state == Qt::Checked)
continuedSearch_ = true;
else
continuedSearch_ = false;
}
template <typename T>
{
}
void MainWidget::HandleSelection(QRectF selection)
{
auto convertedArea = imageView_->Image()->ImageToPixelCoordinates(area);
isSelected_ = true;
selectionRect_ = boundingRect;
}
void MainWidget::Search()
{
if (!classifier_)
return;
double relativeThreshold = 0.55;
int minimumThreshold = 20;
int coarseLocality = 10;
if (isSelected_)
{
Cvb::Rect<int> aoi(selectionRect_.Left(), selectionRect_.Top(), selectionRect_.Right(), selectionRect_.Bottom());
searchResult_ = classifier_->SearchAll(imageView_->Image()->Plane(0), aoi, mode, relativeThreshold, minimumThreshold, coarseLocality);
}
else
{
searchResult_ = classifier_->SearchAll(imageView_->Image()->Plane(0), imageView_->Image()->Bounds(), mode, relativeThreshold, minimumThreshold, coarseLocality);
}
emit SearchDone();
}
void MainWidget::ShowSearchResult()
{
isSelected_ = false;
ctrlWidget_->SetResultInfo(searchResult_.size());
imageScene_->SetSearchResult(*imageView_->Image(), searchResult_);
}
ControlWidget::ControlWidget(QWidget* parent)
: QWidget(parent)
{
auto mainLayout = new QGridLayout;
trainImageView_->SetUploadMode(Cvb::UI::UploadMode::Image);
trainImageView_->SetRenderEngine(Cvb::UI::RenderEngine::Raster);
QPushButton* openImgBtn = new QPushButton(tr("Open Image"));
QObject::connect(openImgBtn, SIGNAL(clicked()), parent, SLOT(HandleLoad()));
grabCheckBox_ = new QCheckBox(tr("Grab"));
QObject::connect(grabCheckBox_, SIGNAL(stateChanged(int)), parent, SLOT(HandleGrab(int)));
QPushButton* snapBtn = new QPushButton(tr("Snap"));
QObject::connect(snapBtn, SIGNAL(clicked()), parent, SLOT(HandleSnap()));
QPushButton* saveBtn = new QPushButton(tr("Save"));
QObject::connect(saveBtn, SIGNAL(clicked()), parent, SLOT(HandleSave()));
contSearchCheckBox_ = new QCheckBox(tr("Continued Search"));
QObject::connect(contSearchCheckBox_, SIGNAL(stateChanged(int)), parent, SLOT(HandleContSearch(int)));
QGroupBox* groupBox = new QGroupBox(tr(""));
QPushButton* loadModelBtn = new QPushButton(tr("Load Model"));
QObject::connect(loadModelBtn, SIGNAL(clicked()), parent, SLOT(HandleLoadModel()));
QPushButton* saveModelBtn = new QPushButton(tr("Save Model"));
QObject::connect(saveModelBtn, SIGNAL(clicked()), parent, SLOT(HandleSaveModel()));
QPushButton* trainBtn = new QPushButton(tr("Train"));
QObject::connect(trainBtn, SIGNAL(clicked()), parent, SLOT(HandleTrain()));
QPushButton* searchBtn = new QPushButton(tr("Search"));
QObject::connect(searchBtn, SIGNAL(clicked()), parent, SLOT(HandleSearch()));
resultLabel_ = new QLabel(tr("Number of search results: 0"));
QGridLayout* gbLayout = new QGridLayout;
gbLayout->addWidget(trainBtn, 0, 0);
gbLayout->addWidget(loadModelBtn, 0, 1);
gbLayout->addWidget(searchBtn, 1, 0);
gbLayout->addWidget(saveModelBtn, 1, 1);
gbLayout->addWidget(resultLabel_, 2, 0, 1, -1);
groupBox->setLayout(gbLayout);
mainLayout->addWidget(trainImageView_, 0, 0, 1, -1);
mainLayout->addWidget(openImgBtn, 1, 0, 1, 1);
mainLayout->addWidget(grabCheckBox_, 1, 1, 1, 1);
mainLayout->addWidget(snapBtn, 1, 2, 1, 1);
mainLayout->addWidget(saveBtn, 2, 0, 1, 1);
mainLayout->addWidget(contSearchCheckBox_, 2, 1, 1, -1);
mainLayout->addWidget(groupBox, 3, 0, 1, -1);
setLayout(mainLayout);
}
void ControlWidget::EnableGrabCheckBox(bool value)
{
grabCheckBox_->setEnabled(value);
}
void ControlWidget::SetResultInfo(size_t numResults)
{
QString count = "Number of search results: " + QString::number(numResults);
resultLabel_->setText(count);
}
void ControlWidget::SetTrainView(
const Cvb::Image & image)
{
if (trainImageView_)
trainImageView_->Refresh(image);
}
CustomImageScene::CustomImageScene(QWidget* parent)
:
Cvb::UI::ImageScene(parent)
{}
void CustomImageScene::SetSearchResult(
const Cvb::Image & image, std::vector<Cvb::ShapeFinder2::SearchResult> result)
{
ClearScene();
searchResult_ = result;
for (int i = 0; i < searchResult_.size(); i++)
{
QGraphicsRectItem* item = new QGraphicsRectItem(QRectF(point.X(), point.Y(), 5, 5));
item->setBrush(QBrush(Qt::red));
this->addItem(item);
QString label =
"Q: " + QString::number(res.
Quality(),
'f', 2) +
", A: " + QString::number(res.
Rotation().
Deg(),
'f', 2);
QGraphicsRectItem* textLabel = new QGraphicsRectItem(QRectF(point.X() + 5, point.Y() + 5, 100, 15));
textLabel->setBrush(QBrush(Qt::red));
this->addItem(textLabel);
QGraphicsTextItem* text = this->addText(label);
text->setPos(point.X() + 5, point.Y() + 2.5);
text->setDefaultTextColor(Qt::black);
}
}
void CustomImageScene::ClearScene()
{
this->clear();
this->update();
}
void CustomImageScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
ClearScene();
this->ImageView()->setDragMode(QGraphicsView::RubberBandDrag);
pointStart_ = event->scenePos();
}
void CustomImageScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
pointEnd_ = event->scenePos();
this->addRect(QRectF(pointStart_, pointEnd_), QPen(Qt::blue, 0));
emit SelectionDone(QRectF(pointStart_, pointEnd_));
}
double Deg() const noexcept
static std::unique_ptr< Classifier > Create(const String &fileName)
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
static std::unique_ptr< Image > Load(const String &fileName)
T Bottom() const noexcept
Size2D< T > Size() const noexcept
Point2D< T > Location() const noexcept
std::unique_ptr< Classifier > Learn(const ImagePlane &plane, Point2D< int > position, Rect< int > teachWindow, const std::vector< Point2D< int > > &dontCarePoints) const
double Quality() const noexcept
Angle Rotation() const noexcept
double Y() const noexcept
double X() const noexcept
Cvb::String QtToCvb(const QString text) noexcept
QString CvbToQt(const Cvb::String &text) noexcept
String ExpandPath(const String &path)
std::shared_ptr< Image > ImagePtr