33 #include <QApplication>
36 #include <Image/ImageProcessor.h>
39 #include <SimoxUtility/algorithm/string.h>
72 std::string getHumanReadableCameraID(
unsigned int index,
unsigned int index_count,
int depth_index);
78 std::string getDateString();
84 ImageMonitorProperties::ImageMonitorProperties()
91 if (
const char* home = std::getenv(
"HOME"))
94 outputDirectory = std::filesystem::path(home) /
"Pictures";
98 outputDirectory = std::filesystem::absolute(
".");
102 imageBufferSize = 100;
104 controlsHidden =
false;
105 depthImageIndex = -1;
107 compressionType = eNoCompression;
108 compressionQuality = 9;
112 ImageMonitorWidgetController::ImageMonitorWidgetController() :
128 return qobject_cast<QWidget*>(widget);
137 std::unique_lock lock(imageMutex);
141 writeImageBuffer =
false;
142 lastBufferTime = IceUtil::Time::now();
143 imageBuffer.resize(10);
147 connect(
this, SIGNAL(
statisticsUpdated(
const QString&)), widget, SLOT(updateStatistics(
const QString&)));
161 disconnectFromProvider();
180 std::unique_lock lock(imageMutex);
181 unsigned int retrievedImages = 0;
182 const bool sourceFps = this->properties.
frameRate < 0.0f;
192 armarx::MetaInfoSizeBasePtr info;
193 retrievedImages =
static_cast<unsigned int>(this->
getImages(this->properties.
providerName, this->images, info));
194 timeReceived = TimeUtil::GetTime();
197 this->timeProvided = IceUtil::Time::microSeconds(info->timeProvided);
200 std::vector<CByteImage*> deleteList;
201 std::vector<CByteImage*> selectedImages;
203 for (
unsigned int i = 0; i < static_cast<unsigned int>(retrievedImages) && i < this->numberImages; ++i)
205 CByteImage* currentImage;
209 currentImage =
new CByteImage(this->images[i]);
210 ::ImageProcessor::CopyImage(this->images[i], currentImage);
211 deleteList.push_back(currentImage);
215 currentImage = this->images[i];
218 if (this->properties.
imagesToShow.size() == 0 or (this->properties.
imagesToShow.size() > 0 and this->properties.imagesToShow.count(i) > 0))
225 selectedImages.push_back(currentImage);
229 if (selectedImages.size() > 0)
231 this->widget->drawImages(
static_cast<int>(selectedImages.size()), &selectedImages[0], this->timeProvided, timeReceived);
234 for (CByteImage* image : deleteList)
245 if (writeImageBuffer && retrievedImages > 0)
249 imageBuffer.resize(
static_cast<unsigned int>(properties.
imageBufferSize));
252 IceUtil::Time timePassed = IceUtil::Time::now() - lastBufferTime;
254 if (timePassed.toMilliSeconds() > 1000.f / properties.
bufferFps)
258 for (
unsigned int i = 0; i < retrievedImages; i++)
260 newImages.push_back(
CByteImagePtr(
new CByteImage(images[i])));
261 ::ImageProcessor::CopyImage(images[i], newImages[i].get());
264 imageBuffer.push_front(newImages);
265 lastBufferTime = IceUtil::Time::now();
269 if (this->recordingTask && this->writeRecordingBuffer)
271 std::vector<CByteImage*> tmpImages(this->numberImages);
272 for (
unsigned int i = 0; i < this->numberImages; ++i)
274 tmpImages.at(i) =
new CByteImage(this->images[i]);
275 ::ImageProcessor::CopyImage(this->images[i], tmpImages[i]);
278 std::unique_lock lock2(this->recordingBufferMutex);
279 this->recordingBuffer.push(tmpImages);
285 properties.
providerName = settings->value(
"providerName",
"").toString().toStdString();
286 properties.
frameRate = settings->value(
"frameRate", -1).toInt();
287 properties.
outputDirectory = settings->value(
"outputPath",
".").toString().toStdString();
288 properties.
imageBufferSize = settings->value(
"imageBufferSize", 99).toInt();
289 properties.
bufferFps = settings->value(
"bufferFps", 5.0f).toFloat();
290 auto imagesToShowStringList = settings->value(
"imagesToShow", 0).toStringList();
292 for (QString&
s : imagesToShowStringList)
296 properties.
controlsHidden = settings->value(
"controlsHidden",
false).toBool();
297 properties.
depthImageIndex = settings->value(
"depthImageIndex", -1).toInt();
298 properties.
maxDepthmm = settings->value(
"maxDepthmm", 5000).toInt();
299 properties.
recordDepthRaw = settings->value(
"recordDepthRaw",
true).toBool();
300 properties.
recordingMethods = settings->value(
"recordingMethods", 0).toStringList();
301 properties.
compressionType =
static_cast<CompressionType
>(settings->value(
"compressionType", 0).toInt());
309 settings->setValue(
"providerName", QString(properties.
providerName.c_str()));
310 settings->setValue(
"frameRate", properties.
frameRate);
311 settings->setValue(
"outputPath", QString(properties.
outputDirectory.c_str()));
313 settings->setValue(
"bufferFps", QString::number(
static_cast<double>(properties.
bufferFps)));
317 l << QString::number(number);
319 settings->setValue(
"imagesToShow", l);
322 settings->setValue(
"maxDepthmm", properties.
maxDepthmm);
326 settings->setValue(
"compressionType", (
int)properties.
compressionType);
337 std::unique_lock lock(imageMutex);
339 disconnectFromProvider();
342 this->properties = properties;
367 int posInBuffer =
static_cast<int>(imageBuffer.size()) - 1 -
static_cast<int>(position);
374 if (
static_cast<unsigned int>(posInBuffer) >= imageBuffer.size())
376 posInBuffer =
static_cast<int>(imageBuffer.size()) - 1;
379 if (imageBuffer.size() == 0)
381 throw LocalException(
"ImageBuffer size is 0");
384 realPosition =
static_cast<unsigned int>(posInBuffer);
385 return imageBuffer[
static_cast<unsigned int>(posInBuffer)];
391 void ImageMonitorWidgetController::connectToProvider()
407 imageProviderPrx = imageProviderInfo.
proxy;
408 numberImages =
static_cast<unsigned int>(imageProviderInfo.
numberImages);
412 std::unique_lock lock(imageMutex);
413 images =
new CByteImage*[numberImages];
415 for (
unsigned int i = 0 ; i < numberImages ; i++)
421 timeProvided = IceUtil::Time::seconds(0);
430 void ImageMonitorWidgetController::disconnectFromProvider()
437 std::unique_lock lock(imageMutex);
441 for (
unsigned int i = 0 ; i < numberImages ; i++)
451 imageProviderPrx =
nullptr;
460 namespace fs = std::filesystem;
462 return absPath.string();
467 std::unique_lock lock(this->imageMutex);
470 std::string baseFilename =
"snapshot_" + getDateString();
472 for (
unsigned int i = 0; i < this->numberImages; ++i)
474 std::filesystem::path
filename = baseFilename + getHumanReadableCameraID(i, this->numberImages, this->properties.
depthImageIndex);
481 if (this->recordingTask && this->recordingTask->isRunning())
487 const std::string baseFilename =
"recording_" + getDateString();
489 const double fps = [
this]() ->
double
495 return static_cast<unsigned int>(source_fps);
498 return static_cast<double>(this->properties.
frameRate);
501 for (
unsigned int i = 0; i < this->numberImages; ++i)
504 const std::string ext = this->properties.
recordingMethods.at(
static_cast<int>(i)).toStdString();
505 const std::filesystem::path
filename = baseFilename + getHumanReadableCameraID(i, this->numberImages, this->properties.
depthImageIndex) + ext;
506 const std::filesystem::path fullFilePath = path /
filename;
510 this->recorders.push_back(rec);
516 this->writeRecordingBuffer =
true;
517 this->recordingTask->start();
524 this->writeRecordingBuffer =
false;
525 if (!this->recordingBuffer.empty())
527 ARMARX_IMPORTANT <<
"Stopping recording... Still " << this->recordingBuffer.size() <<
" images in the queue to write";
533 if (this->recordingTask)
535 this->recordingTask->stop();
544 rec->stopRecording();
547 this->recorders.clear();
550 void ImageMonitorWidgetController::recordFrame()
552 std::vector<CByteImage*> images;
554 std::unique_lock lock(this->recordingBufferMutex);
557 if (!this->writeRecordingBuffer)
559 if (this->recordingBuffer.empty())
565 else if (this->recordingBuffer.size() % 5 == 0)
567 ARMARX_INFO <<
"Stopping recording... Still " << this->recordingBuffer.size() <<
" images in the queue to write";
570 else if (this->recordingBuffer.size() > 0 and this->recordingBuffer.size() % 5 == 0)
572 ARMARX_INFO <<
deactivateSpam(1) <<
"Recording buffer queue size currently at " << this->recordingBuffer.size() <<
" images";
576 if (!this->connected or this->recordingBuffer.empty())
581 images = this->recordingBuffer.front();
582 this->recordingBuffer.pop();
587 for (
unsigned int i = 0; i < this->numberImages; ++i)
589 if (this->recorders[i])
591 this->recorders[i]->recordFrame(*images[i], std::chrono::microseconds{0});
599 void ImageMonitorWidgetController::updateStatistics()
601 std::unique_lock lock(imageMutex);
604 std::stringstream ss;
609 IceUtil::Time displayDelay = widget->getImageViewer()->getDisplayDelay();
610 displayDelayFilter.
update(timeReceived, displayDelay.toMilliSecondsDouble());
615 if (timeProvided.toMilliSeconds())
617 ss <<
" - image age: " << (timeReceived - timeProvided).toMilliSeconds() <<
" ms - display delay: " << IceUtil::Time::milliSecondsDouble(displayDelayFilter.
getCurrentValue()).toMilliSecondsDouble() <<
" ms";
622 ss <<
"display paused";
625 std::string statisticsStr = ss.str();
629 void ImageMonitorWidgetController::hideControls(
bool hide)
631 widget->hideControlWidgets(hide);
639 if (parent != customToolbar->parent())
641 customToolbar->setParent(parent);
644 return customToolbar.data();
647 customToolbar =
new QToolBar(parent);
648 customToolbar->setIconSize(QSize(16, 16));
650 viewingModeAction = customToolbar->addAction(QIcon(
":icons/object-locked-2.ico"),
"Hide controls");
651 viewingModeAction->setCheckable(
true);
652 connect(viewingModeAction, SIGNAL(toggled(
bool)),
this, SLOT(hideControls(
bool)));
655 return customToolbar.data();
661 int pixelCount = depthImage->width * depthImage->height;
662 auto& depthToRgbLookUpTable = depthToRgbLookUpTables[maxDistance];
663 size_t depthValueCount = 256 * 256;
664 if (depthToRgbLookUpTable.size() != depthValueCount)
666 ARMARX_VERBOSE <<
"Resizing lookup table for max distance " << maxDistance;
667 depthToRgbLookUpTable.resize(depthValueCount);
670 for (
int i = 0; i < pixelCount; ++i)
672 int z_value =
visionx::tools::rgbToDepthValue(depthImage->pixels[pixelPos + 0], depthImage->pixels[pixelPos + 1], depthImage->pixels[pixelPos + 2],
false);
676 if (z_value <
static_cast<signed int>(depthToRgbLookUpTable.size()))
678 std::optional<armarx::DrawColor24Bit>& rgbOptional = depthToRgbLookUpTable.at(z_value);
695 depthImage->pixels[pixelPos] = rgb.r;
696 depthImage->pixels[pixelPos + 1] = rgb.g;
697 depthImage->pixels[pixelPos + 2] = rgb.b;
701 depthImage->pixels[pixelPos] = 0;
702 depthImage->pixels[pixelPos + 1] = 0;
703 depthImage->pixels[pixelPos + 2] = 0;
715 std::string getHumanReadableCameraID(
unsigned int index,
unsigned int index_count,
int depth_index)
717 if (index_count >= 2)
719 if (depth_index >= 0)
721 if (
index ==
static_cast<unsigned int>(depth_index))
725 if (index_count == 2 and
index !=
static_cast<unsigned int>(depth_index))
730 else if (index_count == 2)
748 std::string getDateString()
751 std::string date_str = recordStartTime.toDateTime();
753 date_str = simox::alg::replace_all(date_str,
"/",
"-");
754 date_str = simox::alg::replace_all(date_str,
" ",
"_");
755 date_str = simox::alg::replace_all(date_str,
":",
"-");