From 7a08918f09185617a4335957d5bdbeeefec2638f Mon Sep 17 00:00:00 2001 From: Khalil Selyan <36904941+KhalilSelyan@users.noreply.github.com> Date: Fri, 28 Jun 2024 11:22:01 +0300 Subject: [PATCH] feat: update trafficlight roi styling (#6985) Signed-off-by: KhalilSelyan Signed-off-by: palas21 --- .../CMakeLists.txt | 7 + .../images/circle.png | Bin 0 -> 1258 bytes .../images/cross.png | Bin 0 -> 892 bytes .../images/down_left_arrow.png | Bin 0 -> 683 bytes .../images/left_arrow.png | Bin 0 -> 750 bytes .../images/straight_arrow.png | Bin 0 -> 888 bytes .../images/unknown.png | Bin 0 -> 1354 bytes .../traffic_light_roi_visualizer/nodelet.hpp | 14 ++ .../shape_draw.hpp | 51 ++++++ .../traffic_light_roi_visualizer/nodelet.cpp | 23 +-- .../shape_draw.cpp | 173 ++++++++++++++++++ 11 files changed, 255 insertions(+), 13 deletions(-) create mode 100644 perception/traffic_light_visualization/images/circle.png create mode 100644 perception/traffic_light_visualization/images/cross.png create mode 100644 perception/traffic_light_visualization/images/down_left_arrow.png create mode 100644 perception/traffic_light_visualization/images/left_arrow.png create mode 100644 perception/traffic_light_visualization/images/straight_arrow.png create mode 100644 perception/traffic_light_visualization/images/unknown.png create mode 100644 perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/shape_draw.hpp create mode 100644 perception/traffic_light_visualization/src/traffic_light_roi_visualizer/shape_draw.cpp diff --git a/perception/traffic_light_visualization/CMakeLists.txt b/perception/traffic_light_visualization/CMakeLists.txt index 8b5f54c034c7b..08f6b48cabbd9 100644 --- a/perception/traffic_light_visualization/CMakeLists.txt +++ b/perception/traffic_light_visualization/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(OpenCV REQUIRED) ament_auto_add_library(traffic_light_roi_visualizer_nodelet SHARED src/traffic_light_roi_visualizer/nodelet.cpp + src/traffic_light_roi_visualizer/shape_draw.cpp ) target_link_libraries(traffic_light_roi_visualizer_nodelet @@ -24,6 +25,12 @@ ament_auto_add_executable(traffic_light_map_visualizer_node src/traffic_light_map_visualizer/main.cpp ) +# Copy the assets directory to the installation directory +install( + DIRECTORY images/ + DESTINATION share/${PROJECT_NAME}/images +) + ament_auto_package( INSTALL_TO_SHARE launch diff --git a/perception/traffic_light_visualization/images/circle.png b/perception/traffic_light_visualization/images/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..1f44f7bff39a26bdc655092175724c576529e679 GIT binary patch literal 1258 zcmVPx(q)9|URCr$Por!T2K@3I5kpPhZkpPhZkzgVLM*>6w{PM^(M;}tRH0R$c*RIQb ztpAhLQqT1;@U^P~54+CJ$p-@1*$B=A;Pt2#-tqHl^83^H`S=m|8h3ee0Ka z27tgj#A7!79(w=~;Ug{GDk8*(M{EvaYXF~vLyT4UWqSefwRa$G|E&NVUHIKVL<2x< z|2AeaTBA5pyKs+)1n^@_Bab9#>vzHW@Ts+Psb2&DEV8irJu;gJjbu!C!+Y;+$r}Jf zhvi@O1qfK6-D(I=03S!ozpD6CPXGXyOvwwt*|HoDBS9V04Z#Dzl@?no7=)xZ)=ty* zy#VfL`AfaKGNc{=S6^@~hTR;FS^-e}IUa-})CuP8?iK*Z^M6^)<0fsJ>Vy^mcV2XB zwq#zEtpF%~4Z(Afrrx%&0Juw`RhPKl0svIivIPL*e#^1h`q#Z)3w0>c0-*KVjYQ=I zS^%_uI{=jzXaKOczzX6&U+oGMy`Tnw*55At^!Pz` zo1z4OnNPp?GD3EQw^3@Z$#{o08@LF#Pz5FC)PNR@>D%0L*^; z0VKmv4gkZCKY(NyN&#T_@fSZEhEf0+e&PVEin0^{ZoI_-SgivD5GQtOA}0 z4hjLpX{#O?XVSe~kGAvxo^qnrvAN-_AX@_930KX!syh||um(`;*xYb-ta#}GJp9W-vFbw$vNM19R1Hdrk4`5b+$?!Zq-CyX`h}o-Tc!9km~h&Nn$c0Im!{y}zTe{D=@Vo%e_j?6l~zY}>uqZs`HbS^;arS&t>S zKQ}BQ3#wU`cmq<^vw#SHhGcXINU^eq~aSW-5 zdwb`tFS8?0OX70j7c5qdUK*@cjeZM6QZ%&}c0Bo(cYL1VkAI&wR=*csd|LHMN^Jo5 zoE59TKf1>7gx!(ZgkdK`<WYd`wlUC5k;m4VKt#GYkwqiWHikDk;Sq3 zqqD#ShU!PF72fpnyE`z-be?y!kdp6eV2H34v|spUwepd9wf9elyK^wqA9|#|r7A^n z!K2?l>*mbAEn`>C(lCF5{moSk4L$GqCFVCQWoCRJy`n)u{QlvE3|9nI9xrAL5lngP z&agt!<9I()fa8RIdCmiF8iMvx4Xpucb!-l1PAh*zGDLDbeU#4RqCBN%KCeLQgZgAiU!vnA=8*E|5{D#% zj^u#F4?U^oJ@i_`5zn6%1fF^>L}70E6hG z(@F{qe^|dPUmJc)ec!yV*94#BcH4hWl#Tp2aq@%eZ~h@AEr;2Ym})*wbq_78VPR6) zm(RlbV1IUv!|Qp~pV$0Y&GGN&a_{~>8yQs&#Icn9S*4(w%U`q9LaSW-5 zdwb`0=wSnamc*sPFIc=58hJJPEfDGB=j3dk;qkgNJ>9f&+TL&fUHNy)&62;u#Qv&; zKc7v(o|7kmp@{KQ#d`+k3EUHoSF=kr#5CC4vp&G^gz3r0dyH%f;xDBCUx=#CUTgbJ z_kH|R*@E3yh5kR@@kY2eq2WKH-sAaUPJ%oe_zHCGWDYW1XZ+XA?`m)$zG2=E0~Y2# zY!aRPjs^!L6@==In;7>A=XLJqJjgKPhkDERYqvhXoi6rVbThNUyr1_NmCx_!-umUo z>8)pWF$W!)FRI}CN0Kq<=zLy<*grEFf{x8+RmlA#$q;mWJ>LYox>SbFA07=)nO`qI z7{282``IP);??S08CL$d%JAe-IMallcvc6qI#!04KVC6>`Z$%*>qt1OhH$*#f{+^1 zhN6#C8N7~#Gipf3GcG9k^M>K+<8-zeN_AlknUB(0W@y!!HN1S3&NRcQPOKsGaXLfA z&L4s$&(q(nKD}W7{kv6$dyg;PH&5NK3f)$5(8nh&B`Wc%Z z2DD2ugc?=IF|!O>8%LY7Y#URB?Z4kTuXcU2@}Ft`_#0PP9$yyw6zpUXO@geCyN CwI413 literal 0 HcmV?d00001 diff --git a/perception/traffic_light_visualization/images/left_arrow.png b/perception/traffic_light_visualization/images/left_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e48efaa8092e73ba952ade7b7c545d4a4f9164a9 GIT binary patch literal 750 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-HD>U|Qkn;uum9 z_xAQfUu{DH*N4997dWRlX@)R`Ff9$>GP*T&k;ac=q zV%lXS`fg`@Mu1R8?%7@X4{DjN6hE!TEx(i-!VT=h|!@Y z`40!fBk?2O*%ay$Yy24=g&+COq)?Yy1Jq-FB%f8GF8Plb!}%XC!~69&$K2L5U0Esb z@aNL0zUxgL?t;=HtZG0`7QeXU5(xM zUf-%tzJ#yg$G>f943B;v_y3*&w8N)HnBn8&N9l|m^3wNNKJ2V_v;VhnjiusmnF-q% zT6Q!mb5C$U2#!F(VCwX%Zr1gmJI$55rY}tI*Q~vjw9-CamRF(tfY0}0h9gTD7!>pr hU^en}aSW-5 zdwZuh_lkqS(T|?$7dWRlsZL><%P=>9t4GB9nr3<2{@Tx~dp$HfPOiEB|Gs76gp;#h zuHAAV`sKya0%3=b-3e9)mVd9S@3ubFKA-2o$NCvF_y5mIQx;stB=d3qoOKNoIfNPI z9?MVPAJ6z$U>T!Lx4l@R(1FlD6FGtz!XEcK&tNDp|EJONjv?cZA;(KrLf^9|DeYS-{3#{t6TeKSV zKAslTV%T+5-}MCB3#t7)ioy%3{|L3{G-!UjENH~A?5MwM2ipuOdtODMgsMLxEgB4I ze;zlkeyq9U>hcR0cJ^Q6uYRJ+)FAtR_RL!I&5sq=gf`qyd$xK1z4`N^=T?8KROHBM z==dRbNm+^E--2oHSL!keD`@T8uT;)Zvrm_ayFv1P^H1&T@js8>)^mtvVRI;{IVM`* zux+4`@YwVPpR3krAAH|q_x?`YTc!kPTuL9 z&$wYzeicyh>d8B$?YSRZ-RJzsoyo>pQl(DqfctO0Zh77h6-F6B#6bNfR0@sILVf6o8BU$*Ys*B^!7_>;7cf46P_|NK2q!_>)pf6d`)SZy0+FZ=)Lb&oQO)Rr?mxuRjw#lW>_ fKEGx|QbWB^m~cTP|Bp++ti<5y>gTe~DWM4fr!{=S literal 0 HcmV?d00001 diff --git a/perception/traffic_light_visualization/images/unknown.png b/perception/traffic_light_visualization/images/unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..52393aeeea1fe13ef9e1612cbe645bef7373ec45 GIT binary patch literal 1354 zcmV-Q1-1H#P)Px)1W80eRCr$PondyHFbv1#t_Mj^(&i!yY4(9zw7mr%mX7QqV<%Y-(!{F?gHuNYSEwYgUZSjK=54L}UZnE)LHLKFb2P5vjMi#H#Nw&w-|&}2JCdPMw`f@S%$sHkr?2!LjQ^^f{t zkgs3uw#5?pnqMv#1OUT~$I7ls!@7JgN~?n%4+rryHkcxm_GB_3q-VbOKW>d!u%?o3Af2SYvt-{_mfFXIp znxjR^glsO; zatQlUgpa0C8sEHbMmZlWUV@N2+rA%=cD`+6VkHQ>v+Y}-y7LtcfY|emZODpmebC|} z0IR$(+A$KYf)Eb?qqsI={Jm1Jwq1GQlRSu&AY$nWZhTQ$5V7<`F}^4Ogx(*`_@V$1 zrJX@?eeokbC+>}2mn|e zaH#su91v0fV61RvB|V}>!mCZL=XlkXW65j#F83%U0=xlmlphgU5Y`xga%qW+=Zldb zGynlh%yWRSmmn+v)RXITj4o;%kN~)%MLMsum8I7esaH3+-W+=ga(yr<=OqABKcRA#^P5m_tq5#aC7l^vr!8U3{ z0PtCY&S8uCF&OLtAWcVl@48~^(Q}Vu4FIbS)btC%wj-Vcfi-}cnm_s69Ones0zi_U zJ`{FR#1;V3Jn)yOpnmIdG7NhMumpgq{R0MJH95}{!0E$nRNt0ivnI9G`ZB~R8{!Ip^gA;Nz(lHmwAJL*RyeJYU>N~EwI7Q#kkuMdo&ZiC^(6V` z47VkUECCoVrg|SZ4a6)o!W`1bkm-!)>@_`gbyi~lw&Q@-ipb*uR@&b_BU=e-ZxBn0 zIHjK5Fl7&*sSBAmB%P&*TgNL3Ku?aY?zDbI{AYdkWU2$9_&fUu=sxtEj+wL9i;RE^ z5Lv!}dsrNg@YsC5>hviq4S(SXm8A7Sq9TAo0%Bh?0f0f-FXKa0U`f972b;Q#;t M07*qoM6N<$g0_`dDgXcg literal 0 HcmV?d00001 diff --git a/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/nodelet.hpp b/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/nodelet.hpp index f89b8efa35892..f7ac7471e21ef 100644 --- a/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/nodelet.hpp +++ b/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/nodelet.hpp @@ -85,6 +85,20 @@ class TrafficLightRoiVisualizerNodelet : public rclcpp::Node {tier4_perception_msgs::msg::TrafficLightElement::UNKNOWN, "unknown"}, }; + std::string extractShapeName(const std::string & label) + { + size_t start_pos = label.find('-'); + if (start_pos != std::string::npos) { + start_pos++; // Start after the hyphen + size_t end_pos = label.find(',', start_pos); // Find the next comma after the hyphen + if (end_pos == std::string::npos) { // If no comma is found, take the rest of the string + end_pos = label.length(); + } + return label.substr(start_pos, end_pos - start_pos); + } + return "unknown"; // Return "unknown" if no hyphen is found + } + bool createRect( cv::Mat & image, const tier4_perception_msgs::msg::TrafficLightRoi & tl_roi, const cv::Scalar & color); diff --git a/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/shape_draw.hpp b/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/shape_draw.hpp new file mode 100644 index 0000000000000..7f83104edf48e --- /dev/null +++ b/perception/traffic_light_visualization/include/traffic_light_visualization/traffic_light_roi_visualizer/shape_draw.hpp @@ -0,0 +1,51 @@ +// Copyright 2024 The Autoware Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct DrawFunctionParams +{ + cv::Mat & image; + cv::Point position; + cv::Scalar color; + int size; + float probability; +}; + +using DrawFunction = std::function; + +void drawShape( + const DrawFunctionParams & params, const std::string & filename, bool flipHorizontally, + bool flipVertically, int x_offset, int y_offset, double scale_factor = 0.3); +void drawCircle(const DrawFunctionParams & params); +void drawLeftArrow(const DrawFunctionParams & params); +void drawRightArrow(const DrawFunctionParams & params); +void drawStraightArrow(const DrawFunctionParams & params); +void drawDownArrow(const DrawFunctionParams & params); +void drawDownLeftArrow(const DrawFunctionParams & params); +void drawDownRightArrow(const DrawFunctionParams & params); +void drawCross(const DrawFunctionParams & params); +void drawUnknown(const DrawFunctionParams & params); +void drawTrafficLightShape( + cv::Mat & image, const std::string & shape, const cv::Point & position, const cv::Scalar & color, + int size, float probability); diff --git a/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/nodelet.cpp b/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/nodelet.cpp index 45d6759e6cb7d..df097e64ecd36 100644 --- a/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/nodelet.cpp +++ b/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/nodelet.cpp @@ -14,6 +14,8 @@ #include "traffic_light_visualization/traffic_light_roi_visualizer/nodelet.hpp" // NOLINT(whitespace/line_length) +#include "traffic_light_visualization/traffic_light_roi_visualizer/shape_draw.hpp" // NOLINT(whitespace/line_length) + #include #include @@ -93,29 +95,24 @@ bool TrafficLightRoiVisualizerNodelet::createRect( { cv::Scalar color; if (result.label.find("red") != std::string::npos) { - color = cv::Scalar{255, 0, 0}; + color = cv::Scalar{254, 149, 149}; } else if (result.label.find("yellow") != std::string::npos) { - color = cv::Scalar{0, 255, 0}; + color = cv::Scalar{254, 250, 149}; } else if (result.label.find("green") != std::string::npos) { - color = cv::Scalar{0, 0, 255}; + color = cv::Scalar{149, 254, 161}; } else { - color = cv::Scalar{255, 255, 255}; + color = cv::Scalar{250, 250, 250}; } cv::rectangle( image, cv::Point(tl_roi.roi.x_offset, tl_roi.roi.y_offset), cv::Point(tl_roi.roi.x_offset + tl_roi.roi.width, tl_roi.roi.y_offset + tl_roi.roi.height), - color, 3); + color, 2); - int offset = 40; - cv::putText( - image, std::to_string(result.prob), - cv::Point(tl_roi.roi.x_offset, tl_roi.roi.y_offset - (offset * 0)), cv::FONT_HERSHEY_COMPLEX, - 1.1, color, 3); + std::string shape_name = extractShapeName(result.label); - cv::putText( - image, result.label, cv::Point(tl_roi.roi.x_offset, tl_roi.roi.y_offset - (offset * 1)), - cv::FONT_HERSHEY_COMPLEX, 1.1, color, 2); + drawTrafficLightShape( + image, shape_name, cv::Point(tl_roi.roi.x_offset, tl_roi.roi.y_offset), color, 16, result.prob); return true; } diff --git a/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/shape_draw.cpp b/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/shape_draw.cpp new file mode 100644 index 0000000000000..91a00a940885c --- /dev/null +++ b/perception/traffic_light_visualization/src/traffic_light_roi_visualizer/shape_draw.cpp @@ -0,0 +1,173 @@ +// Copyright 2024 The Autoware Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "traffic_light_visualization/traffic_light_roi_visualizer/shape_draw.hpp" + +#include "opencv2/core/types.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" + +void drawShape( + const DrawFunctionParams & params, const std::string & filename, bool flipHorizontally, + bool flipVertically, int x_offset, int y_offset, double scale_factor) +{ + std::string filepath = + ament_index_cpp::get_package_share_directory("traffic_light_visualization") + "/images/" + + filename; + cv::Mat shapeImg = cv::imread(filepath, cv::IMREAD_UNCHANGED); + if (shapeImg.empty()) { + std::cerr << "Failed to load image: " << filepath << std::endl; + return; + } + + if (flipHorizontally) { + cv::flip(shapeImg, shapeImg, 1); // Flip horizontally + } + + if (flipVertically) { + cv::flip(shapeImg, shapeImg, 0); // Flip vertically + } + + cv::resize( + shapeImg, shapeImg, cv::Size(params.size, params.size), scale_factor, scale_factor, + cv::INTER_AREA); + + // Calculate the center position including offsets + cv::Point position( + params.position.x + x_offset, params.position.y - shapeImg.rows / 2 + y_offset); + + // Check for image boundaries + if ( + position.x < 0 || position.y < 0 || position.x + shapeImg.cols > params.image.cols || + position.y + shapeImg.rows > params.image.rows) { + std::cerr << "Adjusted position is out of image bounds." << std::endl; + return; + } + + // Calculate the width of the text + std::string probabilityText = + std::to_string(static_cast(round(params.probability * 100))) + "%"; + int baseline = 0; + cv::Size textSize = + cv::getTextSize(probabilityText, cv::FONT_HERSHEY_SIMPLEX, 0.75, 2, &baseline); + + // Adjust the filled rectangle to be at the top edge and the correct width + int filledRectWidth = + shapeImg.cols + (filename != "unknown.png" ? textSize.width + 10 : 5); // Add some padding + int filledRectHeight = shapeImg.rows + 10; // Add some padding + + cv::rectangle( + params.image, cv::Rect(position.x - 2, position.y - 5, filledRectWidth, filledRectHeight), + params.color, + -1); // Filled rectangle + + // Create ROI on the destination image + cv::Mat destinationROI = params.image(cv::Rect(position, cv::Size(shapeImg.cols, shapeImg.rows))); + + // Overlay the image onto the main image + for (int y = 0; y < shapeImg.rows; ++y) { + for (int x = 0; x < shapeImg.cols; ++x) { + cv::Vec4b & pixel = shapeImg.at(y, x); + if (pixel[3] != 0) { // Only non-transparent pixels + destinationROI.at(y, x) = cv::Vec3b(pixel[0], pixel[1], pixel[2]); + } + } + } + + // Position the probability text right next to the shape + if (filename != "unknown.png") { + cv::putText( + params.image, probabilityText, + cv::Point( + position.x + shapeImg.cols + 5, position.y + shapeImg.rows / 2 + textSize.height / 2), + cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 0), 2, cv::LINE_AA); + } +} + +void drawCircle(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + drawShape(params, "circle.png", false, false, 0, -y_offset); +} + +void drawLeftArrow(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + drawShape(params, "left_arrow.png", false, false, 0, -y_offset); +} + +void drawRightArrow(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + drawShape(params, "left_arrow.png", true, false, 0, -y_offset); +} + +void drawStraightArrow(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; // This adjusts the base position upwards + + drawShape(params, "straight_arrow.png", false, false, 0, -y_offset); +} +void drawDownArrow(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; // This adjusts the base position upwards + drawShape(params, "straight_arrow.png", false, true, 0, -y_offset); +} + +void drawDownLeftArrow(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + drawShape(params, "down_left_arrow.png", false, false, 0, -y_offset); +} + +void drawDownRightArrow(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + drawShape(params, "down_left_arrow.png", true, false, 0, -y_offset); +} + +void drawCross(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + + drawShape(params, "cross.png", false, false, 0, -y_offset); +} + +void drawUnknown(const DrawFunctionParams & params) +{ + int y_offset = params.size / 2 + 5; + drawShape(params, "unknown.png", false, false, 0, -y_offset); +} + +void drawTrafficLightShape( + cv::Mat & image, const std::string & shape, const cv::Point & position, const cv::Scalar & color, + int size, float probability) +{ + static std::map shapeToFunction = { + {"circle", drawCircle}, + {"left", drawLeftArrow}, + {"right", drawRightArrow}, + {"straight", drawStraightArrow}, + {"down", drawDownArrow}, + {"down_left", drawDownLeftArrow}, + {"down_right", drawDownRightArrow}, + {"cross", drawCross}, + {"unknown", drawUnknown}}; + auto it = shapeToFunction.find(shape); + if (it != shapeToFunction.end()) { + DrawFunctionParams params{image, position, color, size, probability}; + it->second(params); + } else { + std::cerr << "Unknown shape: " << shape << std::endl; + } +}