33 #include <QApplication>
36 #include <Image/ImageProcessor.h>
39 #include <SimoxUtility/algorithm/string.h>
72 getHumanReadableCameraID(
unsigned int index,
unsigned int index_count,
int depth_index);
78 std::string getDateString();
83 ImageMonitorProperties::ImageMonitorProperties()
90 if (
const char* home = std::getenv(
"HOME"))
93 outputDirectory = std::filesystem::path(home) /
"Pictures";
97 outputDirectory = std::filesystem::absolute(
".");
101 imageBufferSize = 100;
103 controlsHidden =
false;
104 depthImageIndex = -1;
106 compressionType = eNoCompression;
107 compressionQuality = 9;
110 ImageMonitorWidgetController::ImageMonitorWidgetController() :
127 return qobject_cast<QWidget*>(widget);
137 std::unique_lock lock(imageMutex);
141 writeImageBuffer =
false;
142 lastBufferTime = IceUtil::Time::now();
143 imageBuffer.resize(10);
150 SLOT(updateStatistics(
const QString&)));
166 disconnectFromProvider();
186 std::unique_lock lock(imageMutex);
187 unsigned int retrievedImages = 0;
188 const bool sourceFps = this->properties.
frameRate < 0.0f;
198 armarx::MetaInfoSizeBasePtr info;
199 retrievedImages =
static_cast<unsigned int>(
201 timeReceived = TimeUtil::GetTime();
204 this->timeProvided = IceUtil::Time::microSeconds(info->timeProvided);
207 std::vector<CByteImage*> deleteList;
208 std::vector<CByteImage*> selectedImages;
210 for (
unsigned int i = 0;
211 i < static_cast<unsigned int>(retrievedImages) && i < this->numberImages;
214 CByteImage* currentImage;
216 if (i ==
static_cast<unsigned int>(this->properties.
depthImageIndex) &&
219 currentImage =
new CByteImage(this->images[i]);
220 ::ImageProcessor::CopyImage(this->images[i], currentImage);
221 deleteList.push_back(currentImage);
225 currentImage = this->images[i];
230 this->properties.imagesToShow.count(i) > 0))
237 selectedImages.push_back(currentImage);
241 if (selectedImages.size() > 0)
243 this->widget->drawImages(
static_cast<int>(selectedImages.size()),
249 for (CByteImage* image : deleteList)
260 if (writeImageBuffer && retrievedImages > 0)
262 if ((imageBuffer.size()) !=
static_cast<unsigned int>(properties.
imageBufferSize) &&
265 imageBuffer.resize(
static_cast<unsigned int>(properties.
imageBufferSize));
268 IceUtil::Time timePassed = IceUtil::Time::now() - lastBufferTime;
270 if (timePassed.toMilliSeconds() > 1000.f / properties.
bufferFps)
274 for (
unsigned int i = 0; i < retrievedImages; i++)
276 newImages.push_back(
CByteImagePtr(
new CByteImage(images[i])));
277 ::ImageProcessor::CopyImage(images[i], newImages[i].get());
280 imageBuffer.push_front(newImages);
281 lastBufferTime = IceUtil::Time::now();
285 if (this->recordingTask && this->writeRecordingBuffer)
287 std::vector<CByteImage*> tmpImages(this->numberImages);
288 for (
unsigned int i = 0; i < this->numberImages; ++i)
290 tmpImages.at(i) =
new CByteImage(this->images[i]);
291 ::ImageProcessor::CopyImage(this->images[i], tmpImages[i]);
294 std::unique_lock lock2(this->recordingBufferMutex);
295 this->recordingBuffer.push(tmpImages);
302 properties.
providerName = settings->value(
"providerName",
"").toString().toStdString();
303 properties.
frameRate = settings->value(
"frameRate", -1).toInt();
304 properties.
outputDirectory = settings->value(
"outputPath",
".").toString().toStdString();
305 properties.
imageBufferSize = settings->value(
"imageBufferSize", 99).toInt();
306 properties.
bufferFps = settings->value(
"bufferFps", 5.0f).toFloat();
307 auto imagesToShowStringList = settings->value(
"imagesToShow", 0).toStringList();
309 for (QString&
s : imagesToShowStringList)
313 properties.
controlsHidden = settings->value(
"controlsHidden",
false).toBool();
314 properties.
depthImageIndex = settings->value(
"depthImageIndex", -1).toInt();
315 properties.
maxDepthmm = settings->value(
"maxDepthmm", 5000).toInt();
316 properties.
recordDepthRaw = settings->value(
"recordDepthRaw",
true).toBool();
317 properties.
recordingMethods = settings->value(
"recordingMethods", 0).toStringList();
319 static_cast<CompressionType
>(settings->value(
"compressionType", 0).toInt());
328 settings->setValue(
"providerName", QString(properties.
providerName.c_str()));
329 settings->setValue(
"frameRate", properties.
frameRate);
330 settings->setValue(
"outputPath", QString(properties.
outputDirectory.c_str()));
332 settings->setValue(
"bufferFps", QString::number(
static_cast<double>(properties.
bufferFps)));
336 l << QString::number(number);
338 settings->setValue(
"imagesToShow", l);
341 settings->setValue(
"maxDepthmm", properties.
maxDepthmm);
345 settings->setValue(
"compressionType", (
int)properties.
compressionType);
356 std::unique_lock lock(imageMutex);
358 disconnectFromProvider();
361 this->properties = properties;
388 unsigned int& realPosition)
390 int posInBuffer =
static_cast<int>(imageBuffer.size()) - 1 -
static_cast<int>(position);
397 if (
static_cast<unsigned int>(posInBuffer) >= imageBuffer.size())
399 posInBuffer =
static_cast<int>(imageBuffer.size()) - 1;
402 if (imageBuffer.size() == 0)
404 throw LocalException(
"ImageBuffer size is 0");
407 realPosition =
static_cast<unsigned int>(posInBuffer);
408 return imageBuffer[
static_cast<unsigned int>(posInBuffer)];
415 ImageMonitorWidgetController::connectToProvider()
432 imageProviderPrx = imageProviderInfo.
proxy;
433 numberImages =
static_cast<unsigned int>(imageProviderInfo.
numberImages);
437 std::unique_lock lock(imageMutex);
438 images =
new CByteImage*[numberImages];
440 for (
unsigned int i = 0; i < numberImages; i++)
446 timeProvided = IceUtil::Time::seconds(0);
456 ImageMonitorWidgetController::disconnectFromProvider()
463 std::unique_lock lock(imageMutex);
467 for (
unsigned int i = 0; i < numberImages; i++)
477 imageProviderPrx =
nullptr;
487 namespace fs = std::filesystem;
489 return absPath.string();
495 std::unique_lock lock(this->imageMutex);
498 std::string baseFilename =
"snapshot_" + getDateString();
500 for (
unsigned int i = 0; i < this->numberImages; ++i)
504 getHumanReadableCameraID(i, this->numberImages, this->properties.
depthImageIndex);
512 if (this->recordingTask && this->recordingTask->isRunning())
518 const std::string baseFilename =
"recording_" + getDateString();
520 const double fps = [
this]() ->
double
527 return static_cast<unsigned int>(source_fps);
530 return static_cast<double>(this->properties.
frameRate);
533 for (
unsigned int i = 0; i < this->numberImages; ++i)
536 const std::string ext =
538 const std::filesystem::path
filename =
540 getHumanReadableCameraID(i, this->numberImages, this->properties.
depthImageIndex) +
542 const std::filesystem::path fullFilePath = path /
filename;
547 this->recorders.push_back(rec);
554 this, &ImageMonitorWidgetController::recordFrame, 1,
true,
"ImageRecorderTask");
555 this->writeRecordingBuffer =
true;
556 this->recordingTask->start();
564 this->writeRecordingBuffer =
false;
565 if (!this->recordingBuffer.empty())
567 ARMARX_IMPORTANT <<
"Stopping recording... Still " << this->recordingBuffer.size()
568 <<
" images in the queue to write";
574 if (this->recordingTask)
576 this->recordingTask->stop();
585 rec->stopRecording();
588 this->recorders.clear();
592 ImageMonitorWidgetController::recordFrame()
594 std::vector<CByteImage*> images;
596 std::unique_lock lock(this->recordingBufferMutex);
599 if (!this->writeRecordingBuffer)
601 if (this->recordingBuffer.empty())
607 else if (this->recordingBuffer.size() % 5 == 0)
609 ARMARX_INFO <<
"Stopping recording... Still " << this->recordingBuffer.size()
610 <<
" images in the queue to write";
613 else if (this->recordingBuffer.size() > 0 and this->recordingBuffer.size() % 5 == 0)
616 << this->recordingBuffer.size() <<
" images";
620 if (!this->connected or this->recordingBuffer.empty())
625 images = this->recordingBuffer.front();
626 this->recordingBuffer.pop();
631 for (
unsigned int i = 0; i < this->numberImages; ++i)
633 if (this->recorders[i])
635 this->recorders[i]->recordFrame(*images[i], std::chrono::microseconds{0});
644 ImageMonitorWidgetController::updateStatistics()
646 std::unique_lock lock(imageMutex);
649 std::stringstream ss;
651 ss << std::fixed << std::setprecision(1)
655 IceUtil::Time displayDelay = widget->getImageViewer()->getDisplayDelay();
656 displayDelayFilter.
update(timeReceived, displayDelay.toMilliSecondsDouble());
661 if (timeProvided.toMilliSeconds())
663 ss <<
" - image age: " << (timeReceived - timeProvided).toMilliSeconds()
664 <<
" ms - display delay: "
665 << IceUtil::Time::milliSecondsDouble(displayDelayFilter.
getCurrentValue())
666 .toMilliSecondsDouble()
672 ss <<
"display paused";
675 std::string statisticsStr = ss.str();
680 ImageMonitorWidgetController::hideControls(
bool hide)
682 widget->hideControlWidgets(hide);
691 if (parent != customToolbar->parent())
693 customToolbar->setParent(parent);
696 return customToolbar.data();
699 customToolbar =
new QToolBar(parent);
700 customToolbar->setIconSize(QSize(16, 16));
703 customToolbar->addAction(QIcon(
":icons/object-locked-2.ico"),
"Hide controls");
704 viewingModeAction->setCheckable(
true);
705 connect(viewingModeAction, SIGNAL(toggled(
bool)),
this, SLOT(hideControls(
bool)));
708 return customToolbar.data();
715 int pixelCount = depthImage->width * depthImage->height;
716 auto& depthToRgbLookUpTable = depthToRgbLookUpTables[maxDistance];
717 size_t depthValueCount = 256 * 256;
718 if (depthToRgbLookUpTable.size() != depthValueCount)
720 ARMARX_VERBOSE <<
"Resizing lookup table for max distance " << maxDistance;
721 depthToRgbLookUpTable.resize(depthValueCount);
724 for (
int i = 0; i < pixelCount; ++i)
727 depthImage->pixels[pixelPos + 1],
728 depthImage->pixels[pixelPos + 2],
734 static_cast<signed int>(
735 depthToRgbLookUpTable.size()))
737 std::optional<armarx::DrawColor24Bit>& rgbOptional =
738 depthToRgbLookUpTable.at(z_value);
755 depthImage->pixels[pixelPos] = rgb.r;
756 depthImage->pixels[pixelPos + 1] = rgb.g;
757 depthImage->pixels[pixelPos + 2] = rgb.b;
761 depthImage->pixels[pixelPos] = 0;
762 depthImage->pixels[pixelPos + 1] = 0;
763 depthImage->pixels[pixelPos + 2] = 0;
775 getHumanReadableCameraID(
unsigned int index,
unsigned int index_count,
int depth_index)
777 if (index_count >= 2)
779 if (depth_index >= 0)
781 if (
index ==
static_cast<unsigned int>(depth_index))
785 if (index_count == 2 and
index !=
static_cast<unsigned int>(depth_index))
790 else if (index_count == 2)
812 std::string date_str = recordStartTime.toDateTime();
814 date_str = simox::alg::replace_all(date_str,
"/",
"-");
815 date_str = simox::alg::replace_all(date_str,
" ",
"_");
816 date_str = simox::alg::replace_all(date_str,
":",
"-");