26#include <physiology/modules/graph_tweaks.h>
27#include <physiology/graph/stream_and_packet_names.h>
28#include <physiology/modules/geometry.hpp>
29#include <mediapipe/framework/port/logging.h>
30#include <mediapipe/framework/port/file_helpers.h>
31#include <mediapipe/framework/port/status_macros.h>
32#include <mediapipe/framework/port/parse_text_proto.h>
33#include <mediapipe/framework/calculator.pb.h>
34#include <absl/status/statusor.h>
35#include <opencv2/highgui.hpp>
37#include "initialization.hpp"
38#include "configuration.hpp"
39#ifdef ENABLE_CUSTOM_SERVER
40#include "custom_rest_settings.hpp"
44#include <smartspectra/video_source/camera/camera_v4l2.hpp>
45namespace pcam_v4l2 = presage::camera::v4l2;
48#include <smartspectra/video_source/camera/camera_opencv.hpp>
50namespace presage::smartspectra::container::initialization {
52namespace pcam = presage::camera;
53namespace pcam_cv = presage::camera::opencv;
54namespace pe = physiology::edge;
56static void AddGeneralSidePackets(
57 std::map<std::string, mediapipe::Packet>& input_side_packets,
58 const settings::GeneralSettings& settings
60 if (settings.enable_phasic_bp.has_value()) {
61 input_side_packets[pe::graph::input_side_packets::kEnablePhasicBp] =
62 mediapipe::MakePacket<bool>(settings.enable_phasic_bp.value());
64 if (settings.enable_eda.has_value()) {
65 input_side_packets[pe::graph::input_side_packets::kEnableEda] =
66 mediapipe::MakePacket<bool>(settings.enable_eda.value());
68 input_side_packets[pe::graph::input_side_packets::kEnableDenseFaceMeshPoints] =
69 mediapipe::MakePacket<bool>(settings.enable_dense_facemesh_points);
70 input_side_packets[pe::graph::input_side_packets::kEnableEdgeMetrics] =
71 mediapipe::MakePacket<bool>(settings.enable_edge_metrics);
72 input_side_packets[pe::graph::input_side_packets::kModelDirectory] =
73 mediapipe::MakePacket<std::string>(PHYSIOLOGY_EDGE_MODEL_DIRECTORY);
74 if (settings.use_full_range_face_detection.has_value()){
75 input_side_packets[pe::graph::input_side_packets::kUseFullRangeFaceDetection] =
76 mediapipe::MakePacket<bool>(settings.use_full_range_face_detection.value());
78 if (settings.use_full_pose_landmarks.has_value()) {
79 input_side_packets[pe::graph::input_side_packets::kUseFullPoseLandmarks] =
80 mediapipe::MakePacket<bool>(settings.use_full_pose_landmarks.value());
82 if (settings.enable_pose_landmark_segmentation.has_value()) {
83 input_side_packets[pe::graph::input_side_packets::kEnablePoseLandmarkSegmentation] =
84 mediapipe::MakePacket<bool>(settings.enable_pose_landmark_segmentation.value());
86 if (settings.enable_micromotion.has_value()){
87 input_side_packets[pe::graph::input_side_packets::kEnableMicromotion] =
88 mediapipe::MakePacket<bool>(settings.enable_micromotion.value());
90 input_side_packets[pe::graph::input_side_packets::kLogTransferTimingInfo] =
91 mediapipe::MakePacket<bool>(settings.log_transfer_timing_info);
92 if (settings.video_output_directory.has_value()) {
93 input_side_packets[pe::graph::input_side_packets::kVideoOutputDirectory] =
94 mediapipe::MakePacket<std::string>(settings.video_output_directory.value());
98template<settings::OperationMode TOperationMode, settings::IntegrationMode TIntegrationMode,
bool TLog>
99inline absl::StatusOr<mediapipe::CalculatorGraphConfig> InitializeGraphConfig(
100 const std::string& graph_file_path,
101 const settings::Settings<TOperationMode, TIntegrationMode>& settings,
104 std::string calculator_graph_config_contents;
106 mediapipe::file::GetContents(
108 &calculator_graph_config_contents,
113 LOG(INFO) <<
"Scaling input in graph: " << (settings.scale_input ?
"true" :
"false");
115 mediapipe::CalculatorGraphConfig config;
117 RET_CHECK(config.ParseFromArray(calculator_graph_config_contents.c_str(),
118 calculator_graph_config_contents.length()));
120 if (!settings.scale_input) {
122 presage::graph_tweaks::SetOutputWidthAndHeightToZeroIfPresent(calculator_graph_config_contents);
125 if (settings.print_graph_contents) {
126 LOG(INFO) <<
"Get calculator graph config contents: " << calculator_graph_config_contents;
129 config = mediapipe::ParseTextProtoOrDie<mediapipe::CalculatorGraphConfig>(calculator_graph_config_contents);
132 config.add_executor();
138template<settings::IntegrationMode TIntegrationMode>
139inline absl::Status SupplyGraphIntegrationSettings(
140 std::map<std::string, mediapipe::Packet>& input_side_packets,
141 const settings::IntegrationSettings<TIntegrationMode>& integration_settings
145inline absl::Status SupplyGraphIntegrationSettings(
146 std::map<std::string, mediapipe::Packet>& input_side_packets,
147 const settings::IntegrationSettings<settings::IntegrationMode::Grpc>& integration_settings
149 input_side_packets[pe::graph::input_side_packets::grpc::kGrpcCorePortNumber] = mediapipe::MakePacket<uint16_t>(
150 integration_settings.port_number);
151 return absl::OkStatus();
155inline absl::Status SupplyGraphIntegrationSettings(
156 std::map<std::string, mediapipe::Packet>& input_side_packets,
157 const settings::IntegrationSettings<settings::IntegrationMode::Rest>& integration_settings
159 input_side_packets[pe::graph::input_side_packets::kApiKey] = mediapipe::MakePacket<std::string>(integration_settings.api_key);
161#ifdef ENABLE_CUSTOM_SERVER
163 if (integration_settings.continuous_server_url.has_value()) {
164 settings::CustomServerConfiguration config;
165 config.continuous_server_url = integration_settings.continuous_server_url;
167 MP_RETURN_IF_ERROR(settings::ApplyCustomServerConfig(config));
171 return absl::OkStatus();
174template<
typename TSettings>
175inline absl::Status InitializeGraphWithConfig(
176 mediapipe::CalculatorGraph& graph,
177 const mediapipe::CalculatorGraphConfig& config,
178 const TSettings& settings
181template<settings::IntegrationMode TIntegrationMode>
182inline absl::Status InitializeGraphWithConfig(
183 mediapipe::CalculatorGraph& graph,
184 const mediapipe::CalculatorGraphConfig& config,
185 const settings::Settings<settings::OperationMode::Continuous, TIntegrationMode>& settings
187 std::map<std::string, mediapipe::Packet> input_side_packets;
188 AddGeneralSidePackets(input_side_packets, settings);
189 input_side_packets[pe::graph::input_side_packets::continuous::kPreprocessedDataBufferDuration] =
190 mediapipe::MakePacket<double>(settings.continuous.preprocessed_data_buffer_duration_s);
192 const double lower_buffer_duration_threshold_s = 0.2;
193 if (settings.continuous.preprocessed_data_buffer_duration_s < lower_buffer_duration_threshold_s &&
194 lower_buffer_duration_threshold_s-settings.continuous.preprocessed_data_buffer_duration_s > 1e-6) {
195 return absl::InvalidArgumentError(
196 "The preprocessed data buffer duration is set to less than "
197 + std::to_string(lower_buffer_duration_threshold_s) +
198 " seconds. This currently may cause Physiology Core to fail in producing metrics.");
200 MP_RETURN_IF_ERROR(SupplyGraphIntegrationSettings(input_side_packets, settings.integration));
201 MP_RETURN_IF_ERROR(graph.Initialize(config, input_side_packets));
202 return absl::OkStatus();
205template<settings::IntegrationMode TIntegrationMode>
206inline absl::Status InitializeGraphWithConfig(
207 mediapipe::CalculatorGraph& graph,
208 const mediapipe::CalculatorGraphConfig& config,
209 const settings::Settings<settings::OperationMode::Spot, TIntegrationMode>& settings
211 std::map<std::string, mediapipe::Packet> input_side_packets;
212 input_side_packets[pe::graph::input_side_packets::spot::kSpotDurationS] =
213 mediapipe::MakePacket<double>(settings.spot.spot_duration_s);
214 AddGeneralSidePackets(input_side_packets, settings);
215 MP_RETURN_IF_ERROR(SupplyGraphIntegrationSettings(input_side_packets, settings.integration));
216 MP_RETURN_IF_ERROR(graph.Initialize(config, input_side_packets));
217 return absl::OkStatus();
221 platform_independence::DeviceType TDeviceType,
222 settings::OperationMode TOperationMode,
223 settings::IntegrationMode TIntegrationMode,
226absl::Status InitializeGraph(
227 mediapipe::CalculatorGraph& graph,
228 const std::string& graph_file_path,
229 const settings::Settings<TOperationMode, TIntegrationMode>& settings,
233 LOG(INFO) <<
"Initialize the calculator graph.";
234 LOG(INFO) <<
"OpenGl buffers used in graph: "
235 << (TDeviceType == platform_independence::DeviceType::OpenGl ?
"true" :
"false");
237 auto status_or_config =
238 InitializeGraphConfig<TOperationMode, TIntegrationMode, TLog>(graph_file_path, settings, binary_graph);
240 if (!status_or_config.ok()) {
241 return status_or_config.status();
243 mediapipe::CalculatorGraphConfig config = status_or_config.value();
244 return InitializeGraphWithConfig(graph, config, settings);
247template<platform_independence::DeviceType TDeviceType>
248inline absl::Status InitializeComputingDevice_Internal(
249 mediapipe::CalculatorGraph& graph,
250 platform_independence::DeviceContext<TDeviceType>& device_context
254inline absl::Status InitializeComputingDevice_Internal<platform_independence::DeviceType::Cpu>(
255 mediapipe::CalculatorGraph& graph,
256 platform_independence::DeviceContext<platform_independence::DeviceType::Cpu>& device_context
258 return absl::OkStatus();
264inline absl::Status InitializeComputingDevice_Internal<platform_independence::DeviceType::OpenGl>(
265 mediapipe::CalculatorGraph& graph,
266 platform_independence::DeviceContext<platform_independence::DeviceType::OpenGl>& device_context
268 MP_ASSIGN_OR_RETURN(
auto gpu_resources, mediapipe::GpuResources::Create());
269 MP_RETURN_IF_ERROR(graph.SetGpuResources(std::move(gpu_resources)));
270 device_context.gpu_helper.InitializeForTest(graph.GetGpuResources().get());
271 return absl::OkStatus();
276template<platform_independence::DeviceType TDeviceType,
bool TLog>
277absl::Status InitializeComputingDevice(
278 mediapipe::CalculatorGraph& graph,
279 platform_independence::DeviceContext<TDeviceType>& device_context
282 LOG(INFO) <<
"Initialize the compute device.";
284 return InitializeComputingDevice_Internal(graph, device_context);
287static std::string SubstituteVideoSinkTemplate(
288 const std::string& template_string,
289 const cv::Size& output_resolution,
290 const float output_fps
292 std::string result = template_string;
295 std::regex width_regex(
"%width%");
296 std::regex height_regex(
"%height%");
297 std::regex fps_regex(
"%fps%");
300 result = std::regex_replace(result, width_regex, std::to_string(output_resolution.width));
301 result = std::regex_replace(result, height_regex, std::to_string(output_resolution.height));
302 result = std::regex_replace(result, fps_regex, std::to_string(output_fps));
307template<platform_independence::DeviceType TDeviceType,
bool TLog>
308absl::Status InitializeVideoSink(
309 cv::VideoWriter& stream_writer,
310 const cv::Size& input_resolution,
311 const std::string& destination,
312 const float output_fps,
313 settings::VideoSinkMode video_sink_mode
315 if (!destination.empty() && video_sink_mode != settings::VideoSinkMode::Unknown_EnumEnd) {
317 LOG(INFO) <<
"Initialize the video sink.";
319 cv::Size output_resolution = input_resolution;
320 switch (video_sink_mode) {
321 case settings::VideoSinkMode::MJPG:
323 destination, cv::VideoWriter::fourcc(
'M',
'J',
'P',
'G'),
324 output_fps, output_resolution,
true
327 case settings::VideoSinkMode::GSTREAMER_TEMPLATED:
329 SubstituteVideoSinkTemplate(destination, output_resolution, output_fps),
330 cv::CAP_GSTREAMER, 0, output_fps, output_resolution,
true
334 return absl::InvalidArgumentError(absl::StrCat(
"Unsupported video sink mode with int code ",
335 static_cast<int>(video_sink_mode)));
337 RET_CHECK(stream_writer.isOpened());
339 return absl::OkStatus();
343absl::Status InitializeGui(
const settings::GeneralSettings& settings,
const std::string& window_name) {
345 LOG(INFO) <<
"Initialize the graphical user interface.";
348 if (!settings.headless) {
349 cv::namedWindow(window_name, 1);
351 return absl::OkStatus();