32 #include <VisionX/interface/components/Calibration.h>
36 #include <Calibration/StereoCalibration.h>
37 #include <DataStructures/DynamicArray.h>
38 #include <Image/ImageProcessor.h>
39 #include <Image/PrimitivesDrawer.h>
46 qRegisterMetaType<CByteImage**>(
"CByteImage**");
48 QPointer<QWidget> widgetPointer = getWidget();
49 this->widgetPointer = widgetPointer.data();
51 widget.setupUi(widgetPointer);
55 proxyFinder->setSearchMask(
"*Provider|*Result");
57 widget.chooseProviderLayout->addWidget(proxyFinder);
60 QSizePolicy sizePoli(QSizePolicy::Expanding, QSizePolicy::Expanding);
61 sizePoli.setVerticalStretch(15);
62 imageViewer->setSizePolicy(sizePoli);
64 widget.imageViewerLayout_2->addWidget(imageViewer);
85 proxyFinder->setIceManager(this->getIceManager(),
86 proxyFinder->getSelectedProxyName().isEmpty());
88 points = Eigen::ArrayX2f::Zero(4, 2);
89 numSelectedPoints = 0;
93 SIGNAL(selected(
int,
float,
float)),
95 SLOT(addPolygonPoint(
int,
float,
float)));
96 connect(widget.connectButton, SIGNAL(clicked(
bool)),
this, SLOT(connectButtonClicked(
bool)));
97 connect(widget.loadFeaturesButton,
98 SIGNAL(clicked(
bool)),
100 SLOT(loadFeaturesButtonClicked(
bool)));
101 connect(widget.saveFeaturesButton,
102 SIGNAL(clicked(
bool)),
104 SLOT(saveFeaturesButtonClicked(
bool)));
106 widget.pausePlayButton, SIGNAL(clicked(
bool)),
this, SLOT(pausePlayButtonClicked(
bool)));
108 widget.disconnectButton, SIGNAL(clicked(
bool)),
this, SLOT(disconnectButtonClicked(
bool)));
109 connect(widget.addFeaturesButton,
110 SIGNAL(clicked(
bool)),
112 SLOT(addFeaturesButtonClicked(
bool)));
113 connect(widget.clearFeaturesButton,
114 SIGNAL(clicked(
bool)),
116 SLOT(clearFeaturesButtonClicked(
bool)));
117 connect(widget.thresholdSpinBox,
118 SIGNAL(valueChanged(
double)),
120 SLOT(thresholdChanged(
double)));
122 widget.maxFeatureSpinBox, SIGNAL(valueChanged(
int)),
this, SLOT(maxFeaturesChanged(
int)));
132 objectFeatureSet =
new CTexturedFeatureSet(
"objectFeatureSet");
133 viewFeatureSet =
new CTexturedFeatureSet(
"viewFeatureSet");
135 featureCalculator =
new CHarrisSIFTFeatureCalculator(0.001, 1, 3500);
141 FeatureLearningWidgetController::addFeatures()
144 for (
int i = 0; i < viewFeatureSet->GetSize(); i++)
146 CFeatureEntry* copiedEntry = viewFeatureSet->GetFeature(i)->Clone();
147 Math2d::ApplyHomography(homography, copiedEntry->point, copiedEntry->point);
148 bool addedSuccessfully = objectFeatureSet->AddFeature(copiedEntry,
false);
149 if (addedSuccessfully)
158 FeatureLearningWidgetController::clearFeatures()
160 objectFeatureSet->Clear();
165 FeatureLearningWidgetController::loadFeatureSet(QString filePath)
167 std::string filePathStr = filePath.toStdString();
168 const char* filePathCStr = filePathStr.c_str();
169 if (!objectFeatureSet->LoadFromFile(filePathCStr))
171 ARMARX_ERROR <<
" could not load feature set from file " << filePath;
172 widget.lastActionInfoLabel->setText(
"Loading features failed!");
176 ARMARX_INFO <<
"loaded feature set with " << objectFeatureSet->GetSize() <<
" features";
179 mySettings.setValue(
"default_load_dir", currentDir.absoluteFilePath(filePath));
181 widget.lastActionInfoLabel->setText(
"Loaded feature set with " +
182 QString::number(objectFeatureSet->GetSize()) +
185 float x = fabsf(2.f * objectFeatureSet->m_3dPoint2.x);
186 float y = fabsf(2.f * objectFeatureSet->m_3dPoint2.y);
187 float z = fabsf(2.f * objectFeatureSet->m_3dPoint2.z);
193 widget.widthLineEdit->setText(QString::number(xInt));
194 widget.heightLineEdit->setText(QString::number(yInt));
195 widget.depthLineEdit->setText(QString::number(zInt));
198 points = Eigen::ArrayX2f::Zero(4, 2);
199 numSelectedPoints = 0;
204 FeatureLearningWidgetController::saveFeatureSet(QString filePath,
float w,
float h,
float d)
211 Math3d::SetVec(objectFeatureSet->m_3dPoint1, -w / 2.0f, -h / 2.0f, -d / 2.0f);
212 Math3d::SetVec(objectFeatureSet->m_3dPoint2, w / 2.0f, -h / 2.0f, -d / 2.0f);
213 Math3d::SetVec(objectFeatureSet->m_3dPoint3, w / 2.0f, h / 2.0f, -d / 2.0f);
214 Math3d::SetVec(objectFeatureSet->m_3dPoint4, -w / 2.0f, h / 2.0f, -d / 2.0f);
216 std::string filePathStr = filePath.toStdString();
217 const char* filePathCStr = filePathStr.c_str();
220 if (!objectFeatureSet->SaveToFile(filePathCStr))
222 ARMARX_ERROR <<
" could not save feature set to file " << filePath;
223 widget.lastActionInfoLabel->setText(
"Saving features failed!");
227 ARMARX_INFO <<
" saved feature set with " << objectFeatureSet->GetSize()
228 <<
" features to file " << filePathCStr;
231 std::string matPath = filePath.toStdString();
232 const unsigned int n = matPath.find_last_of(
".dat");
233 matPath.resize(n - 2);
236 CFloatMatrix*
object = createCuboid(w, h, d);
237 object->SaveToFile(matPath.c_str());
241 mySettings.setValue(
"default_save_dir", currentDir.absoluteFilePath(filePath));
242 widget.lastActionInfoLabel->setText(
243 "Saved " + QString::number(objectFeatureSet->GetSize()) +
" features into file");
255 points = Eigen::ArrayX2f::Zero(4, 2);
259 for (
int i = 0; i < numImages; i++)
261 delete cameraImages[i];
264 delete[] cameraImages;
266 delete visualizationImage;
270 delete featureCalculator;
272 delete objectFeatureSet;
273 delete viewFeatureSet;
281 double ba0 = b.x -
a.x;
282 double ba1 = b.y -
a.y;
284 double p1a0 = p1.x -
a.x;
285 double p1a1 = p1.y -
a.y;
287 double p2a0 = p2.x -
a.x;
288 double p2a1 = p2.y -
a.y;
290 double cp1 = ba0 * p1a1 - ba1 * p1a0;
291 double cp2 = ba0 * p2a1 - ba1 * p2a0;
293 return cp1 * cp2 >= 0;
299 return SameSide(p,
a, b,
c) && SameSide(p, b,
a,
c) && SameSide(p,
c,
a, b);
305 Vec2d n = {b.y -
a.y,
a.x - b.x};
306 Math2d::NormalizeVec(n);
308 return std::fabs(Math2d::ScalarProduct(n, p) - Math2d::ScalarProduct(n,
a));
313 FeatureLearningWidgetController::updateFeatures()
315 if (numSelectedPoints == 0)
317 viewFeatureSet->Clear();
322 if (numSelectedPoints < 4)
327 Vec2d sourcePoints[4];
329 for (
int i = 0; i < numSelectedPoints; i++)
331 sourcePoints[i].x = points(i, 0) * grayImage->width;
332 sourcePoints[i].y = points(i, 1) * grayImage->height;
335 const float w = widget.widthLineEdit->text().toFloat();
336 const float h = widget.heightLineEdit->text().toFloat();
338 Vec2d targetPoints[4];
339 Math2d::SetVec(targetPoints[0], 0, 0);
340 Math2d::SetVec(targetPoints[1], w, 0);
341 Math2d::SetVec(targetPoints[2], w, h);
342 Math2d::SetVec(targetPoints[3], 0, h);
344 LinearAlgebra::DetermineHomography(sourcePoints, targetPoints, 4, homography,
true);
346 objectFeatureSet->SetCornerPoints(
347 targetPoints[0], targetPoints[1], targetPoints[2], targetPoints[3]);
349 featureCalculatorMutex.lock();
350 CDynamicArray dynamic_array(featureCalculator->GetMaxInterestPoints());
351 const int nFeatures = featureCalculator->CalculateFeatures(grayImage, &dynamic_array);
352 featureCalculatorMutex.unlock();
353 viewFeatureSet->Clear();
355 for (
int i = 0; i < nFeatures; i++)
357 CFeatureEntry* pFeatureEntry = (CFeatureEntry*)dynamic_array.GetElement(i);
359 if (PointInPoly(pFeatureEntry->point, sourcePoints[0], sourcePoints[1], sourcePoints[2]) ||
360 PointInPoly(pFeatureEntry->point, sourcePoints[2], sourcePoints[3], sourcePoints[0]))
362 const double dThreshold = 10;
364 if (DistancePointStraightLine(sourcePoints[0], sourcePoints[1], pFeatureEntry->point) >
366 DistancePointStraightLine(sourcePoints[1], sourcePoints[2], pFeatureEntry->point) >
368 DistancePointStraightLine(sourcePoints[2], sourcePoints[3], pFeatureEntry->point) >
370 DistancePointStraightLine(sourcePoints[3], sourcePoints[0], pFeatureEntry->point) >
373 viewFeatureSet->AddFeature(pFeatureEntry->Clone(),
false);
382 FeatureLearningWidgetController::updateSelection()
384 Vec2d sourcePoints[4];
389 for (
int i = 0; i < numSelectedPoints; i++)
391 float relativeX = points(i, 0);
392 float relativeY = points(i, 1);
394 sourcePoints[i].x = relativeX * visualizationImage->width;
395 sourcePoints[i].y = relativeY * visualizationImage->height;
400 if (numSelectedPoints >= 1)
402 PrimitivesDrawer::DrawCircle(visualizationImage, sourcePoints[0], 3, 255, 0, 0, -1);
405 if (numSelectedPoints >= 2)
407 PrimitivesDrawer::DrawCircle(visualizationImage, sourcePoints[1], 3, 255, 0, 0, -1);
408 PrimitivesDrawer::DrawLine(visualizationImage, sourcePoints[0], sourcePoints[1], 0, 0, 255);
411 if (numSelectedPoints >= 3)
413 PrimitivesDrawer::DrawCircle(visualizationImage, sourcePoints[2], 3, 255, 0, 0, -1);
414 PrimitivesDrawer::DrawLine(visualizationImage, sourcePoints[1], sourcePoints[2], 0, 0, 255);
417 if (numSelectedPoints >= 4)
419 PrimitivesDrawer::DrawCircle(visualizationImage, sourcePoints[3], 3, 255, 0, 0, -1);
420 PrimitivesDrawer::DrawLine(visualizationImage, sourcePoints[2], sourcePoints[3], 0, 0, 255);
421 PrimitivesDrawer::DrawLine(visualizationImage, sourcePoints[3], sourcePoints[0], 0, 0, 255);
427 if (numSelectedPoints >= 4)
429 for (
int i = 0; i < viewFeatureSet->GetSize(); i++)
431 const CFeatureEntry* pFeatureEntry = viewFeatureSet->GetFeature(i);
432 PrimitivesDrawer::DrawCircle(
433 visualizationImage, pFeatureEntry->point, 2, 0, 255, 0, -1);
443 std::unique_lock lock(imageMutex);
445 if (!lock.owns_lock())
458 armarx::MetaInfoSizeBasePtr info;
463 if (!waitForImages(imageProviderName))
468 if (getImages(imageProviderName, cameraImages, info) != numImages)
477 ::ImageProcessor::ConvertImage(cameraImages[0], grayImage);
478 ::ImageProcessor::CopyImage(cameraImages[0], visualizationImage);
487 CByteImage** images =
new CByteImage* {visualizationImage};
488 imageViewer->setImages(
489 1, images, IceUtil::Time::microSeconds(info->timeProvided), timeReceived);
500 if (numSelectedPoints >= 4)
502 numSelectedPoints = 0;
505 points(numSelectedPoints, 0) = x;
506 points(numSelectedPoints, 1) = y;
521 QString previousLoadDirectory = mySettings.value(
"default_load_dir").toString();
522 if (previousLoadDirectory ==
"")
524 QString previousSaveDirectory = mySettings.value(
"default_save_dir").toString();
525 if (previousSaveDirectory ==
"")
527 previousLoadDirectory =
"~";
531 previousLoadDirectory = previousSaveDirectory;
534 QString filePath = QFileDialog::getOpenFileName(
535 widgetPointer,
"Load Features", previousLoadDirectory,
"(*.dat)");
536 loadFeatureSet(filePath);
543 if (objectFeatureSet->GetSize() < 1)
545 QMessageBox emptyFeatureSetBox;
546 emptyFeatureSetBox.setText(
"Cannot save empty feature set!");
547 emptyFeatureSetBox.setWindowTitle(
"Feature set empty");
548 emptyFeatureSetBox.exec();
551 float width = widget.widthLineEdit->text().toFloat();
552 float height = widget.heightLineEdit->text().toFloat();
553 float depth = widget.depthLineEdit->text().toFloat();
555 if (width <= 0.f || height <= 0.f || depth <= 0.f)
557 QMessageBox invalidDimensionsBox;
558 invalidDimensionsBox.setText(
"Cannot save feature set with object dimensions <= 0!");
559 invalidDimensionsBox.setWindowTitle(
"Invalid object dimensions");
560 invalidDimensionsBox.exec();
564 QString previousSaveDirectory = mySettings.value(
"default_save_dir").toString();
565 if (previousSaveDirectory ==
"")
567 QString previousLoadDirectory = mySettings.value(
"default_load_dir").toString();
568 if (previousLoadDirectory ==
"")
570 previousSaveDirectory =
"~";
574 previousSaveDirectory = previousLoadDirectory;
577 QString filePath = QFileDialog::getSaveFileName(
578 widgetPointer,
"Save Features", previousSaveDirectory,
"(*.dat)");
579 if (filePath.right(4) !=
".dat")
583 saveFeatureSet(filePath, width, height, depth);
595 isPaused = !isPaused;
597 updateImageMonitorUI();
604 std::unique_lock lock(imageMutex);
606 disconnectFromProvider();
621 FeatureLearningWidgetController::connectToProvider()
629 imageProviderName = proxyFinder->getSelectedProxyName().toStdString();
630 ARMARX_INFO <<
"Trying to connect to provider " << imageProviderName;
634 usingImageProvider(imageProviderName);
636 catch (Ice::NotRegisteredException& e)
638 ARMARX_ERROR <<
"Cannot connect to the given image provider called " << imageProviderName;
640 QMessageBox cannotConnectToProviderBox;
641 QString cannotConnectToProviderString =
642 "Cannot connect to the given image provider called " +
643 QString::fromStdString(imageProviderName);
644 cannotConnectToProviderBox.setText(cannotConnectToProviderString);
645 cannotConnectToProviderBox.setWindowTitle(
"Cannot connect to Image Provider");
646 cannotConnectToProviderBox.exec();
649 catch (armarx::LocalException& e)
651 ARMARX_ERROR <<
"Image Provider field is empty, cannot connect!" << imageProviderName;
653 QMessageBox noProviderGivenBox;
654 QString cannotConnectToProviderString =
655 "Cannot connect to an image provider since the field is empty.";
656 noProviderGivenBox.setText(cannotConnectToProviderString);
657 noProviderGivenBox.setWindowTitle(
"No Provider given");
658 noProviderGivenBox.exec();
662 ARMARX_INFO << getName() <<
" connecting to " << imageProviderName;
669 imageProviderPrx = imageProviderInfo.proxy;
670 numImages = imageProviderInfo.numberImages;
672 StereoCalibrationProviderInterfacePrx calibrationProvider =
673 StereoCalibrationProviderInterfacePrx::checkedCast(imageProviderInfo.proxy);
675 if (!calibrationProvider)
677 ARMARX_WARNING <<
"image provider does not have a stereo calibration interface";
681 undistortImages = calibrationProvider->getImagesAreUndistorted();
682 if (!undistortImages)
684 ARMARX_ERROR <<
"Images expected to be undistorted, but the calibration returns that "
685 "the images are distorted";
694 std::unique_lock lock(imageMutex);
695 cameraImages =
new CByteImage*[numImages];
697 for (
int i = 0; i < numImages; i++)
704 grayImage =
new CByteImage(imageProviderInfo.imageFormat.dimension.width,
705 imageProviderInfo.imageFormat.dimension.height,
706 CByteImage::eGrayScale);
709 timeProvided = IceUtil::Time::seconds(0);
713 updateImageMonitorUI();
717 emit imageProviderConnected(
true);
722 FeatureLearningWidgetController::disconnectFromProvider()
730 std::unique_lock lock(imageMutex);
734 for (
int i = 0; i < numImages; i++)
736 delete cameraImages[i];
739 delete[] cameraImages;
741 delete visualizationImage;
745 releaseImageProvider(imageProviderName);
747 imageProviderPrx = 0;
751 numSelectedPoints = 0;
753 QString emptyFeaturesInfoText =
"";
754 widget.lastActionInfoLabel->setText(emptyFeaturesInfoText);
756 updateImageMonitorUI();
759 emit imageProviderConnected(
false);
763 FeatureLearningWidgetController::updatePausePlayButtonText()
767 widget.pausePlayButton->setText(
"Play");
770 widget.pausePlayButton->setText(
"Pause");
774 FeatureLearningWidgetController::updateImageMonitorUI()
776 updatePausePlayButtonText();
778 widget.disconnectButton->setEnabled(connected);
779 widget.pausePlayButton->setEnabled(connected);
783 QString providerName = imageProviderName.c_str();
784 QString statusString =
"Status: connected to image provider " + providerName;
787 statusString = statusString +
" - PAUSED";
789 widget.imageMonitorStatusLabel->setText(statusString);
793 widget.imageMonitorStatusLabel->setText(
"Status: not connected");
800 disconnectFromProvider();
806 int numFeaturesAdded = addFeatures();
807 QString featuresString =
" features";
808 if (numFeaturesAdded == 1)
810 featuresString =
" feature";
812 QString addedFeaturesText =
813 "Added " + QString::number(numFeaturesAdded) + featuresString +
" to the selection";
814 widget.lastActionInfoLabel->setText(addedFeaturesText);
822 QString clearedFeaturesText =
"Cleared the selected features";
823 widget.lastActionInfoLabel->setText(clearedFeaturesText);
829 FeatureLearningWidgetController::updateFeaturesUI()
833 widget.addFeaturesButton->setEnabled(
false);
834 widget.clearFeaturesButton->setEnabled(
false);
835 widget.featuresInRegionLabel->setText(
"");
836 widget.featuresSelectedLabel->setText(
"");
839 widget.addFeaturesButton->setEnabled(
true);
840 widget.clearFeaturesButton->setEnabled(
true);
841 QString featuresInRegionString =
"Features in Region: ";
842 if (numSelectedPoints == 4)
844 featuresInRegionString += QString::number(viewFeatureSet->GetSize());
846 widget.featuresInRegionLabel->setText(featuresInRegionString);
848 QString featuresSelectedString =
849 "Features selected: " + QString::number(objectFeatureSet->GetSize());
850 widget.featuresSelectedLabel->setText(featuresSelectedString);
854 FeatureLearningWidgetController::createCuboid(
float w,
float h,
float d)
856 CFloatMatrix*
object =
new CFloatMatrix(6, 36);
859 addRectangle(
object, offset, 0, 0, 0, w, 0, 0, w, h, 0, 0, h, 0, 0, 0, -1);
860 addRectangle(
object, offset, 0, 0, d, w, 0, d, w, h, d, 0, h, d, 0, 0, 1);
861 addRectangle(
object, offset, 0, 0, 0, 0, 0, d, 0, h, d, 0, h, 0, -1, 0, 0);
862 addRectangle(
object, offset, w, 0, 0, w, 0, d, w, h, d, w, h, 0, 1, 0, 0);
863 addRectangle(
object, offset, 0, 0, 0, 0, 0, d, w, 0, d, w, 0, 0, 0, -1, 0);
864 addRectangle(
object, offset, 0, h, 0, 0, h, d, w, h, d, w, h, 0, 0, 1, 0);
866 for (
int i = 0; i < 36; i++)
868 const int offset = 6 * i;
869 object->data[offset + 3] -= w / 2.0;
870 object->data[offset + 4] -= h / 2.0;
871 object->data[offset + 5] -= d / 2.0;
878 FeatureLearningWidgetController::addRectangle(CFloatMatrix*
object,
896 addTriangle(
object, offset, x1, y1, z1, x2, y2, z2, x3, y3, z3, n1, n2, n3);
897 addTriangle(
object, offset, x4, y4, z4, x1, y1, z1, x3, y3, z3, n1, n2, n3);
901 FeatureLearningWidgetController::addTriangle(CFloatMatrix*
object,
916 float*
data =
object->data;
918 data[offset + 0] = n1;
919 data[offset + 1] = n2;
920 data[offset + 2] = n3;
921 data[offset + 3] = x1;
922 data[offset + 4] = y1;
923 data[offset + 5] = z1;
926 data[offset + 0] = n1;
927 data[offset + 1] = n2;
928 data[offset + 2] = n3;
929 data[offset + 3] = x2;
930 data[offset + 4] = y2;
931 data[offset + 5] = z2;
934 data[offset + 0] = n1;
935 data[offset + 1] = n2;
936 data[offset + 2] = n3;
937 data[offset + 3] = x3;
938 data[offset + 4] = y3;
939 data[offset + 5] = z3;
946 std::lock_guard g{featureCalculatorMutex};
947 featureCalculator->SetThreshold(threshold);
953 std::lock_guard g{featureCalculatorMutex};
954 featureCalculator->SetMaxInterestPoints(maxFeatures);