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();
90 if (
const char* home = std::getenv(
"HOME"))
112 displayDelayFilter(
IceUtil::Time::seconds(3), 100)
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&)));
157 if (properties.providerName !=
"" )
166 disconnectFromProvider();
186 std::unique_lock lock(imageMutex);
187 unsigned int retrievedImages = 0;
188 const bool sourceFps = this->properties.frameRate < 0.0f;
192 if (sourceFps && !this->
waitForImages(this->properties.providerName))
198 armarx::MetaInfoSizeBasePtr info;
199 retrievedImages =
static_cast<unsigned int>(
200 this->
getImages(this->properties.providerName, this->images, info));
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) &&
217 this->properties.recordDepthRaw)
219 currentImage =
new CByteImage(this->images[i]);
220 ::ImageProcessor::CopyImage(this->images[i], currentImage);
221 deleteList.push_back(currentImage);
225 currentImage = this->images[i];
228 if (this->properties.imagesToShow.size() == 0 or
229 (this->properties.imagesToShow.size() > 0 and
230 this->properties.imagesToShow.count(i) > 0))
232 if (i ==
static_cast<unsigned int>(this->properties.depthImageIndex))
237 selectedImages.push_back(currentImage);
241 if (selectedImages.size() > 0)
243 this->widget->drawImages(
static_cast<int>(selectedImages.size()),
249 for (CByteImage* image : deleteList)
256 this->fpsCounter.assureFPS(this->properties.frameRate);
260 if (writeImageBuffer && retrievedImages > 0)
262 if ((imageBuffer.size()) !=
static_cast<unsigned int>(properties.imageBufferSize) &&
263 properties.imageBufferSize > 0)
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();
308 properties.imagesToShow.clear();
309 for (QString& s : imagesToShowStringList)
311 properties.imagesToShow.insert(s.toULong());
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();
318 properties.compressionType =
319 static_cast<CompressionType
>(settings->value(
"compressionType", 0).
toInt());
320 properties.compressionQuality = settings->value(
"compressionQuality", 0).toInt();
322 ARMARX_INFO <<
"bufferFps: " << properties.bufferFps;
328 settings->setValue(
"providerName", QString(properties.providerName.c_str()));
329 settings->setValue(
"frameRate", properties.frameRate);
330 settings->setValue(
"outputPath", QString(properties.outputDirectory.c_str()));
331 settings->setValue(
"imageBufferSize", properties.imageBufferSize);
332 settings->setValue(
"bufferFps", QString::number(
static_cast<double>(properties.bufferFps)));
334 for (
auto number : properties.imagesToShow)
336 l << QString::number(number);
338 settings->setValue(
"imagesToShow", l);
339 settings->setValue(
"controlsHidden", properties.controlsHidden);
340 settings->setValue(
"depthImageIndex", properties.depthImageIndex);
341 settings->setValue(
"maxDepthmm", properties.maxDepthmm);
342 settings->setValue(
"recordDepthRaw", properties.recordDepthRaw);
343 settings->setValue(
"recordingMethods", properties.recordingMethods);
345 settings->setValue(
"compressionType", (
int)properties.compressionType);
346 settings->setValue(
"compressionQuality", (
int)properties.compressionQuality);
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);
448 properties.compressionQuality);
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;
488 fs::path absPath = fs::absolute(fs::path(properties.outputDirectory));
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)
502 std::filesystem::path filename =
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
522 if (this->properties.frameRate <= 0)
526 const float source_fps = std::round(stats.imageProviderFPS.getFPS());
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 =
537 this->properties.recordingMethods.at(
static_cast<int>(i)).toStdString();
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);
683 properties.controlsHidden = 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)));
706 viewingModeAction->setChecked(properties.controlsHidden);
708 return customToolbar.data();
714 float maxDistance = properties.maxDepthmm;
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)
802 return "_cam" + std::to_string(
index);
811 IceUtil::Time recordStartTime = IceUtil::Time::now();
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,
":",
"-");
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
std::string getName() const
Retrieve name of object.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
float getFPS()
Get frames per second.
ImageMonitorProperties brief one line description.
CompressionType compressionType
std::filesystem::path outputDirectory
void usingImageProvider(std::string name)
Registers a delayed topic subscription and a delayed provider proxy retrieval which all will be avail...
bool waitForImages(int milliseconds=1000)
Wait for new images.
ImageProviderInfo getImageProvider(std::string name, ImageType destinationImageType=eRgb, bool waitForProxy=false)
Select an ImageProvider.
int getImages(CByteImage **ppImages)
Poll images from provider.
ImageTransferStats getImageTransferStats(std::string provideNname, bool resetStats=false)
Retrieve statistics for a connection to an ImageProvider.
void setCompressionType(CompressionType compressionType=ePNG, int compressionQuality=9)
Sets the compression type and compression quality.
void releaseImageProvider(std::string providerName)
int numberImages
Number of images.
ImageProviderInterfacePrx proxy
proxy to image provider
The ImageTransferStats class provides information on the connection between ImageProvider and ImagePr...
FPSCounter pollingFPS
Statistics for the images polled by the ImageProcessor.
FPSCounter imageProviderFPS
Statistics for the images announced by the ImageProvider.
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_VERBOSE
The logging level for verbose information.
#define TIMING_START(name)
Helper macro to do timing tests.
#define TIMING_END(name)
Prints duration.
HsvColor HeatMapColor(float percentage)
HeatMapColor calculates the color of a value between 0 and 1 on a heat map.
DrawColor24Bit HsvToRgb(const HsvColor &in)
This file offers overloads of toIce() and fromIce() functions for STL container types.
const LogSender::manipulator flush
int toInt(const std::string &input)
visionx::imrec::Recording newRecording(const std::filesystem::path &path, const std::string &name, const Format format, double fps)
void takeSnapshot(const CByteImage &image, const std::filesystem::path &filePath)
Takes a snapshot using the default recording method for snapshots.
std::shared_ptr< AbstractRecordingStrategy > Recording
Convenience alias for any recording strategy.
std::shared_ptr< CByteImage > CByteImagePtr
std::vector< CByteImagePtr > ImageContainer