def __init__(self, racecar_info): """ To capture the video of evaluation done during the training phase Args: racecar_info (dict): Information of the agent """ self.racecar_info = racecar_info # Store the font which we will use to write the phase with self.training_phase_font = utils.get_font('Amazon_Ember_RgIt', 35) # Subscriber to get the phase of the training (Ideal, training, evaluation) rospy.Subscriber('/agent/training_phase', String, self._training_phase_cb_) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # # TODO: Currently dont have the gradient image for the training. So not using any gradient overlay on image # self.gradient_img = utils.get_image(TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG.value, # IconographicImageSize.FULL_IMAGE_SIZE.value) # self.gradient_alpha = self.gradient_img[:, :, 3] / 255.0 # # String indicating the current phase self._current_training_phase = 'Initializing' # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info)
def __init__(self, racecar_name, racecar_info, race_type): """ To capture the video of evaluation done during the training phase Args: racecar_info (dict): Information of the agent """ self.racecar_info = racecar_info racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 # Store the font which we will use to write the phase with self.training_phase_font = utils.get_font('Amazon_Ember_RgIt', 35) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value # Gradient overlay image width, height = IconographicImageSize.FULL_IMAGE_SIZE.value image = np.zeros(height * width * 4) image.resize(height, width, 4) self.gradient_img = self._plot_track_on_gradient(image) self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values( self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info)
def __init__(self, racecar_name, racecars_info, race_type): """ This class is used for head to head racing where there are more than one agent Args: racecar_name (str): The agent name with 45degree camera view racecars_info (dict): All the agents information race_type (str): The type of race. This is used to know if its race type or evaluation """ self.racecar_name = racecar_name self.racecars_info = racecars_info self.race_type = race_type # init cv bridge self.bridge = CvBridge() # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font( 'AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font( 'AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font( 'AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() gradient_img_path = TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG.value self.gradient_img = utils.get_image( gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) self.gradient_alpha = self.gradient_img[:, :, 3] / 255.0 self.mp4_video_metrics_srv_list = list() for racecar_info in self.racecars_info: agent_name = 'agent' if len( racecars_info) == 1 else "agent_{}".format( racecar_info['name'].split("_")[1]) rospy.wait_for_service("/{}/{}".format(agent_name, "mp4_video_metrics")) self.mp4_video_metrics_srv_list.append( ServiceProxyWrapper( "/{}/{}".format(agent_name, "mp4_video_metrics"), VideoMetricsSrv)) # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecars_info)
def __init__(self, racecar_name, racecars_info, race_type): """ This class is used for head to head racing where there are more than one agent Args: racecar_name (str): The agent name with 45degree camera view racecars_info (dict): All the agents information race_type (str): The type of race. This is used to know if its race type or evaluation """ self.racecar_name = racecar_name self.racecars_info = racecars_info racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 self.race_type = race_type # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font( 'AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font( 'AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font( 'AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") self._total_laps = int(rospy.get_param("NUMBER_OF_TRIALS", 0)) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value if self.is_league_leaderboard \ else XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value gradient_img_path = TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG.value self.gradient_img = self._plot_track_on_gradient(gradient_img_path) self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values( self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecars_info)
def __init__(self, racecar_name, racecar_info, race_type): """ Initializing the required data for the head to bot, time-trail. This is used for single agent Arguments: racecars_info (list): list of dict having information of the agent race_type (str): Since this class is reused for all the different race_type """ self.racecar_info = racecar_info self.race_type = race_type racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font( 'AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font( 'AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font( 'AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") self._total_laps = int(rospy.get_param("NUMBER_OF_TRIALS", 0)) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value if self.is_league_leaderboard \ else XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value # Gradient overlay image gradient_img_path = TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG.value self.gradient_img = self._plot_track_on_gradient(gradient_img_path) self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values( self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info)
def __init__(self, racecar_info, race_type): """ Initializing the required data for the head to bot, time-trail. This is used for single agent Arguments: racecars_info (list): list of dict having information of the agent race_type (str): Since this class is reused for all the different race_type """ self.racecar_info = racecar_info self.race_type = race_type # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font( 'AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font( 'AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font( 'AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() gradient_img_path = TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG.value self.gradient_img = utils.get_image( gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) self.gradient_alpha = self.gradient_img[:, :, 3] / 255.0 # Subscribing to the agent metrics rospy.wait_for_service("/agent/mp4_video_metrics") self.mp4_video_metrics_srv = ServiceProxyWrapper( "/agent/mp4_video_metrics", VideoMetricsSrv) # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info)
class TrainingImageEditing(ImageEditingInterface): """ Editing the image in training phase """ def __init__(self, racecar_name, racecar_info, race_type): """ To capture the video of evaluation done during the training phase Args: racecar_info (dict): Information of the agent """ self.racecar_info = racecar_info racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 # Store the font which we will use to write the phase with self.training_phase_font = utils.get_font('Amazon_Ember_RgIt', 35) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value # Gradient overlay image width, height = IconographicImageSize.FULL_IMAGE_SIZE.value image = np.zeros(height * width * 4) image.resize(height, width, 4) self.gradient_img = self._plot_track_on_gradient(image) self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values( self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info) def _edit_major_cv_image(self, major_cv_image, cur_training_phase): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera Returns: Image: Edited main camera image """ # Applying gradient to whole major image and then writing text major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha) # Add the label that lets the user know the training phase major_cv_image = utils.write_text_on_image( image=major_cv_image, text=cur_training_phase, loc=XYPixelLoc.TRAINING_PHASE_LOC.value, font=self.training_phase_font, font_color=None, font_shadow_color=RaceCarColorToRGB.Black.value) major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA) return major_cv_image def _plot_track_on_gradient(self, gradient_img): """ For the given gradient apply the track iconographic image and use this to apply gradient on each camera frame. Previously this was done on the top camera which changed every frame. But with the track iconographic image set static, adding the track on gradient is more optimized. Arguments: gradient_img (str): Gradient image Returns: (Image): Edited gradient image with track image """ track_icongraphy_scaled = utils.resize_image(self.track_icongraphy_img, SCALE_RATIO) track_icongraphy_alpha = track_icongraphy_scaled[:, :, 3] / 255.0 # Track image is placed at the bottom right with some offset (only in leaderboard tracks) x_min = -(self.track_loc_offset[1] + track_icongraphy_scaled.shape[0]) x_max = gradient_img.shape[0] - self.track_loc_offset[1] y_min = -(self.track_loc_offset[0] + track_icongraphy_scaled.shape[1]) y_max = gradient_img.shape[1] - self.track_loc_offset[0] # This is used as the offset for plotting the agent dots self.track_start_loc = (gradient_img.shape[1] + y_min, gradient_img.shape[0] + x_min) for channel in range(0, 4): gradient_img[x_min:x_max, y_min:y_max, channel] =\ (track_icongraphy_alpha * track_icongraphy_scaled[:, :, channel]) + \ (1 - track_icongraphy_alpha) * (gradient_img[x_min:x_max, y_min:y_max, channel]) return gradient_img def _plot_agents_on_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Add the agents, obstacles on the track. Arguments: major_cv_image (Image): Edited image having gradient, text, track mp4_video_metrics_info (List): List of ROS metric values of each agent Returns: Image: Edited image of the iconographic view of the top camera """ agents_loc = [(metric.x, metric.y) for metric in mp4_video_metrics_info] objects_loc = [] if mp4_video_metrics_info[0].object_locations: objects_loc = [ (object_loc.x, object_loc.y) for object_loc in mp4_video_metrics_info[0].object_locations ] return self.top_view_graphics.plot_agents_as_circles( major_cv_image, agents_loc, objects_loc, self.track_start_loc) def edit_image(self, major_cv_image, metric_info): mp4_video_metrics_info = metric_info[ FrameQueueData.AGENT_METRIC_INFO.value] cur_training_phase = metric_info[FrameQueueData.TRAINING_PHASE.value] major_cv_image = self._edit_major_cv_image(major_cv_image, cur_training_phase) major_cv_image = self._plot_agents_on_major_cv_image( major_cv_image, mp4_video_metrics_info) return cv2.cvtColor(major_cv_image, cv2.COLOR_BGRA2RGB)
class MultiAgentImageEditing(ImageEditingInterface): """Image editing class for head to head where there are multiple agents """ def __init__(self, racecar_name, racecars_info, race_type): """ This class is used for head to head racing where there are more than one agent Args: racecar_name (str): The agent name with 45degree camera view racecars_info (dict): All the agents information race_type (str): The type of race. This is used to know if its race type or evaluation """ self.racecar_name = racecar_name self.racecars_info = racecars_info self.race_type = race_type # init cv bridge self.bridge = CvBridge() # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font( 'AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font( 'AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font( 'AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() gradient_img_path = TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG.value self.gradient_img = utils.get_image( gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) self.gradient_alpha = self.gradient_img[:, :, 3] / 255.0 self.mp4_video_metrics_srv_list = list() for racecar_info in self.racecars_info: agent_name = 'agent' if len( racecars_info) == 1 else "agent_{}".format( racecar_info['name'].split("_")[1]) rospy.wait_for_service("/{}/{}".format(agent_name, "mp4_video_metrics")) self.mp4_video_metrics_srv_list.append( ServiceProxyWrapper( "/{}/{}".format(agent_name, "mp4_video_metrics"), VideoMetricsSrv)) # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecars_info) def _edit_major_cv_image(self, major_cv_image): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera Returns: Image: Edited main camera image """ # Applying gradient to whole major image and then writing text major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_img, self.gradient_alpha) # Subscribing to the agent metrics mp4_video_metrics_info = list() for racecar_info, mp4_video_metrics_srv in zip( self.racecars_info, self.mp4_video_metrics_srv_list): mp4_video_metrics = mp4_video_metrics_srv(VideoMetricsSrvRequest()) mp4_video_metrics_info.append(mp4_video_metrics) # Adding display name to the image agents_speed = 0 agent_done = False for i, racecar_info in enumerate(self.racecars_info): loc_x, loc_y = XYPixelLoc.MULTI_AGENT_DISPLAY_NAME_LOC.value[i][ 0], XYPixelLoc.MULTI_AGENT_DISPLAY_NAME_LOC.value[i][1] # Display name (Racer name/Model name) display_name = racecar_info['display_name'] display_name_txt = display_name if len( display_name) < 15 else "{}...".format(display_name[:15]) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=display_name_txt, loc=(loc_x, loc_y), font=self.amazon_ember_regular_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Lap Counter loc_y += 30 total_laps = rospy.get_param("NUMBER_OF_TRIALS", 0) current_lap = int(mp4_video_metrics_info[i].lap_counter) + 1 lap_counter_text = "{}/{}".format(current_lap, total_laps) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=lap_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_heavy_30px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Reset counter loc_y += 45 reset_counter_text = "Reset | {}".format( mp4_video_metrics_info[i].reset_counter) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=reset_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_18px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) if self.racecar_name == racecar_info['name']: agents_speed = mp4_video_metrics_info[i].throttle # The race is complete when total lap is same as current lap and done flag is set agent_done = agent_done or (mp4_video_metrics_info[i].done and (current_lap == int(total_laps))) # Speed loc_x, loc_y = XYPixelLoc.SPEED_EVAL_LOC.value if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.SPEED_LEADERBOARD_LOC.value speed_text = "{} m/s".format( utils.get_speed_formatted_str(agents_speed)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=speed_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Leaderboard name if self.is_league_leaderboard: major_cv_image = utils.write_text_on_image( image=major_cv_image, text=self.leaderboard_name, loc=XYPixelLoc.LEADERBOARD_NAME_LOC.value, font=self.amazon_ember_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Evaluation type loc_x, loc_y = XYPixelLoc.RACE_TYPE_EVAL_LOC.value if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.RACE_TYPE_RACE_LOC.value race_text = "race" if self.is_racing else "evaluation" evaluation_type_txt = "{} {}".format( RACE_TYPE_TO_VIDEO_TEXT_MAPPING[self.race_type], race_text) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=evaluation_type_txt, loc=(loc_x, loc_y), font=self.amazon_ember_light_italic_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # total_evaluation_time (Race time) loc_x, loc_y = XYPixelLoc.MULTI_AGENT_EVAL_TIME.value total_eval_milli_seconds = mp4_video_metrics_info[ 0].total_evaluation_time time_delta = datetime.timedelta(milliseconds=total_eval_milli_seconds) total_eval_time_text = "Race | {}".format( utils.milliseconds_to_timeformat(time_delta)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=total_eval_time_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_18px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # AWS Deepracer logo at the bottom for the community leaderboard if self.is_league_leaderboard: major_cv_image = utils.write_text_on_image( image=major_cv_image, text=AWS_DEEPRACER_WATER_MARK, loc=XYPixelLoc.AWS_DEEPRACER_WATER_MARK_LOC.value, font=self.amazon_ember_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Check if the done flag is set and set the banner appropriately if agent_done: # When the cv2 text is written, it automatically drops the alpha value of the image rel_y_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value[ 1] if self.is_league_leaderboard else 0 major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2RGBA) racecomplete_image = utils.get_image( TrackAssetsIconographicPngs.RACE_COMPLETE_OVERLAY_PNG.value, IconographicImageSize.RACE_COMPLETE_IMAGE_SIZE.value) x_offset = major_cv_image.shape[ 1] - racecomplete_image.shape[1] // 2 y_offset = major_cv_image.shape[ 0] - RACE_COMPLETE_Y_OFFSET - rel_y_offset - racecomplete_image.shape[ 0] // 2 major_cv_image = utils.plot_rectangular_image_on_main_image( major_cv_image, racecomplete_image, (x_offset, y_offset)) return major_cv_image def _edit_minor_cv_image(self): """ Apply all the editing for the iconographic of the top view camera image Returns: Image: Edited image of the iconographic view of the top camera """ # Edit the top camera image to have circles instead of dots return self.top_view_graphics.plot_agents_as_circles( self.track_icongraphy_img.copy()) def edit_image(self, major_cv_image): major_cv_image = self._edit_major_cv_image(major_cv_image) minor_cv_image = self._edit_minor_cv_image() offset_loc = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value if self.is_league_leaderboard \ else XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value return utils.overlay_track_images(major_cv_image, minor_cv_image, offset_loc)
class SingleAgentImageEditing(ImageEditingInterface): """ Image editing class for head to bot, time-trail, obstacle where there is only single agent """ def __init__(self, racecar_info, race_type): """ Initializing the required data for the head to bot, time-trail. This is used for single agent Arguments: racecars_info (list): list of dict having information of the agent race_type (str): Since this class is reused for all the different race_type """ self.racecar_info = racecar_info self.race_type = race_type # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font( 'AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font( 'AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font( 'AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() gradient_img_path = TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG.value self.gradient_img = utils.get_image( gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) self.gradient_alpha = self.gradient_img[:, :, 3] / 255.0 # Subscribing to the agent metrics rospy.wait_for_service("/agent/mp4_video_metrics") self.mp4_video_metrics_srv = ServiceProxyWrapper( "/agent/mp4_video_metrics", VideoMetricsSrv) # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info) def _edit_major_cv_image(self, major_cv_image): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera Returns: Image: Edited main camera image """ # Applying gradient to whole major image and then writing text major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_img, self.gradient_alpha) mp4_video_metrics_info = self.mp4_video_metrics_srv( VideoMetricsSrvRequest()) # Top left location of the picture loc_x, loc_y = XYPixelLoc.SINGLE_AGENT_DISPLAY_NAME_LOC.value # Display name (Racer name/Model name) display_name = self.racecar_info['display_name'] display_name_txt = display_name if len( display_name) < 15 else "{}...".format(display_name[:15]) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=display_name_txt, loc=(loc_x, loc_y), font=self.amazon_ember_regular_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Lap Counter loc_y += 30 total_laps = rospy.get_param("NUMBER_OF_TRIALS", 0) current_lap = int(mp4_video_metrics_info.lap_counter) + 1 lap_counter_text = "{}/{}".format(current_lap, total_laps) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=lap_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_heavy_30px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # total_evaluation_time (Race time) loc_y += 45 total_eval_milli_seconds = mp4_video_metrics_info.total_evaluation_time time_delta = datetime.timedelta(milliseconds=total_eval_milli_seconds) total_eval_time_text = "Race | {}".format( utils.milliseconds_to_timeformat(time_delta)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=total_eval_time_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_18px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Reset counter loc_y += 25 reset_counter_text = "Reset | {}".format( mp4_video_metrics_info.reset_counter) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=reset_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_18px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Speed loc_x, loc_y = XYPixelLoc.SPEED_EVAL_LOC.value if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.SPEED_LEADERBOARD_LOC.value speed_text = "{} m/s".format( utils.get_speed_formatted_str(mp4_video_metrics_info.throttle)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=speed_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Leaderboard name if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.LEADERBOARD_NAME_LOC.value major_cv_image = utils.write_text_on_image( image=major_cv_image, text=self.leaderboard_name, loc=(loc_x, loc_y), font=self.amazon_ember_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Evaluation type loc_x, loc_y = XYPixelLoc.RACE_TYPE_EVAL_LOC.value if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.RACE_TYPE_RACE_LOC.value race_text = "race" if self.is_racing else "evaluation" evaluation_type_txt = "{} {}".format( RACE_TYPE_TO_VIDEO_TEXT_MAPPING[self.race_type], race_text) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=evaluation_type_txt, loc=(loc_x, loc_y), font=self.amazon_ember_light_italic_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # AWS Deepracer logo at the bottom for the community leaderboard if self.is_league_leaderboard: major_cv_image = utils.write_text_on_image( image=major_cv_image, text=AWS_DEEPRACER_WATER_MARK, loc=XYPixelLoc.AWS_DEEPRACER_WATER_MARK_LOC.value, font=self.amazon_ember_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Check if the done flag is set and set the banner appropriately if mp4_video_metrics_info.done and (int(total_laps) == current_lap): # When the cv2 text is written, it automatically drops the alpha value of the image rel_y_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value[ 1] if self.is_league_leaderboard else 0 major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2RGBA) racecomplete_image = utils.get_image( TrackAssetsIconographicPngs.RACE_COMPLETE_OVERLAY_PNG.value, IconographicImageSize.RACE_COMPLETE_IMAGE_SIZE.value) x_offset = major_cv_image.shape[ 1] - racecomplete_image.shape[1] // 2 y_offset = major_cv_image.shape[ 0] - RACE_COMPLETE_Y_OFFSET - rel_y_offset - racecomplete_image.shape[ 0] // 2 major_cv_image = utils.plot_rectangular_image_on_main_image( major_cv_image, racecomplete_image, (x_offset, y_offset)) return major_cv_image def _edit_minor_cv_image(self): """ Apply all the editing for the iconographic of the top view camera image Returns: Image: Edited image of the iconographic view of the top camera """ return self.top_view_graphics.plot_agents_as_circles( self.track_icongraphy_img.copy()) def edit_image(self, major_cv_image): major_cv_image = self._edit_major_cv_image(major_cv_image) minor_cv_image = self._edit_minor_cv_image() offset_loc = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value if self.is_league_leaderboard \ else XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value return utils.overlay_track_images(major_cv_image, minor_cv_image, offset_loc)
def __init__(self, racecar_name, racecars_info, race_type): """ This class is used for head to head racing where there are more than one agent Args: racecar_name (str): The agent name with 45degree camera view racecars_info (dict): All the agents information race_type (str): The type of race. This is used to know if its race type or evaluation """ self.racecar_name = racecar_name self.racecars_info = racecars_info racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 self.race_type = race_type # Store the font which we will use to write the phase with self.formula1_display_regular_12px = utils.get_font( 'Formula1-Display-Regular', 12) self.formula1_display_regular_14px = utils.get_font( 'Formula1-Display-Regular', 14) self.formula1_display_regular_16px = utils.get_font( 'Formula1-Display-Regular', 16) self.formula1_display_wide_12px = utils.get_font( 'Formula1-Display-Wide', 12) self.formula1_display_bold_16px = utils.get_font( 'Formula1-Display-Bold', 16) self.total_laps = int(rospy.get_param("NUMBER_OF_TRIALS", 0)) self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value # Default image of top view gradient_default_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_DEFAULT_PNG.value self.gradient_default_img = self._plot_track_on_gradient( gradient_default_img_path) self.gradient_default_alpha_rgb_mul, self.one_minus_gradient_default_alpha = utils.get_gradient_values( self.gradient_default_img) # Midway track gradient gradient_midway_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_MIDWAY_PNG.value self.gradient_midway_img = self._plot_track_on_gradient( gradient_midway_img_path) self.gradient_midway_alpha_rgb_mul, self.one_minus_gradient_midway_alpha = utils.get_gradient_values( self.gradient_midway_img) # Finisher track gradient gradient_finisher_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_FINISHERS_PNG.value self.gradient_finisher_img = self._plot_track_on_gradient( gradient_finisher_img_path) self.gradient_finisher_alpha_rgb_mul, self.one_minus_gradient_finisher_alpha = utils.get_gradient_values( self.gradient_finisher_img) # Top camera gradient num_racers = len(self.racecars_info) if num_racers <= 8: # TODO: Add one box image and use 1 box image if number of racers are <= 4. gradient_top_camera_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_TOPVIEW_2BOX_PNG.value elif num_racers <= 12: gradient_top_camera_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_TOPVIEW_3BOX_PNG.value else: raise Exception( "More than 12 racers are not supported for Grand Prix") gradient_top_camera_img = utils.get_image( gradient_top_camera_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) gradient_top_camera_img = cv2.cvtColor(gradient_top_camera_img, cv2.COLOR_RGBA2BGRA) self.gradient_top_camera_alpha_rgb_mul, self.one_minus_gradient_top_camera_alpha = utils.get_gradient_values( gradient_top_camera_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecars_info, race_type) self.hex_car_colors = [ val['racecar_color'].split('_')[-1] for val in racecars_info ] self._racer_color_code_rect_img = list() self._racer_color_code_slash_img = list() for car_color in self.hex_car_colors: # Rectangular png of racers racer_color_code_rect = "{}_{}".format( TrackAssetsIconographicPngs.F1_AGENTS_RECT_DISPLAY_ICON_PNG. value, car_color) self._racer_color_code_rect_img.append( utils.get_image( racer_color_code_rect, IconographicImageSize. F1_RACER_RECT_DISPLAY_ICON_SIZE.value)) # Slash png of racers racer_color_code_slash = "{}_{}".format( TrackAssetsIconographicPngs.F1_AGENTS_SLASH_DISPLAY_ICON_PNG. value, car_color) racer_color_code_slash_img = utils.get_image( racer_color_code_slash, IconographicImageSize.F1_RACER_SLASH_DISPLAY_ICON_SIZE.value) self._racer_color_code_slash_img.append( cv2.cvtColor(racer_color_code_slash_img, cv2.COLOR_RGBA2BGRA))
class F1ImageEditing(ImageEditingInterface): """Image editing class for F1 grand prix """ _lock = threading.Lock() _finished_lap_time = OrderedDict() _leader_percentage_completion = list() _leader_elapsed_time = list() def __init__(self, racecar_name, racecars_info, race_type): """ This class is used for head to head racing where there are more than one agent Args: racecar_name (str): The agent name with 45degree camera view racecars_info (dict): All the agents information race_type (str): The type of race. This is used to know if its race type or evaluation """ self.racecar_name = racecar_name self.racecars_info = racecars_info racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 self.race_type = race_type # Store the font which we will use to write the phase with self.formula1_display_regular_12px = utils.get_font( 'Formula1-Display-Regular', 12) self.formula1_display_regular_14px = utils.get_font( 'Formula1-Display-Regular', 14) self.formula1_display_regular_16px = utils.get_font( 'Formula1-Display-Regular', 16) self.formula1_display_wide_12px = utils.get_font( 'Formula1-Display-Wide', 12) self.formula1_display_bold_16px = utils.get_font( 'Formula1-Display-Bold', 16) self.total_laps = int(rospy.get_param("NUMBER_OF_TRIALS", 0)) self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value # Default image of top view gradient_default_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_DEFAULT_PNG.value self.gradient_default_img = self._plot_track_on_gradient( gradient_default_img_path) self.gradient_default_alpha_rgb_mul, self.one_minus_gradient_default_alpha = utils.get_gradient_values( self.gradient_default_img) # Midway track gradient gradient_midway_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_MIDWAY_PNG.value self.gradient_midway_img = self._plot_track_on_gradient( gradient_midway_img_path) self.gradient_midway_alpha_rgb_mul, self.one_minus_gradient_midway_alpha = utils.get_gradient_values( self.gradient_midway_img) # Finisher track gradient gradient_finisher_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_FINISHERS_PNG.value self.gradient_finisher_img = self._plot_track_on_gradient( gradient_finisher_img_path) self.gradient_finisher_alpha_rgb_mul, self.one_minus_gradient_finisher_alpha = utils.get_gradient_values( self.gradient_finisher_img) # Top camera gradient num_racers = len(self.racecars_info) if num_racers <= 8: # TODO: Add one box image and use 1 box image if number of racers are <= 4. gradient_top_camera_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_TOPVIEW_2BOX_PNG.value elif num_racers <= 12: gradient_top_camera_img_path = TrackAssetsIconographicPngs.F1_OVERLAY_TOPVIEW_3BOX_PNG.value else: raise Exception( "More than 12 racers are not supported for Grand Prix") gradient_top_camera_img = utils.get_image( gradient_top_camera_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) gradient_top_camera_img = cv2.cvtColor(gradient_top_camera_img, cv2.COLOR_RGBA2BGRA) self.gradient_top_camera_alpha_rgb_mul, self.one_minus_gradient_top_camera_alpha = utils.get_gradient_values( gradient_top_camera_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecars_info, race_type) self.hex_car_colors = [ val['racecar_color'].split('_')[-1] for val in racecars_info ] self._racer_color_code_rect_img = list() self._racer_color_code_slash_img = list() for car_color in self.hex_car_colors: # Rectangular png of racers racer_color_code_rect = "{}_{}".format( TrackAssetsIconographicPngs.F1_AGENTS_RECT_DISPLAY_ICON_PNG. value, car_color) self._racer_color_code_rect_img.append( utils.get_image( racer_color_code_rect, IconographicImageSize. F1_RACER_RECT_DISPLAY_ICON_SIZE.value)) # Slash png of racers racer_color_code_slash = "{}_{}".format( TrackAssetsIconographicPngs.F1_AGENTS_SLASH_DISPLAY_ICON_PNG. value, car_color) racer_color_code_slash_img = utils.get_image( racer_color_code_slash, IconographicImageSize.F1_RACER_SLASH_DISPLAY_ICON_SIZE.value) self._racer_color_code_slash_img.append( cv2.cvtColor(racer_color_code_slash_img, cv2.COLOR_RGBA2BGRA)) def _get_racecar_ranking(self, racers_ranking, racer_number): """ Returns the rank of the racer given the racing number. This function considers the cars finished and uses the elapsed time for giving the ranking. Arguments: racers_ranking (OrderedDict): Sorted ordered dict with racer_number and progress percentage along with lap racer_number (int): This is the racers number assigned when kinesis video node is spwan Returns: (int): The rank of the racer """ if racer_number in racers_ranking: rank = list(racers_ranking.keys()).index(racer_number) + 1 # The difference between self.racecars_info and racers_ranking will give # the number of racers completed the race at given time frame. # Relying on self._finished_lap_time for the ranking is a bad idea as given racers_ranking may not # be from current time (can be from queued frame which is from the past), # where self._finished_lap_time always contains the latest information. rank += len(self.racecars_info) - len(racers_ranking) return rank else: # If racer_number is not in racers_ranking then given racer_number racer at the given time frame # has already completed the race, then it must rely on self._finished_lap_time to # retrieve its ranking as racers_ranking won't contain the given racer_number and # self._finished_lap_time is the ONLY source to retrieve its ranking. return list(self._finished_lap_time.keys()).index(racer_number) + 1 def _get_gap_time(self, racers_ranking, racer_number, racer_metrics_info): """ Checks if a car has complete the race and considers the difference in the elapsed time as gap time. If no racers have completed the lap then this function considers _leader_percentage_completion which tracks all the data points of the leaders progress to the elapsed time. Arguments: racers_ranking (OrderedDict): Sorted ordered dict with racer_number and progress percentage along with lap racer_number (int): This is the racers number assigned when kinesis video node is spwan racer_metrics_info (dict): Given racers metric information """ if len(racers_ranking) < len(self.racecars_info): # if size of racers_ranking is smaller than size of self.racecars_info, then # it means there is at least one racer completed the race. leader_elapsed_time = list(self._finished_lap_time.items())[0][1] if racer_number not in racers_ranking: # If given racer has been also completed the race then the gap time is # just difference between the race finish times of given racer and leader. # Otherwise, the gap time needs to be retrieved from interpolation estimation. return (self._finished_lap_time[racer_number] - leader_elapsed_time) / 1000 # Once leader completed the race, it's unnecessary to update # self._leader_elapsed_time and self._leader_percentage_completion lists. # Thus, set race_leader_index to None to avoid updating these static lists. race_leader_index = None else: # If none of the racers completed the race then every progress and time of the leader # needs to be recorded. race_leader_index = list(racers_ranking.items())[0][0] with self._lock: try: if race_leader_index is not None: if len(self._leader_elapsed_time) == 0 or \ (racer_metrics_info.total_evaluation_time > self._leader_elapsed_time[-1] and racers_ranking[race_leader_index] > self._leader_percentage_completion[-1]): # ONLY append leader's datapoint when there is an actual latest new datapoint. self._leader_percentage_completion.append( racers_ranking[race_leader_index]) # Since the total evaluation time is same for all the racers self._leader_elapsed_time.append( racer_metrics_info.total_evaluation_time) if racers_ranking[racer_number] < 0.0: # If the progress of the racer is less than 0 then it means racer hasn't passed the start line yet. # In such case, the gap time based on leader's elapsed time is meaningless. # Thus, just return 0. return 0 leader_elapsed_time = np.interp( racers_ranking[racer_number], self._leader_percentage_completion, self._leader_elapsed_time) return (racer_metrics_info.total_evaluation_time - leader_elapsed_time) / 1000 except Exception as ex: LOG.info( "Failed to find race leaders elapsed time: {}".format(ex)) return 0 def _racers_rank_name_gap_time(self, racers_ranking, mp4_video_metrics_info): """ Gets all the racers information rank, name, gap time. This is used for showing duirng the halfway mark and finished lap. This is used for the top view camera editing. Arguments: racers_ranking (OrderedDict): Sorted ordered dict with racer_number and progress percentage along with lap mp4_video_metrics_info (list): All the racers metric information Returns: (list): Sorted list based on the ranking along with rank, name, gap time, racer_number """ rank_name_gap_time = list() for i in range(len(self.racecars_info)): racer_number = int(self.racecars_info[i]['name'].split("_")[1]) rank_name_gap_time.append([ self._get_racecar_ranking(racers_ranking, racer_number), self.racecars_info[i]['display_name'], self._get_gap_time(racers_ranking, racer_number, mp4_video_metrics_info[racer_number]), racer_number ]) return sorted(rank_name_gap_time, key=lambda item: item[0]) def _default_edit(self, major_cv_image): """ This is used as a default edit. Arguments: major_cv_image (Image): Main camera image for the racecar Returns: major_cv_image (Image): Edited Main camera image """ # Applying gradient to whole major image and then writing text # F1 logo at the top left # loc_x, loc_y = XYPixelLoc.F1_LOGO_LOC.value # f1_logo_image = utils.get_image(TrackAssetsIconographicPngs.F1_LOGO_PNG.value, # IconographicImageSize.F1_LOGO_IMAGE_SIZE.value) # major_cv_image = utils.plot_rectangular_image_on_main_image(major_cv_image, f1_logo_image, (loc_x, loc_y)) major_cv_image = utils.apply_gradient( major_cv_image, self.gradient_default_alpha_rgb_mul, self.one_minus_gradient_default_alpha) return major_cv_image def _basic_racer_info_display(self, major_cv_image, racers_ranking, cur_racer_metrics_info, show_racer_name=True): """ Basic display editting of the main camera following the car. All the information at the bottom left of the MP4 is edited here. Arguments: major_cv_image (Image): Main camera image for the racecar racers_ranking (OrderedDict): Sorted ordered dict with racer_number and progress percentage along with lap cur_racer_metrics_info (dict): Given racers metric information Keyword Arguments: show_racer_name (bool): All the other states call this. But the finisher state does not have enough space to show the display name.(default: {True}) Returns: major_cv_image (Image): Edited Main camera image """ if show_racer_name: # Adding display name to the image loc_x, loc_y = XYPixelLoc.F1_DISPLAY_NAME_LOC.value display_name = self.racecars_info[ self.racecar_index]['display_name'] display_name_txt = display_name if len( display_name) <= 6 else "{}".format(display_name[:6]) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=display_name_txt, loc=(loc_x, loc_y), font=self.formula1_display_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Racers ranking loc_x, loc_y = XYPixelLoc.F1_RANKING_LOC.value cur_rank = self._get_racecar_ranking(racers_ranking, self.racecar_index) rank_txt = "Rank {}/{}".format(cur_rank, len(self.racecars_info)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=rank_txt, loc=(loc_x, loc_y), font=self.formula1_display_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Lap Counter|Total eval|gap time # Lap counter calculation loc_x, loc_y = XYPixelLoc.F1_LAP_EVAL_GAP_LOC.value current_lap = min( int(cur_racer_metrics_info.lap_counter) + 1, int(self.total_laps)) lap_counter_text = "Lap {:2d} / {:2d}".format(current_lap, int(self.total_laps)) # Total eval time if self.racecar_index in self._finished_lap_time: # If the racer finished the race then lock the total evaluation time to finished lap time. total_eval_milli_seconds = self._finished_lap_time[ self.racecar_index] else: total_eval_milli_seconds = cur_racer_metrics_info.total_evaluation_time time_delta = datetime.timedelta(milliseconds=total_eval_milli_seconds) total_eval_time_text = "{}".format( utils.milliseconds_to_timeformat(time_delta)) # Writing to the frame (Lap Counter|Total eval|gap time) lap_elapsed_gap_time = "{} | {} | Gap ".format(lap_counter_text, total_eval_time_text) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=lap_elapsed_gap_time, loc=(loc_x, loc_y), font=self.formula1_display_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Gap (In orange color) loc_x, loc_y = XYPixelLoc.F1_LAP_EVAL_GAP_VAL_LOC.value # For the racer ranking 0, we see small floating number for the gap. Hence making it zero if cur_rank == 1: gap_time = 0 gap_time = self._get_gap_time(racers_ranking, self.racecar_index, cur_racer_metrics_info) gap_time_text = "+{:.3f}".format(gap_time) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=gap_time_text, loc=(loc_x, loc_y), font=self.formula1_display_regular_16px, font_color=RaceCarColorToRGB.Orange.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Leaderboard name f1_water_mark_text = "{}".format(self.leaderboard_name) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=f1_water_mark_text, loc=XYPixelLoc.F1_LEADERBOARD_NAME_LOC.value, font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Do all plotting of images at the end if show_racer_name: # Draw racer color code loc_x, loc_y = XYPixelLoc.F1_DISPLAY_NAME_SLASH_LOC.value major_cv_image = utils.plot_rectangular_image_on_main_image( major_cv_image, self._racer_color_code_slash_img[self.racecar_index], (loc_x, loc_y)) major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA) return major_cv_image def _midway_racers_progress_display(self, major_cv_image, rank_name_gap_time, mp4_video_metrics_info): """ Half way of the track we show the stats of the racers with there ranks and gap time Arguments: major_cv_image (Image): Main camera image for the racecar rank_name_gap_time (list): Sorted list based on the ranking along with rank, name, gap time, racer_number mp4_video_metrics_info (list): All the racers metric information Returns: major_cv_image (Image): Edited Main camera image """ # Applying gradient to whole major image and then writing text major_cv_image = utils.apply_gradient( major_cv_image, self.gradient_midway_alpha_rgb_mul, self.one_minus_gradient_midway_alpha) major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_BGR2RGBA) # LAP Title loc_x, loc_y = XYPixelLoc.F1_MIDWAY_LAP_TEXT_LOC.value lap_txt = "LAP" major_cv_image = utils.write_text_on_image( image=major_cv_image, text=lap_txt, loc=(loc_x, loc_y), font=self.formula1_display_wide_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # LAP counter loc_x, loc_y = XYPixelLoc.F1_MIDWAY_LAP_COUNTER_LOC.value current_lap = min( int(mp4_video_metrics_info[self.racecar_index].lap_counter) + 1, int(self.total_laps)) lap_counter_text = "{} / {}".format(current_lap, int(self.total_laps)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=lap_counter_text, loc=(loc_x, loc_y), font=self.formula1_display_bold_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Each racers ranking, name and gap time rank_loc_x, rank_loc_y = XYPixelLoc.F1_MIDWAY_LEADER_RANK_LOC.value color_code_loc_x, color_code_loc_y = XYPixelLoc.F1_MIDWAY_LEADER_COLOR_CODE_LOC.value display_name_loc_x, display_name_loc_y = XYPixelLoc.F1_MIDWAY_LEADER_DISPLAY_NAME_LOC.value gap_loc_x, gap_loc_y = XYPixelLoc.F1_MIDWAY_LEADER_GAP_LOC.value # All the other racers name and gap for i, val in enumerate(rank_name_gap_time): rank, display_name, gap_time, racer_number = val # Rank racer_rank = "{}".format(rank) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=racer_rank, loc=(rank_loc_x, rank_loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Draw racer color code icon major_cv_image = utils.plot_rectangular_image_on_main_image( major_cv_image, self._racer_color_code_rect_img[racer_number], (color_code_loc_x, color_code_loc_y)) # Adding display name to the table display_name_txt = display_name if len( display_name) <= 6 else "{}".format(display_name[:6]) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=display_name_txt, loc=(display_name_loc_x, display_name_loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Adding gap to the table (Do not write gap for leader) if i: gap_time_text = "+{:.3f}".format(gap_time) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=gap_time_text, loc=(gap_loc_x, gap_loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) rank_loc_y += 20 color_code_loc_y += 20 display_name_loc_y += 20 gap_loc_y += 20 return cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA) def _race_finish_display(self, major_cv_image, rank_name_gap_time): """ This displays the stats of all the racers who have finished the lap. When the car finishes the lap the finisher racers get added into this list. Arguments: major_cv_image (Image): Main camera image for the racecar rank_name_gap_time (list): Sorted list based on the ranking along with rank, name, gap time, racer_number Returns: major_cv_image (Image): Edited Main camera image """ # Applying gradient to whole major image and then writing text major_cv_image = utils.apply_gradient( major_cv_image, self.gradient_finisher_alpha_rgb_mul, self.one_minus_gradient_finisher_alpha) major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_BGR2RGBA) # F1 logo # loc_x, loc_y = XYPixelLoc.F1_LOGO_LOC.value # f1_logo_image = utils.get_image(TrackAssetsIconographicPngs.F1_LOGO_PNG.value, # IconographicImageSize.F1_LOGO_IMAGE_SIZE.value, # is_rgb=True) # major_cv_image = utils.plot_rectangular_image_on_main_image(major_cv_image, f1_logo_image, (loc_x, loc_y)) # Finish Title loc_x, loc_y = XYPixelLoc.F1_FINISHED_TITLE_LOC.value finisher_txt = "Finishers" major_cv_image = utils.write_text_on_image( image=major_cv_image, text=finisher_txt, loc=(loc_x, loc_y), font=self.formula1_display_bold_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) rank_loc_x, rank_loc_y = XYPixelLoc.F1_FINISHED_RANK_LOC.value color_code_loc_x, color_code_loc_y = XYPixelLoc.F1_FINISHED_COLOR_CODE_LOC.value display_name_loc_x, display_name_loc_y = XYPixelLoc.F1_FINISHED_DISPLAY_NAME_LOC.value for rank, display_name, _, racer_number in rank_name_gap_time: if racer_number not in self._finished_lap_time: continue # All racers name and gap racer_rank = "{}".format(rank) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=racer_rank, loc=(rank_loc_x, rank_loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Draw racer color code icon major_cv_image = utils.plot_rectangular_image_on_main_image( major_cv_image, self._racer_color_code_rect_img[racer_number], (color_code_loc_x, color_code_loc_y)) # Adding display name to the table display_name_txt = display_name if len( display_name) <= 6 else "{}".format(display_name[:6]) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=display_name_txt, loc=(display_name_loc_x, display_name_loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) rank_loc_y += 20 color_code_loc_y += 20 display_name_loc_y += 20 return cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA) def _get_racers_metric_info(self, mp4_video_metrics_info): racers_ranking = OrderedDict() is_finish_lap_time_changed = False for i in range(len(self.racecars_info)): racer_number = int(self.racecars_info[i]['name'].split("_")[1]) mp4_video_metrics = mp4_video_metrics_info[racer_number] if mp4_video_metrics.done and mp4_video_metrics.lap_counter + 1 >= int( self.total_laps): if racer_number not in self._finished_lap_time: self._finished_lap_time[ racer_number] = mp4_video_metrics.total_evaluation_time is_finish_lap_time_changed = True else: racers_ranking[racer_number] = mp4_video_metrics.completion_percentage / 100.0 +\ mp4_video_metrics.lap_counter if is_finish_lap_time_changed: # Re-order the self._finished_lap_time ONLY if there is any information updated. self._finished_lap_time = OrderedDict( sorted(self._finished_lap_time.items(), key=lambda item: item[1])) racers_ranking = OrderedDict( sorted(racers_ranking.items(), key=lambda item: item[1], reverse=True)) rank_name_gap_time = self._racers_rank_name_gap_time( racers_ranking, mp4_video_metrics_info) return racers_ranking, rank_name_gap_time def _edit_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera mp4_video_metrics_info (list): All the racers metric information Returns: Image: Edited main camera image """ racers_ranking, rank_name_gap_time = self._get_racers_metric_info( mp4_video_metrics_info) # Check if the done flag is set and set the banner appropriately cur_racer_metrics_info = mp4_video_metrics_info[self.racecar_index] current_lap = min( int(cur_racer_metrics_info.lap_counter) + 1, int(self.total_laps)) agent_done = cur_racer_metrics_info.done and (current_lap == int( self.total_laps)) if cur_racer_metrics_info.completion_percentage > 50.0 and cur_racer_metrics_info.completion_percentage < 60.0: major_cv_image = self._midway_racers_progress_display( major_cv_image, rank_name_gap_time, mp4_video_metrics_info) major_cv_image = self._basic_racer_info_display( major_cv_image, racers_ranking, cur_racer_metrics_info) elif agent_done: major_cv_image = self._race_finish_display(major_cv_image, rank_name_gap_time) major_cv_image = self._basic_racer_info_display( major_cv_image, racers_ranking, cur_racer_metrics_info, show_racer_name=False) else: major_cv_image = self._default_edit(major_cv_image) major_cv_image = self._basic_racer_info_display( major_cv_image, racers_ranking, cur_racer_metrics_info) return major_cv_image def _plot_track_on_gradient(self, gradient_img_path): """ For the given gradient apply the track iconographic image and use this to apply gradient on each camera frame. Previously this was done on the top camera which changed every frame. But with the track iconographic image set static, adding the track on gradient is more optimized. Arguments: gradient_img_path (str): Gradient image path Returns: (Image): Edited gradient image with track image """ gradient_img = utils.get_image( gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) gradient_img = cv2.cvtColor(gradient_img, cv2.COLOR_RGBA2BGRA) track_icongraphy_scaled = utils.resize_image(self.track_icongraphy_img, SCALE_RATIO) track_icongraphy_alpha = track_icongraphy_scaled[:, :, 3] / 255.0 # Track image is placed at the bottom right with some offset (only in leaderboard tracks) x_min = -(self.track_loc_offset[1] + track_icongraphy_scaled.shape[0]) x_max = gradient_img.shape[0] - self.track_loc_offset[1] y_min = -(self.track_loc_offset[0] + track_icongraphy_scaled.shape[1]) y_max = gradient_img.shape[1] - self.track_loc_offset[0] # This is used as the offset for plotting the agent dots self.track_start_loc = (gradient_img.shape[1] + y_min, gradient_img.shape[0] + x_min) for channel in range(0, 4): gradient_img[x_min:x_max, y_min:y_max, channel] =\ (track_icongraphy_alpha * track_icongraphy_scaled[:, :, channel]) + \ (1 - track_icongraphy_alpha) * (gradient_img[x_min:x_max, y_min:y_max, channel]) return gradient_img def _plot_agents_on_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Add the agents, obstacles on the track. Arguments: major_cv_image (Image): Edited image having gradient, text, track mp4_video_metrics_info (List): List of ROS metric values of each agent Returns: Image: Edited image with gradient, text, track and agents with dots """ agents_loc = [(metric.x, metric.y) for metric in mp4_video_metrics_info] objects_loc = [] if mp4_video_metrics_info[0].object_locations: objects_loc = [ (object_loc.x, object_loc.y) for object_loc in mp4_video_metrics_info[0].object_locations ] return self.top_view_graphics.plot_agents_as_circles( major_cv_image, agents_loc, objects_loc, self.track_start_loc) def edit_image(self, major_cv_image, metric_info): mp4_video_metrics_info = metric_info[ FrameQueueData.AGENT_METRIC_INFO.value] # Find max total_evaluation_time from mp4_video_metrics_info # to synchronize the total evaluation time throughout racers. max_total_evaluation_time = max( [item.total_evaluation_time for item in mp4_video_metrics_info]) for info in mp4_video_metrics_info: info.total_evaluation_time = max_total_evaluation_time major_cv_image = self._edit_major_cv_image(major_cv_image, mp4_video_metrics_info) major_cv_image = self._plot_agents_on_major_cv_image( major_cv_image, mp4_video_metrics_info) return cv2.cvtColor(major_cv_image, cv2.COLOR_BGRA2RGB) def _edit_top_camera_image_util(self, top_camera_image, rank_name_gap_time, mp4_video_metrics_info): """ Showing stats on the top view camera image Arguments: major_cv_image (Image): Main camera image for the racecar rank_name_gap_time (list): Sorted list based on the ranking along with rank, name, gap time, racer_number mp4_video_metrics_info (list): All the racers metric information Returns: major_cv_image (Image): Edited Main camera image """ # Applying gradient to whole major image and then writing text top_camera_image = utils.apply_gradient( top_camera_image, self.gradient_top_camera_alpha_rgb_mul, self.one_minus_gradient_top_camera_alpha) top_camera_image = cv2.cvtColor(top_camera_image, cv2.COLOR_BGR2RGBA) # LAP Title loc_x, loc_y = XYPixelLoc.F1_TOP_CAMERA_LAP_TEXT_LOC.value lap_txt = "LAP" top_camera_image = utils.write_text_on_image( image=top_camera_image, text=lap_txt, loc=(loc_x, loc_y), font=self.formula1_display_wide_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # LAP counter loc_x, loc_y = XYPixelLoc.F1_TOP_CAMERA_LAP_COUNTER_LOC.value # For Top view, lap counter should be based on leader's lap count. leader_index = rank_name_gap_time[0][3] current_lap = min( int(mp4_video_metrics_info[leader_index].lap_counter) + 1, int(self.total_laps)) lap_counter_text = "{} / {}".format(current_lap, int(self.total_laps)) top_camera_image = utils.write_text_on_image( image=top_camera_image, text=lap_counter_text, loc=(loc_x, loc_y), font=self.formula1_display_bold_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Each racers ranking, name and gap time rank_loc_x, rank_loc_y = XYPixelLoc.F1_TOP_CAMERA_LEADER_RANK_LOC.value color_code_loc_x, color_code_loc_y = XYPixelLoc.F1_TOP_CAMERA_LEADER_COLOR_CODE_LOC.value display_name_loc_x, display_name_loc_y = XYPixelLoc.F1_TOP_CAMERA_LEADER_DISPLAY_NAME_LOC.value gap_loc_x, gap_loc_y = XYPixelLoc.F1_TOP_CAMERA_LEADER_GAP_LOC.value for row in range(4): for col in range(3): if (row + 4 * col) >= len(rank_name_gap_time): continue rank, display_name, gap_time, racer_number = rank_name_gap_time[ row + 4 * col] # Rank loc_x, loc_y = rank_loc_x + (col * 185), rank_loc_y + (row * 20) racer_rank = "{}".format(rank) top_camera_image = utils.write_text_on_image( image=top_camera_image, text=racer_rank, loc=(loc_x, loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Draw racer color code icon loc_x, loc_y = color_code_loc_x + ( col * 185), color_code_loc_y + (row * 20) top_camera_image = utils.plot_rectangular_image_on_main_image( top_camera_image, self._racer_color_code_rect_img[racer_number], (loc_x, loc_y)) # Adding display name to the table loc_x, loc_y = display_name_loc_x + ( col * 185), display_name_loc_y + (row * 20) display_name_txt = display_name if len( display_name) <= 6 else "{}".format(display_name[:6]) top_camera_image = utils.write_text_on_image( image=top_camera_image, text=display_name_txt, loc=(loc_x, loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Adding gap to the table if row != 0 or col != 0: loc_x, loc_y = gap_loc_x + (col * 180), gap_loc_y + (row * 20) # The gradient box images are not equally placed for gaps if col == 2: loc_x, loc_y = gap_loc_x + (col * 182), gap_loc_y + ( row * 20) gap_time_text = "+{:.3f}".format(gap_time) top_camera_image = utils.write_text_on_image( image=top_camera_image, text=gap_time_text, loc=(loc_x, loc_y), font=self.formula1_display_regular_12px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) return top_camera_image def edit_top_camera_image(self, top_camera_image, metric_info): """ Editing the top camera image Arguments: top_camera_image (Image): Edited top camera image """ mp4_video_metrics_info = metric_info[ FrameQueueData.AGENT_METRIC_INFO.value] _, rank_name_gap_time = self._get_racers_metric_info( mp4_video_metrics_info) top_camera_image = self._edit_top_camera_image_util( top_camera_image, rank_name_gap_time, mp4_video_metrics_info) return cv2.cvtColor(top_camera_image, cv2.COLOR_BGRA2RGB)
class TrainingImageEditing(ImageEditingInterface): """ Editing the image in training phase """ def __init__(self, racecar_info): """ To capture the video of evaluation done during the training phase Args: racecar_info (dict): Information of the agent """ self.racecar_info = racecar_info # Store the font which we will use to write the phase with self.training_phase_font = utils.get_font('Amazon_Ember_RgIt', 35) # Subscriber to get the phase of the training (Ideal, training, evaluation) rospy.Subscriber('/agent/training_phase', String, self._training_phase_cb_) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # # TODO: Currently dont have the gradient image for the training. So not using any gradient overlay on image # self.gradient_img = utils.get_image(TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG.value, # IconographicImageSize.FULL_IMAGE_SIZE.value) # self.gradient_alpha = self.gradient_img[:, :, 3] / 255.0 # # String indicating the current phase self._current_training_phase = 'Initializing' # Top camera information top_camera_info = utils.get_top_camera_info() self.edited_topview_pub = rospy.Publisher('/deepracer/topview_stream', ROSImg, queue_size=1) self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info) def _training_phase_cb_(self, phase): """ Callback function that gives the training phase - Whether its in evaluation, ideal, training, initializing Args: phase: [description] """ self._current_training_phase = phase.data def _edit_major_cv_image(self, major_cv_image): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera Returns: Image: Edited main camera image """ # # TODO: Currently dont have the gradient image for the training. So not using any gradient overlay on image # major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_img, self.gradient_alpha) # if self._current_training_phase: # Add the label that lets the user know the training phase major_cv_image = utils.write_text_on_image( image=major_cv_image, text=self._current_training_phase, loc=XYPixelLoc.TRAINING_PHASE_LOC.value, font=self.training_phase_font, font_color=None, font_shadow_color=RaceCarColorToRGB.Black.value) return major_cv_image def _edit_minor_cv_image(self): """ Apply all the editing for the iconographic of the top view camera image Returns: Image: Edited image of the iconographic view of the top camera """ return self.top_view_graphics.plot_agents_as_circles( self.track_icongraphy_img.copy()) def edit_image(self, major_cv_image): major_cv_image = self._edit_major_cv_image(major_cv_image) minor_cv_image = self._edit_minor_cv_image() return utils.overlay_track_images(major_cv_image, minor_cv_image)
class MultiAgentImageEditing(ImageEditingInterface): """Image editing class for head to head where there are multiple agents """ def __init__(self, racecar_name, racecars_info, race_type): """ This class is used for head to head racing where there are more than one agent Args: racecar_name (str): The agent name with 45degree camera view racecars_info (dict): All the agents information race_type (str): The type of race. This is used to know if its race type or evaluation """ self.racecar_name = racecar_name self.racecars_info = racecars_info racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 self.race_type = race_type # Store the font which we will use to write the phase with self.amazon_ember_regular_20px = utils.get_font('AmazonEmber-Regular', 20) self.amazon_ember_regular_16px = utils.get_font('AmazonEmber-Regular', 16) self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30) self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18) self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20) self.amazon_ember_light_italic_20px = utils.get_font('AmazonEmber-LightItalic', 20) self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING" self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE" self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "") # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value if self.is_league_leaderboard \ else XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value gradient_img_path = TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \ if self.is_league_leaderboard else TrackAssetsIconographicPngs.HEAD_TO_HEAD_OVERLAY_PNG.value self.gradient_img = self._plot_track_on_gradient(gradient_img_path) self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values(self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics(top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecars_info) def _edit_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera Returns: Image: Edited main camera image """ # Applying gradient to whole major image and then writing text major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha) # Adding display name to the image agents_speed = 0 agent_done = False if self.race_type != RaceType.F1.value: for i, racecar_info in enumerate(self.racecars_info): loc_x, loc_y = XYPixelLoc.MULTI_AGENT_DISPLAY_NAME_LOC.value[i][0], XYPixelLoc.MULTI_AGENT_DISPLAY_NAME_LOC.value[i][1] # Display name (Racer name/Model name) display_name = racecar_info['display_name'] display_name_txt = display_name if len(display_name) < 15 else "{}...".format(display_name[:15]) major_cv_image = utils.write_text_on_image(image=major_cv_image, text=display_name_txt, loc=(loc_x, loc_y), font=self.amazon_ember_regular_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Lap Counter loc_y += 30 total_laps = rospy.get_param("NUMBER_OF_TRIALS", 0) current_lap = int(mp4_video_metrics_info[i].lap_counter) + 1 lap_counter_text = "{}/{}".format(current_lap, total_laps) major_cv_image = utils.write_text_on_image(image=major_cv_image, text=lap_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_heavy_30px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Reset counter loc_y += 45 reset_counter_text = "Reset | {}".format(mp4_video_metrics_info[i].reset_counter) major_cv_image = utils.write_text_on_image(image=major_cv_image, text=reset_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_18px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) if self.racecar_name == racecar_info['name']: agents_speed = mp4_video_metrics_info[i].throttle # The race is complete when total lap is same as current lap and done flag is set agent_done = agent_done or (mp4_video_metrics_info[i].done and (current_lap == int(total_laps))) # Speed loc_x, loc_y = XYPixelLoc.SPEED_EVAL_LOC.value if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.SPEED_LEADERBOARD_LOC.value speed_text = "{} m/s".format(utils.get_speed_formatted_str(agents_speed)) major_cv_image = utils.write_text_on_image(image=major_cv_image, text=speed_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Leaderboard name if self.is_league_leaderboard: major_cv_image = utils.write_text_on_image(image=major_cv_image, text=self.leaderboard_name, loc=XYPixelLoc.LEADERBOARD_NAME_LOC.value, font=self.amazon_ember_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Evaluation type loc_x, loc_y = XYPixelLoc.RACE_TYPE_EVAL_LOC.value if self.is_league_leaderboard: loc_x, loc_y = XYPixelLoc.RACE_TYPE_RACE_LOC.value race_text = "race" if self.is_racing else "evaluation" evaluation_type_txt = "{} {}".format(RACE_TYPE_TO_VIDEO_TEXT_MAPPING[self.race_type], race_text) major_cv_image = utils.write_text_on_image(image=major_cv_image, text=evaluation_type_txt, loc=(loc_x, loc_y), font=self.amazon_ember_light_italic_20px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # total_evaluation_time (Race time) loc_x, loc_y = XYPixelLoc.MULTI_AGENT_EVAL_TIME.value total_eval_milli_seconds = mp4_video_metrics_info[0].total_evaluation_time time_delta = datetime.timedelta(milliseconds=total_eval_milli_seconds) total_eval_time_text = "Race | {}".format(utils.milliseconds_to_timeformat(time_delta)) major_cv_image = utils.write_text_on_image(image=major_cv_image, text=total_eval_time_text, loc=(loc_x, loc_y), font=self.amazon_ember_light_18px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # AWS Deepracer logo at the bottom for the community leaderboard if self.is_league_leaderboard: major_cv_image = utils.write_text_on_image(image=major_cv_image, text=AWS_DEEPRACER_WATER_MARK, loc=XYPixelLoc.AWS_DEEPRACER_WATER_MARK_LOC.value, font=self.amazon_ember_regular_16px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Check if the done flag is set and set the banner appropriately if agent_done: # When the cv2 text is written, it automatically drops the alpha value of the image rel_y_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value[1] if self.is_league_leaderboard else 0 major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2RGBA) racecomplete_image = utils.get_image(TrackAssetsIconographicPngs.RACE_COMPLETE_OVERLAY_PNG.value, IconographicImageSize.RACE_COMPLETE_IMAGE_SIZE.value) x_offset = major_cv_image.shape[1] - racecomplete_image.shape[1]//2 y_offset = major_cv_image.shape[0] - RACE_COMPLETE_Y_OFFSET - rel_y_offset - racecomplete_image.shape[0]//2 major_cv_image = utils.plot_rectangular_image_on_main_image( major_cv_image, racecomplete_image, (x_offset, y_offset)) major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA) return major_cv_image def _plot_track_on_gradient(self, gradient_img_path): """ For the given gradient apply the track iconographic image and use this to apply gradient on each camera frame. Previously this was done on the top camera which changed every frame. But with the track iconographic image set static, adding the track on gradient is more optimized. Arguments: gradient_img_path (str): Gradient image path Returns: (Image): Edited gradient image with track image """ gradient_img = utils.get_image(gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) gradient_img = cv2.cvtColor(gradient_img, cv2.COLOR_RGBA2BGRA) track_icongraphy_scaled = utils.resize_image(self.track_icongraphy_img, SCALE_RATIO) track_icongraphy_alpha = track_icongraphy_scaled[:, :, 3]/255.0 # Track image is placed at the bottom right with some offset (only in leaderboard tracks) x_min = -(self.track_loc_offset[1] + track_icongraphy_scaled.shape[0]) x_max = gradient_img.shape[0] - self.track_loc_offset[1] y_min = -(self.track_loc_offset[0] + track_icongraphy_scaled.shape[1]) y_max = gradient_img.shape[1] - self.track_loc_offset[0] # This is used as the offset for plotting the agent dots self.track_start_loc = (gradient_img.shape[1] + y_min, gradient_img.shape[0] + x_min) for channel in range(0, 4): gradient_img[x_min:x_max, y_min:y_max, channel] =\ (track_icongraphy_alpha * track_icongraphy_scaled[:, :, channel]) + \ (1 - track_icongraphy_alpha) * (gradient_img[x_min:x_max, y_min:y_max, channel]) return gradient_img def _plot_agents_on_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Add the agents, obstacles on the track. Arguments: major_cv_image (Image): Edited image having gradient, text, track mp4_video_metrics_info (List): List of ROS metric values of each agent Returns: Image: Edited image with gradient, text, track and agents with dots """ agents_loc = [(metric.x, metric.y) for metric in mp4_video_metrics_info] objects_loc = [] if mp4_video_metrics_info[0].object_locations: objects_loc = [(object_loc.x, object_loc.y) for object_loc in mp4_video_metrics_info[0].object_locations] return self.top_view_graphics.plot_agents_as_circles( major_cv_image, agents_loc, objects_loc, self.track_start_loc) def edit_image(self, major_cv_image, metric_info): mp4_video_metrics_info = metric_info[FrameQueueData.AGENT_METRIC_INFO.value] major_cv_image = self._edit_major_cv_image(major_cv_image, mp4_video_metrics_info) major_cv_image = self._plot_agents_on_major_cv_image(major_cv_image, mp4_video_metrics_info) return cv2.cvtColor(major_cv_image, cv2.COLOR_BGRA2RGB)
def __init__(self, racecar_name, racecar_info, race_type): """ Initializing the required data for the head to bot, time-trail. This is used for single agent Arguments: racecar_name (str): racecar name in string racecars_info (list): list of dict having information of the agent race_type (str): Since this class is reused for all the different race_type """ # race duration in milliseconds self._world_name = rospy.get_param("WORLD_NAME") self.num_sectors = int(rospy.get_param("NUM_SECTORS", "3")) self.race_duration = int( rospy.get_param("RACE_DURATION", DEFAULT_RACE_DURATION)) * 1000 self.racecar_info = racecar_info self.race_type = race_type racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 # Store the font which we will use to write the phase with self.amazon_ember_regular_28px = utils.get_font( 'AmazonEmber-Regular', 28) self.amazon_ember_regular_14px = utils.get_font( 'AmazonEmber-Regular', 14) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = VirtualEventXYPixelLoc.TRACK_IMG_VIRTUAL_EVENT_LOC.value self._track_x_min = None self._track_x_max = None self._track_y_min = None self._track_y_max = None # Gradient overlay image with track and virtual event mock gradient_img_path = VirtualEventIconographicPngs.OVERLAY_PNG.value self.gradient_img = self._plot_track_on_gradient(gradient_img_path) # Time remaining text loc_x, loc_y = VirtualEventXYPixelLoc.TIME_REMAINING_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="TIME REMAINING", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Speed text loc_x, loc_y = VirtualEventXYPixelLoc.SPEED_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="m/s", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Reset text loc_x, loc_y = VirtualEventXYPixelLoc.RESET_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="RESET", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # current lap time text loc_x, loc_y = VirtualEventXYPixelLoc.CURRENT_LAP_TIME_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="CURRENT LAP TIME", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # best lap time text loc_x, loc_y = VirtualEventXYPixelLoc.BEST_LAP_TIME_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="BEST LAP TIME", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # apply graident self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values( self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info, is_virtual_event=True) # virtual event image editting state machine self._image_edit_fsm = FSM(initial_state=VirtualEventWaitState()) # if best sector time download from s3 failed. Then, initialize best sector time as None # and not display sector color self._sector_times = {} # declare sector images self._sectors_img_dict = {} for idx in range(self.num_sectors): sector = SECTOR_X_FORMAT.format(idx + 1) sector_color_img_dict = utils.init_sector_img_dict( world_name=self._world_name, sector=sector) self._sectors_img_dict[sector] = sector_color_img_dict # use the s3 bucket and prefix for yaml file stored as environment variable because # here is SimApp use only. For virtual event there is no s3 bucket and prefix past # through yaml file. All are past through sqs. For simplicity, reuse the yaml s3 bucket # and prefix environment variable. self._virtual_event_best_sector_time = VirtualEventBestSectorTime( bucket=os.environ.get("YAML_S3_BUCKET", ''), s3_key=get_s3_key(os.environ.get("YAML_S3_PREFIX", ''), SECTOR_TIME_S3_POSTFIX), region_name=os.environ.get("APP_REGION", "us-east-1"), local_path=SECTOR_TIME_LOCAL_PATH) self._sector_times.update( self._virtual_event_best_sector_time.get_sector_time( num_sectors=self.num_sectors)) # declare default best personal and current persoanl time to inf for idx in range(self.num_sectors): sector = SECTOR_X_FORMAT.format(idx + 1) self._sector_times[SECTOR_TIME_FORMAT_DICT[ TrackSectorTime.BEST_PERSONAL].format(sector)] = float("inf") self._sector_times[SECTOR_TIME_FORMAT_DICT[ TrackSectorTime.CURRENT_PERSONAL].format(sector)] = float( "inf") self._curr_lap_time = 0 self._last_eval_time = 0 self._curr_progress = 0 self._last_progress = 0 self._current_lap = 1 # Initializing the fader behaviour to pre-compute the gradient values final_fading_image = utils.get_image( VirtualEventIconographicPngs.FINAL_FADING_IMAGE_50ALPHA.value, IconographicImageSize.FULL_IMAGE_SIZE.value) final_fading_image = cv2.cvtColor(final_fading_image, cv2.COLOR_RGBA2BGRA) self._fader_obj = Fader( final_fading_image, fading_min_percent=VirtualEventFader.FADING_MIN_PERCENT.value, fading_max_percent=VirtualEventFader.FADING_MAX_PERCENT.value, num_frames=VirtualEventFader.NUM_FRAMES.value)
class VirtualEventImageEditing(ImageEditingInterface): """ Image editing class for virtual event """ def __init__(self, racecar_name, racecar_info, race_type): """ Initializing the required data for the head to bot, time-trail. This is used for single agent Arguments: racecar_name (str): racecar name in string racecars_info (list): list of dict having information of the agent race_type (str): Since this class is reused for all the different race_type """ # race duration in milliseconds self._world_name = rospy.get_param("WORLD_NAME") self.num_sectors = int(rospy.get_param("NUM_SECTORS", "3")) self.race_duration = int( rospy.get_param("RACE_DURATION", DEFAULT_RACE_DURATION)) * 1000 self.racecar_info = racecar_info self.race_type = race_type racecar_index = get_racecar_idx(racecar_name) self.racecar_index = racecar_index if racecar_index else 0 # Store the font which we will use to write the phase with self.amazon_ember_regular_28px = utils.get_font( 'AmazonEmber-Regular', 28) self.amazon_ember_regular_14px = utils.get_font( 'AmazonEmber-Regular', 14) # The track image as iconography self.track_icongraphy_img = utils.get_track_iconography_image() # Track image offset self.track_loc_offset = VirtualEventXYPixelLoc.TRACK_IMG_VIRTUAL_EVENT_LOC.value self._track_x_min = None self._track_x_max = None self._track_y_min = None self._track_y_max = None # Gradient overlay image with track and virtual event mock gradient_img_path = VirtualEventIconographicPngs.OVERLAY_PNG.value self.gradient_img = self._plot_track_on_gradient(gradient_img_path) # Time remaining text loc_x, loc_y = VirtualEventXYPixelLoc.TIME_REMAINING_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="TIME REMAINING", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Speed text loc_x, loc_y = VirtualEventXYPixelLoc.SPEED_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="m/s", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Reset text loc_x, loc_y = VirtualEventXYPixelLoc.RESET_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="RESET", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # current lap time text loc_x, loc_y = VirtualEventXYPixelLoc.CURRENT_LAP_TIME_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="CURRENT LAP TIME", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # best lap time text loc_x, loc_y = VirtualEventXYPixelLoc.BEST_LAP_TIME_TEXT.value self.gradient_img = utils.write_text_on_image( image=self.gradient_img, text="BEST LAP TIME", loc=(loc_x, loc_y), font=self.amazon_ember_regular_14px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # apply graident self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values( self.gradient_img) # Top camera information top_camera_info = utils.get_top_camera_info() self.top_view_graphics = TopViewGraphics( top_camera_info.horizontal_fov, top_camera_info.padding_pct, top_camera_info.image_width, top_camera_info.image_height, racecar_info, is_virtual_event=True) # virtual event image editting state machine self._image_edit_fsm = FSM(initial_state=VirtualEventWaitState()) # if best sector time download from s3 failed. Then, initialize best sector time as None # and not display sector color self._sector_times = {} # declare sector images self._sectors_img_dict = {} for idx in range(self.num_sectors): sector = SECTOR_X_FORMAT.format(idx + 1) sector_color_img_dict = utils.init_sector_img_dict( world_name=self._world_name, sector=sector) self._sectors_img_dict[sector] = sector_color_img_dict # use the s3 bucket and prefix for yaml file stored as environment variable because # here is SimApp use only. For virtual event there is no s3 bucket and prefix past # through yaml file. All are past through sqs. For simplicity, reuse the yaml s3 bucket # and prefix environment variable. self._virtual_event_best_sector_time = VirtualEventBestSectorTime( bucket=os.environ.get("YAML_S3_BUCKET", ''), s3_key=get_s3_key(os.environ.get("YAML_S3_PREFIX", ''), SECTOR_TIME_S3_POSTFIX), region_name=os.environ.get("APP_REGION", "us-east-1"), local_path=SECTOR_TIME_LOCAL_PATH) self._sector_times.update( self._virtual_event_best_sector_time.get_sector_time( num_sectors=self.num_sectors)) # declare default best personal and current persoanl time to inf for idx in range(self.num_sectors): sector = SECTOR_X_FORMAT.format(idx + 1) self._sector_times[SECTOR_TIME_FORMAT_DICT[ TrackSectorTime.BEST_PERSONAL].format(sector)] = float("inf") self._sector_times[SECTOR_TIME_FORMAT_DICT[ TrackSectorTime.CURRENT_PERSONAL].format(sector)] = float( "inf") self._curr_lap_time = 0 self._last_eval_time = 0 self._curr_progress = 0 self._last_progress = 0 self._current_lap = 1 # Initializing the fader behaviour to pre-compute the gradient values final_fading_image = utils.get_image( VirtualEventIconographicPngs.FINAL_FADING_IMAGE_50ALPHA.value, IconographicImageSize.FULL_IMAGE_SIZE.value) final_fading_image = cv2.cvtColor(final_fading_image, cv2.COLOR_RGBA2BGRA) self._fader_obj = Fader( final_fading_image, fading_min_percent=VirtualEventFader.FADING_MIN_PERCENT.value, fading_max_percent=VirtualEventFader.FADING_MAX_PERCENT.value, num_frames=VirtualEventFader.NUM_FRAMES.value) def _edit_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Apply all the editing for the Major 45degree camera image Args: major_cv_image (Image): Image straight from the camera mp4_video_metrics_info (dict): rest image editting info Returns: Image: Edited main camera image """ major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha) ######################### # update display params # ######################### episode_status = mp4_video_metrics_info[ self.racecar_index].episode_status # Display name (Racer name/Model name) display_name = self.racecar_info[self.racecar_index]['display_name'] # total_evaluation_time (Race time) total_eval_milli_seconds = mp4_video_metrics_info[ self.racecar_index].total_evaluation_time # Reset counter reset_counter = mp4_video_metrics_info[ self.racecar_index].reset_counter # Speed speed = mp4_video_metrics_info[self.racecar_index].throttle # Current progress current_progress = mp4_video_metrics_info[ self.racecar_index].completion_percentage # Prepare a dict for finite state machine on event call info_dict = { VirtualEventMP4Params.COUNTDOWN_TIMER.value: mp4_video_metrics_info[self.racecar_index].pause_duration, VirtualEventMP4Params.MAJOR_CV_IMAGE.value: major_cv_image, VirtualEventMP4Params.CURRENT_LAP.value: self._current_lap, VirtualEventMP4Params.TOTAL_EVAL_SECONDS.value: total_eval_milli_seconds, VirtualEventMP4Params.RESET_COUNTER.value: reset_counter, VirtualEventMP4Params.SPEED.value: speed, VirtualEventMP4Params.CURR_PROGRESS.value: current_progress, VirtualEventMP4Params.LAST_EVAL_SECONDS.value: self._last_eval_time, VirtualEventMP4Params.X_MIN.value: self._track_x_min, VirtualEventMP4Params.X_MAX.value: self._track_x_max, VirtualEventMP4Params.Y_MIN.value: self._track_y_min, VirtualEventMP4Params.Y_MAX.value: self._track_y_max, VirtualEventMP4Params.SECTOR_TIMES.value: self._sector_times, VirtualEventMP4Params.CURR_LAP_TIME.value: self._curr_lap_time, VirtualEventMP4Params.SECTOR_IMAGES.value: self._sectors_img_dict, VirtualEventMP4Params.FADER_OBJ.value: self._fader_obj } ##################### # run state machine # ##################### # virtual event image edit finite state machine on event info_dict = self._image_edit_fsm.execute(input_val={ 'event': episode_status, 'info_dict': info_dict }) # update display param from the finite state machine return value major_cv_image = info_dict[VirtualEventMP4Params.MAJOR_CV_IMAGE.value] total_eval_milli_seconds = info_dict[ VirtualEventMP4Params.TOTAL_EVAL_SECONDS.value] reset_counter = info_dict[VirtualEventMP4Params.RESET_COUNTER.value] speed = info_dict[VirtualEventMP4Params.SPEED.value] self._current_lap = info_dict[VirtualEventMP4Params.CURRENT_LAP.value] self._last_eval_time = info_dict[ VirtualEventMP4Params.LAST_EVAL_SECONDS.value] self._sector_times = info_dict[ VirtualEventMP4Params.SECTOR_TIMES.value] self._curr_lap_time = info_dict[ VirtualEventMP4Params.CURR_LAP_TIME.value] # Time remaining digit loc_x, loc_y = VirtualEventXYPixelLoc.TIME_REMAINING_DIGIT.value time_remaining = self.race_duration - total_eval_milli_seconds time_remaining = time_remaining if time_remaining > 0.0 else 0.0 time_remaining = datetime.timedelta(milliseconds=time_remaining) time_remaining = utils.milliseconds_to_timeformat(time_remaining) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=time_remaining, loc=(loc_x, loc_y), font=self.amazon_ember_regular_28px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Speed digit loc_x, loc_y = VirtualEventXYPixelLoc.SPEED_DIGIT.value speed_text = utils.get_speed_formatted_str(speed) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=speed_text, loc=(loc_x, loc_y), font=self.amazon_ember_regular_28px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # Reset digit loc_x, loc_y = VirtualEventXYPixelLoc.RESET_DIGIT.value reset_counter_text = "{}".format(reset_counter) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=reset_counter_text, loc=(loc_x, loc_y), font=self.amazon_ember_regular_28px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # curent lap time digit loc_x, loc_y = VirtualEventXYPixelLoc.CURRENT_LAP_TIME_DIGIT.value curr_lap_time = utils.milliseconds_to_timeformat( datetime.timedelta(milliseconds=self._curr_lap_time)) major_cv_image = utils.write_text_on_image( image=major_cv_image, text=curr_lap_time, loc=(loc_x, loc_y), font=self.amazon_ember_regular_28px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) # best lap time digit loc_x, loc_y = VirtualEventXYPixelLoc.BEST_LAP_TIME_DIGIT.value best_lap_time = mp4_video_metrics_info[ self.racecar_index].best_lap_time # The initial default best_lap_time from s3_metrics.py is inf # If the ros service in s3_metrics.py has not come up yet, best_lap_time is 0 best_lap_time = utils.milliseconds_to_timeformat( datetime.timedelta(milliseconds=best_lap_time)) \ if best_lap_time != float("inf") and best_lap_time != 0 else "--:--.---" major_cv_image = utils.write_text_on_image( image=major_cv_image, text=best_lap_time, loc=(loc_x, loc_y), font=self.amazon_ember_regular_28px, font_color=RaceCarColorToRGB.White.value, font_shadow_color=RaceCarColorToRGB.Black.value) major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA) return major_cv_image def _plot_track_on_gradient(self, gradient_img_path): """ For the given gradient apply the track iconographic image and use this to apply gradient on each camera frame. Previously this was done on the top camera which changed every frame. But with the track iconographic image set static, adding the track on gradient is more optimized. Arguments: gradient_img_path (str): Gradient image path Returns: (Image): Edited gradient image with track image """ gradient_img = utils.get_image( gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value) gradient_img = cv2.cvtColor(gradient_img, cv2.COLOR_RGBA2BGRA) track_icongraphy_scaled = utils.resize_image(self.track_icongraphy_img, SCALE_RATIO) track_icongraphy_alpha = track_icongraphy_scaled[:, :, 3] / 255.0 # Track image is placed at the top left self._track_x_min = self.track_loc_offset[1] self._track_x_max = self.track_loc_offset[ 1] + track_icongraphy_scaled.shape[0] self._track_y_min = self.track_loc_offset[0] self._track_y_max = self.track_loc_offset[ 0] + track_icongraphy_scaled.shape[1] # This is used as the offset for plotting the agent dots self.track_start_loc = (self._track_y_min, self._track_x_min) for channel in range(0, 4): gradient_img[self._track_x_min:self._track_x_max, self._track_y_min:self._track_y_max, channel] =\ (track_icongraphy_alpha * track_icongraphy_scaled[:, :, channel]) + \ (1 - track_icongraphy_alpha) * (gradient_img[self._track_x_min:self._track_x_max, self._track_y_min:self._track_y_max, channel]) return gradient_img def _plot_agents_on_major_cv_image(self, major_cv_image, mp4_video_metrics_info): """ Add the agents, obstacles on the track. Arguments: major_cv_image (Image): Edited image having gradient, text, track mp4_video_metrics_info (List): List of ROS metric values of each agent Returns: Image: Edited image with gradient, text, track and agents with dots """ agents_loc = [(metric.x, metric.y) for metric in mp4_video_metrics_info] objects_loc = [] if mp4_video_metrics_info[0].object_locations: objects_loc = [ (object_loc.x, object_loc.y) for object_loc in mp4_video_metrics_info[0].object_locations ] return self.top_view_graphics.plot_agents_as_circles( major_cv_image, agents_loc, objects_loc, self.track_start_loc) def edit_image(self, major_cv_image, metric_info): mp4_video_metrics_info = metric_info[ FrameQueueData.AGENT_METRIC_INFO.value] major_cv_image = self._edit_major_cv_image(major_cv_image, mp4_video_metrics_info) major_cv_image = self._plot_agents_on_major_cv_image( major_cv_image, mp4_video_metrics_info) return cv2.cvtColor(major_cv_image, cv2.COLOR_BGRA2RGB)