34 #include <opencv2/opencv.hpp>
37 #include <SimoxUtility/algorithm.h>
56 if (numberImages == 0)
58 return armarx::Blob();
67 if (numberImages == 0)
69 return armarx::Blob();
96 setImageFormat(ImageDimension(640, 480), eBayerPattern, eBayerPatternRg);
111 if (numberImages != 0)
159 if (numberImages != 0)
172 <<
" Did you forget to set call setNumberImages and setImageFormat in onInitImageProvider?";
178 info->timeProvided = timestamp;
182 info =
new armarx::MetaInfoSizeBase(imageFormat.dimension.width * imageFormat.dimension.height * imageFormat.bytesPerPixel,
183 imageFormat.dimension.width * imageFormat.dimension.height * imageFormat.bytesPerPixel,
205 switch (imageFormat.type)
214 throw armarx::LocalException() <<
"unsupported image type " << (int)(imageFormat.type);
224 if (numberImages == 0)
226 return armarx::Blob();
230 auto imageTimestamp = IceUtil::Time::microSeconds(info->timeProvided);
231 auto key = std::make_pair(compressionType, compressionQuality);
233 std::unique_lock lock2(compressionDataMapMutex);
234 if (compressionDataMap.count(key) && compressionDataMap.at(key).imageTimestamp == imageTimestamp)
237 return compressionDataMap.at(key).compressedImage;
239 cv::Mat mat(imageFormat.dimension.height * numberImages, imageFormat.dimension.width, type,
imageBuffers[0]);
242 armarx::Blob encodedImg;
244 std::vector<int> compression_params;
245 std::string extension;
246 switch (compressionType)
250 compression_params = {cv::IMWRITE_PNG_COMPRESSION, compressionQuality,
251 cv::IMWRITE_PNG_STRATEGY, cv::IMWRITE_PNG_STRATEGY_RLE
256 compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
257 compression_params.push_back(compressionQuality);
260 throw armarx::LocalException() <<
"unsupported image type " << (int)(imageFormat.type);
262 cv::imencode(
"*" + extension, mat, encodedImg, compression_params);
264 compressionDataMap[key] = {encodedImg, imageTimestamp};
267 ARMARX_DEBUG <<
deactivateSpam(1) <<
"size before compression: " << imageFormat.dimension.height* numberImages* imageFormat.dimension.width* imageFormat.bytesPerPixel <<
" size after: " << encodedImg.size();
277 BayerPatternType bayerPatternType)
280 imageFormat.dimension = imageDimension;
281 imageFormat.type = imageType;
282 imageFormat.bpType = bayerPatternType;
288 imageFormat.bytesPerPixel = 1;
292 imageFormat.bytesPerPixel = 3;
296 imageFormat.bytesPerPixel = 4;
299 case eFloat3Channels:
300 imageFormat.bytesPerPixel = 12;
304 imageFormat.bytesPerPixel = 12;
307 case eColoredPointsScan:
308 imageFormat.bytesPerPixel = 16;
316 this->numberImages = numberImages;
322 if (numberImages == 0)
324 ARMARX_INFO <<
"Number of images is 0 - thus none can be provided";
328 int imageSize = imageFormat.dimension.width * imageFormat.dimension.height * imageFormat.bytesPerPixel;
342 for (
int i = 0 ; i < numberImages ; i++)
365 if (numberImages == 0)
367 ARMARX_INFO <<
"Number of images is 0 - thus none can be provided";
374 for (
int i = 0 ; i < numberImages ; i++)
386 if (numberImages == 0)
388 ARMARX_INFO <<
"Number of images is 0 - thus none can be provided";
395 for (
int i = 0 ; i < numberImages ; i++)
407 if (numberImages == 0)
409 ARMARX_INFO <<
"Number of images is 0 - thus none can be provided";
416 for (
int i = 0 ; i < numberImages ; i++)
429 int imageSize = imageFormat.dimension.width * imageFormat.dimension.height * imageFormat.bytesPerPixel;
431 const bool is_recording = [&]
435 std::scoped_lock l{rec.statusMutex};
437 if (rec.status.type == imrec::State::stopping)
439 rec.status.type = imrec::State::writing;
444 return rec.status.type == imrec::State::running;
451 std::chrono::microseconds timestamp{image_timestamp.toMicroSeconds()};
452 std::unordered_map<int, CByteImage*> current_frames;
453 for (
int i = 0; i < numberImages; ++i)
455 if (not rec.config.channelConfigs[i].disabled)
458 std::memcpy(current_frames[i]->pixels,
imageBuffers[i], imageSize);
464 std::scoped_lock l{rec.bufferMutex};
465 rec.buffer.push_back({timestamp, current_frames});
480 <<
"Must supply same number of channel configs as there are channels";
482 std::scoped_lock l{rec.callMutex};
485 std::scoped_lock l{rec.statusMutex};
487 auto are_disabled = [](
const auto &
c)
494 if (rec.status.type != imrec::State::ready
495 or std::all_of(cfg.channelConfigs.begin(), cfg.channelConfigs.end(), are_disabled))
500 rec.status.framesWritten = 0;
501 rec.status.framesBuffered = 0;
502 rec.status.type = imrec::State::scheduled;
507 simox::alg::replace_all(
514 rec.runningTask->start();
526 std::scoped_lock l{rec.statusMutex};
535 std::scoped_lock l{rec.callMutex};
538 std::scoped_lock l{rec.statusMutex};
542 if (rec.status.type != imrec::State::scheduled
543 and rec.status.type != imrec::State::running)
548 rec.status.type = imrec::State::stopping;
553 const bool join =
true;
554 rec.runningTask->stop(join);
573 std::scoped_lock l{rec.statusMutex};
574 return rec.status.type;
578 if (state == imrec::State::ready)
587 else if (state == imrec::State::scheduled)
591 const IceUtil::Time start_at = IceUtil::Time::microSeconds(rec.config.startTimestamp);
597 const std::filesystem::path path =
599 rec.channelRecordings.clear();
600 for (
int i = 0; i < numberImages; ++i)
602 const imrec::ChannelConfig& channel_cfg = rec.config.channelConfigs[i];
603 if (not channel_cfg.disabled)
606 const std::string name =
getName() +
"_" + channel_cfg.name;
610 r->writeMetadataDatetime(
"recording_manager_time", std::chrono::microseconds{rec.config.startTimestamp});
611 r->writeMetadataLine(
"image_provider_name",
"string",
getName());
612 r->writeMetadataLine(
"channel_name",
"string", channel_cfg.name);
613 rec.channelRecordings[i] = r;
619 std::scoped_lock l{rec.statusMutex};
620 rec.status.type = imrec::State::running;
630 bool write_frames =
false;
631 std::chrono::microseconds timestamp;
632 std::unordered_map<int, CByteImage*> frames;
637 std::scoped_lock l{rec.bufferMutex, rec.statusMutex};
638 if (not rec.buffer.empty())
640 std::tie(timestamp, frames) = rec.buffer.front();
641 rec.buffer.pop_front();
644 rec.status.framesBuffered =
static_cast<long>(rec.buffer.size());
645 ++rec.status.framesWritten;
652 if (state == imrec::State::writing)
654 ARMARX_DEBUG <<
"Buffer fully written to disk, exiting.";
655 rec.status.type = imrec::State::ready;
664 for (
auto& [i, frame] : frames)
667 r->recordFrame(*frame, timestamp);
674 else if (state == imrec::State::running)
676 std::unique_lock l{rec.bufferMutex};
678 rec.cv.wait(l, [&] {
return not rec.buffer.empty(); });
691 for (
auto& [i, r] : rec.channelRecordings)
695 rec.channelRecordings.clear();
704 std::vector<imrec::ChannelPreferences>
709 std::vector<imrec::ChannelPreferences> default_names;
710 imrec::ChannelPreferences cp;
711 cp.requiresLossless =
false;
712 for (
int i = 0; i < numberImages; ++i)
715 default_names.push_back(cp);
717 return default_names;