STM32N6 NPU Deployment — Politecnico di Milano  1.0
Documentation for Neural Network Deployment on STM32N6 NPU - Politecnico di Milano 2024-2025
postprocess.py
Go to the documentation of this file.
1 # /*---------------------------------------------------------------------------------------------
2 # * Copyright (c) 2024 STMicroelectronics.
3 # * All rights reserved.
4 # *
5 # * This software is licensed under terms that can be found in the LICENSE file in
6 # * the root directory of this software component.
7 # * If no LICENSE file comes with this software, it is provided AS-IS.
8 # *--------------------------------------------------------------------------------------------*/
9 
10 
21 Authors: Giacomo Colosio, Sebastiano Colosio, Patrizio Acquadro, Tito Nicola Drugman
22 #
23 # @copyright Copyright (c) 2023-2024 STMicroelectronics. All rights reserved.
24 
25 
26 import tensorflow as tf
27 import numpy as np
28 
29 
30 def heatmaps_spe_postprocess(tensor:tf.Tensor):
31  '''
32  Post-process for the single pose estimation heatmaps use-case
33 
34  Args
35  tensor (tf.Tensor): shape (batch, res, res, keypoints) FLOAT32 heatmaps outputs of the single pose estimation models
36 
37  Returns:
38  detection (tf.Tensor): shape (batch, 1, keypoints*3) FLOAT32 the (x,y,conf) values of all keypoints for the single person
39  '''
40 
41  sh = tensor.shape # (batch, res,res,keypoints)
42 
43  predictions_flat = tf.reshape(tensor,[sh[0],sh[1]*sh[2],-1]) # flatten the prediction tensor, shape : (batch,res*res,keypoints)
44  arg_pred = tf.argmax(predictions_flat,1) # find the argmax for each keypoints in this flatten tensor, shape : (batch,keypoints)
45  val_pred = tf.reduce_max(predictions_flat,1) # find the max value of this flatten tensor, shape : (batch,keypoints)
46 
47  arg_pred_x = (tf.cast(arg_pred//sh[2],dtype=tf.float32)+0.5) / sh[1] # find the x values of keypoints in [0,1], shape : (batch,keypoints)
48  arg_pred_y = (tf.cast(arg_pred%sh[2],dtype=tf.float32)+0.5) / sh[2] # find the y values of keypoints in [0,1], shape : (batch,keypoints)
49 
50  detection = tf.stack([arg_pred_y,arg_pred_x,val_pred],1) # shape : (batch,3,keypoints)
51  detection = tf.transpose(detection,[0,2,1]) # shape : (batch,keypoints,3)
52  detection = tf.reshape(detection,[sh[0],-1]) # shape : (batch,keypoints*3)
53  detection = detection[:,None,:] # shape : (batch,1,keypoints*3)
54 
55  return detection # shape : (batch,1,keypoints*3)
56 
57 def spe_postprocess(tensor:tf.Tensor):
58  '''
59  Post-process for the single pose estimation use-case
60 
61  Args
62  tensor (tf.Tensor): shape (batch, 1, keypoints, 3) FLOAT32 the (x,y,conf) values of all keypoints but in a different format
63 
64  Returns:
65  detection (tf.Tensor): shape (batch, 1, keypoints*3) FLOAT32 the (x,y,conf) values of all keypoints for the single person
66  '''
67 
68  sh = tensor.shape
69 
70  x = tensor[:,:,:,1]
71  y = tensor[:,:,:,0]
72  v = tensor[:,:,:,2]
73 
74  detection = tf.stack([x,y,v],-1) # (batch, 1, keypoints, 3)
75 
76  detection = tf.reshape(detection,[sh[0],1,-1]) # (batch, 1, keypoints*3)
77 
78  return detection
79 
80 def _padded_nms(tensor:tf.Tensor,max_output_size:int,iou_threshold:float,score_threshold:float):
81  '''
82  Function used to apply NMS on each image of the batch independently
83 
84  Args
85  tensor (tf.Tensor): shape (5+keypoints*3, num_boxes) FLOAT32 outputs of the YOLO multi pose estimation models
86  max_output_size (tf.Tensor): shape (1,) INT32 max number of detections per image
87  iou_threshold (tf.Tensor): shape (1,) FLOAT32 threshold for NMS iou
88  score_threshold (tf.Tensor): shape (1,) FLOAT32 threshold to filter detections under a certain score
89 
90  Returns:
91  detection (tf.Tensor): shape (max_output_size, 5+keypoints*3) FLOAT32 bounding boxes + (x,y,conf) values for all keypoints of all detected persons
92  '''
93 
94  boxes = tensor[:4] # shape : (4,num_boxes)
95  scores = tensor[4] # shape : (num_boxes)
96  keypoints = tensor[5:] # shape : (nb_kpts*3,num_boxes)
97 
98  # the scores must be of the form [y1, x1, y2, x2] in order for the NMS to work
99  # they are initially of the form [x,y,w,h]
100 
101  x1 = boxes[0] - boxes[2]/2 # shape : (num_boxes)
102  y1 = boxes[1] - boxes[3]/2 # shape : (num_boxes)
103  x2 = boxes[0] + boxes[2]/2 # shape : (num_boxes)
104  y2 = boxes[1] + boxes[3]/2 # shape : (num_boxes)
105 
106  yxboxes = tf.stack([y1, x1, y2, x2]) # shape : (4,num_boxes)
107  yxboxes = tf.transpose(yxboxes,[1,0]) # shape : (num_boxes,4)
108 
109  selected_indices = tf.image.non_max_suppression(yxboxes,scores,
110  max_output_size = max_output_size,
111  iou_threshold = iou_threshold,
112  score_threshold = score_threshold)
113  indices = selected_indices.shape[0]
114 
115  if indices:
116  detection = tf.gather(tensor,selected_indices,axis=1) # shape : (5+nb_kpts*3,selected_indices)
117  detection = tf.transpose(detection,[1,0]) # shape : (selected_indices,5+nb_kpts*3)
118  else:
119  detection = tf.zeros((indices,tensor.shape[0]))
120 
121  zero_padding = tf.zeros((max_output_size-indices,tensor.shape[0])) # shape : (max_output_size-selected_indices,5+nb_kpts*3)
122 
123  detection = tf.concat([detection,zero_padding],0) # shape : (max_output_size,5+nb_kpts*3)
124 
125  return detection # shape : (max_output_size, 5+nb_kpts*3)
126 
127 def yolo_mpe_postprocess(tensor:tf.Tensor,max_output_size:int=20,iou_threshold:float=0.7,score_threshold:float=0.25):
128  '''
129  Post-process for the multi pose estimation use-case
130 
131  Args
132  tensor (tf.Tensor): shape (batch, 5+keypoints*3, num_boxes) FLOAT32 outputs of the YOLO multi pose estimation models
133  max_output_size (tf.Tensor): shape (1,) INT32 max number of detections per image
134  iou_threshold (tf.Tensor): shape (1,) FLOAT32 threshold for NMS iou
135  score_threshold (tf.Tensor): shape (1,) FLOAT32 threshold to filter detections under a certain score
136 
137  Returns:
138  detections (tf.Tensor): shape (batch, max_output_size, 5+keypoints*3) FLOAT32 values for all keypoints of all detected persons
139  '''
140 
141 
142  tensor = tf.constant(tensor,tf.float32)
143 
144  args = {'max_output_size':max_output_size,
145  'iou_threshold':iou_threshold,
146  'score_threshold':score_threshold}
147 
148  detections = tf.map_fn(lambda x : _padded_nms(x,**args),tensor)
149 
150  return detections # shape : (batch, max_output_size, 5+keypoints*3)
151 
152 
153 def hand_landmarks_postprocess(tensor:[tf.Tensor]):
154  '''
155  Post-process for the hand landmarks use-case
156 
157  Args
158  tensor list(tf.Tensor): shape [(batch,1),(batch, keypoints*3),(batch,1),(batch, keypoints*3)] FLOAT32 outputs of the hand landmarks
159 
160  Returns:
161  det (tf.Tensor): shape (batch, keypoints*3) FLOAT32 3D detections of the hand landmarks in pixels
162  norm_det (tf.Tensor): shape (batch, keypoints*3) FLOAT32 3D detections of the hand landmarks centered reduced (invariant of hand size and position)
163  htype (tf.Tensor): shape (batch,) FLOAT32 type of hand (right or left) if near 0 -> left, if near 1 -> right
164  hprob (tf.Tensor): shape (batch,) FLOAT32 presence probability of the hand
165  '''
166 
167  det = tensor[-1]
168  norm_det = tensor[1]
169 
170  x_sc = tf.reduce_sum((norm_det[:,0::3]-tf.reduce_mean(norm_det[:,0::3],-1))*(det[:,0::3]-tf.reduce_mean(det[:,0::3],-1)),-1) / tf.reduce_sum((norm_det[:,0::3]-tf.reduce_mean(norm_det[:,0::3],-1))**2,-1)
171  y_sc = tf.reduce_sum((norm_det[:,1::3]-tf.reduce_mean(norm_det[:,1::3],-1))*(det[:,1::3]-tf.reduce_mean(det[:,1::3],-1)),-1) / tf.reduce_sum((norm_det[:,1::3]-tf.reduce_mean(norm_det[:,1::3],-1))**2,-1)
172  z_sc = tf.reduce_sum((norm_det[:,2::3]-tf.reduce_mean(norm_det[:,2::3],-1))*(det[:,2::3]-tf.reduce_mean(det[:,2::3],-1)),-1) / tf.reduce_sum((norm_det[:,2::3]-tf.reduce_mean(norm_det[:,2::3],-1))**2,-1)
173 
174  x_off = tf.reduce_mean(det[:,0::3],-1) - x_sc*tf.reduce_mean(norm_det[:,0::3],-1)
175  y_off = tf.reduce_mean(det[:,1::3],-1) - x_sc*tf.reduce_mean(norm_det[:,1::3],-1)
176  z_off = tf.reduce_mean(det[:,2::3],-1) - x_sc*tf.reduce_mean(norm_det[:,2::3],-1)
177 
178  norm_det[:,0::3] *= x_sc
179  norm_det[:,1::3] *= y_sc
180  norm_det[:,2::3] *= z_sc
181 
182  norm_det[:,0::3] += x_off
183  norm_det[:,1::3] += y_off
184  norm_det[:,2::3] += z_off
185 
186  htype = tensor[0][:,0]
187  hprob = tensor[2][:,0]
188 
189  return det, norm_det, htype, hprob
190 
191 
192 def head_landmarks_postprocess(tensor:[tf.Tensor]):
193  '''
194  Post-process for the head landmarks use-case
195 
196  Args
197  tensor list(tf.Tensor): shape [(batch,1,1,keypoints*3),(batch,1,1),(batch,1)] FLOAT32 outputs of the head landmarks
198 
199  Returns:
200  det (tf.Tensor): shape (batch, keypoints*3) FLOAT32 3D detections of the head landmarks in pixels
201  hprob (tf.Tensor): shape (batch,) FLOAT32 presence probability of the head
202  '''
203 
204  det = tensor[0][:,0,0] # shape (batch, 478)
205  hprob = tensor[2][:,0] # shape (batch,)
206 
207  return det, hprob
def heatmaps_spe_postprocess(tf.Tensor tensor)
Definition: postprocess.py:30
def yolo_mpe_postprocess(tf.Tensor tensor, int max_output_size=20, float iou_threshold=0.7, float score_threshold=0.25)
Definition: postprocess.py:127
def _padded_nms(tf.Tensor tensor, int max_output_size, float iou_threshold, float score_threshold)
Definition: postprocess.py:80
def head_landmarks_postprocess([tf.Tensor] tensor)
Definition: postprocess.py:192
def hand_landmarks_postprocess([tf.Tensor] tensor)
Definition: postprocess.py:153
def spe_postprocess(tf.Tensor tensor)
Definition: postprocess.py:57