def fcn_scoring_graph(input, config, mode): in_heatmap, pr_scores = input detections_per_image = pr_scores.shape[2] rois_per_image = KB.int_shape(pr_scores)[2] img_h, img_w = config.IMAGE_SHAPE[:2] batch_size = config.BATCH_SIZE num_classes = config.NUM_CLASSES heatmap_scale = config.HEATMAP_SCALE_FACTOR class_column = 4 score_column = 5 if mode == 'training': sequence_column = 6 norm_score_column = 7 else: dt_type_column = 6 sequence_column = 7 norm_score_column = 8 print('\n ') print('----------------------') print('>>> FCN Scoring Layer - mode:', mode) print('----------------------') logt('in_heatmap.shape ', in_heatmap) logt('pr_hm_scores.shape', pr_scores) # rois per image is determined by size of input tensor # detection mode: config.TRAIN_ROIS_PER_IMAGE # ground_truth : config.DETECTION_MAX_INSTANCES print(' detctions_per_image : ', detections_per_image, 'pr_scores shape', pr_scores.shape) print(' rois_per_image : ', rois_per_image) print(' config.DETECTION_MAX_INSTANCES : ', config.DETECTION_MAX_INSTANCES) print(' config.DETECTIONS_PER_CLASS : ', config.DETECTION_PER_CLASS) print(' sequence_column : ', sequence_column) print(' norm_score_column : ', norm_score_column) ##--------------------------------------------------------------------------------------------- ## Stack non_zero bboxes from PR_SCORES into pt2_dense ##--------------------------------------------------------------------------------------------- # pt2_ind shape : [?, 3] : [ {image_index, class_index , roi row_index }] # pt2_dense shape: [?, 11] : # pt2_dense[0:3] roi coordinates # pt2_dense[4] is class id # pt2_dense[5] is score from mrcnn # pt2_dense[6] is bbox sequence id # pt2_dense[7] is normalized score (per class) #----------------------------------------------------------------------------- pt2_sum = tf.reduce_sum(tf.abs(pr_scores[:, :, :, :class_column]), axis=-1) pt2_ind = tf.where(pt2_sum > 0) pt2_dense = tf.gather_nd(pr_scores, pt2_ind) logt('in_heatmap ', in_heatmap) logt('pr_scores.shape ', pr_scores) logt('pt2_sum shape ', pt2_sum) logt('pt2_ind shape ', pt2_ind) logt('pt2_dense shape ', pt2_dense) ##--------------------------------------------------------------------------------------------- ## Build mean and convariance tensors for bounding boxes ##--------------------------------------------------------------------------------------------- # bboxes_scaled = tf.to_int32(tf.round(pt2_dense[...,0:4])) / heatmap_scale bboxes_scaled = pt2_dense[..., 0:class_column] / heatmap_scale width = bboxes_scaled[:, 3] - bboxes_scaled[:, 1] # x2 - x1 height = bboxes_scaled[:, 2] - bboxes_scaled[:, 0] cx = bboxes_scaled[:, 1] + (width / 2.0) cy = bboxes_scaled[:, 0] + (height / 2.0) # means = tf.stack((cx,cy),axis = -1) covar = tf.stack((width * 0.5, height * 0.5), axis=-1) covar = tf.sqrt(covar) ##--------------------------------------------------------------------------------------------- ## build indices and extract heatmaps corresponding to each bounding boxes' class id ##--------------------------------------------------------------------------------------------- hm_indices = tf.cast(pt2_ind[:, :2], dtype=tf.int32) logt('hm_indices ', hm_indices) pt2_heatmaps = tf.transpose(in_heatmap, [0, 3, 1, 2]) logt('pt2_heatmaps', pt2_heatmaps) pt2_heatmaps = tf.gather_nd(pt2_heatmaps, hm_indices) logt('pt2_heatmaps', pt2_heatmaps) ##-------------------------------------------------------------------------------------------- ## (0) Generate scores using prob_grid and pt2_dense ##-------------------------------------------------------------------------------------------- old_style_scores = tf.map_fn( build_hm_score_v2, [pt2_heatmaps, bboxes_scaled, pt2_dense[:, norm_score_column]], dtype=tf.float32, swap_memory=True) logt('old_style_scores', old_style_scores) # old_style_scores = tf.scatter_nd(pt2_ind, old_style_scores, # [batch_size, num_classes, rois_per_image, KB.int_shape(old_style_scores)[-1]], # name = 'scores_scattered') # print(' old_style_scores :', old_style_scores.get_shape(), KB.int_shape(old_style_scores)) ##--------------------------------------------------------------------------------------------- ## generate score based on gaussian using bounding box masks ##--------------------------------------------------------------------------------------------- alt_scores_1 = tf.map_fn(build_hm_score_v3, [pt2_heatmaps, cy, cx, covar], dtype=tf.float32) logt('alt_scores_1 ', alt_scores_1) ##--------------------------------------------------------------------------------------------- ## Scatter back to per-class tensor / normalize by class ##--------------------------------------------------------------------------------------------- alt_scores_1_norm = tf.scatter_nd( pt2_ind, alt_scores_1, [ batch_size, num_classes, detections_per_image, KB.int_shape(alt_scores_1)[-1] ], name='alt_scores_1_norm') logt('alt_scores_1_scattered', alt_scores_1_norm) alt_scores_1_norm = normalize_scores(alt_scores_1_norm) logt('alt_scores_1_norm(by_class)', alt_scores_1_norm) alt_scores_1_norm = tf.gather_nd(alt_scores_1_norm, pt2_ind) logt('alt_scores_1_norm(by_image)', alt_scores_1_norm) ##--------------------------------------------------------------------------------------------- ## Normalize input heatmap normalization (per class) to calculate alt_score_2 ##-------------------------------------------------------------------------------------------- print( '\n Normalize heatmap within each class !-------------------------------------' ) in_heatmap_norm = tf.transpose(in_heatmap, [0, 3, 1, 2]) print(' in_heatmap_norm : ', in_heatmap_norm.get_shape(), 'Keras tensor ', KB.is_keras_tensor(in_heatmap_norm)) ## normalize in class normalizer = tf.reduce_max(in_heatmap_norm, axis=[-2, -1], keepdims=True) normalizer = tf.where(normalizer < 1.0e-15, tf.ones_like(normalizer), normalizer) in_heatmap_norm = in_heatmap_norm / normalizer # gauss_heatmap_sum_normalized = gauss_heatmap_sum / normalizer print(' normalizer shape : ', normalizer.shape) print(' normalized heatmap : ', in_heatmap_norm.shape, ' Keras tensor ', KB.is_keras_tensor(in_heatmap_norm)) ##--------------------------------------------------------------------------------------------- ## build indices and extract heatmaps corresponding to each bounding boxes' class id ## build alternative scores# based on normalized/sclaked clipped heatmap ##--------------------------------------------------------------------------------------------- hm_indices = tf.cast(pt2_ind[:, :2], dtype=tf.int32) logt('hm_indices shape', hm_indices) pt2_heatmaps = tf.gather_nd(in_heatmap_norm, hm_indices) logt('pt2_heatmaps', pt2_heatmaps) alt_scores_2 = tf.map_fn(build_hm_score_v3, [pt2_heatmaps, cy, cx, covar], dtype=tf.float32) logt('alt_scores_2', alt_scores_2) alt_scores_2_norm = tf.scatter_nd( pt2_ind, alt_scores_2, [ batch_size, num_classes, rois_per_image, KB.int_shape(alt_scores_2)[-1] ], name='alt_scores_2') logt('alt_scores_2(scattered)', alt_scores_2_norm) alt_scores_2_norm = normalize_scores(alt_scores_2_norm) logt('alt_scores_2_norm(by_class)', alt_scores_2_norm) alt_scores_2_norm = tf.gather_nd(alt_scores_2_norm, pt2_ind) logt('alt_scores_2_norm(by_image)', alt_scores_2_norm) #################################################################################################################### ##-------------------------------------------------------------------------------------------- ## Append alt_scores_1, alt_scores_1_norm to yield fcn_scores_dense ##-------------------------------------------------------------------------------------------- fcn_scores_dense = tf.concat([ pt2_dense[:, :norm_score_column + 1], old_style_scores, alt_scores_1, alt_scores_1_norm, alt_scores_2, alt_scores_2_norm ], axis=-1, name='fcn_scores_dense') logt('fcn_scores_dense ', fcn_scores_dense) ##--------------------------------------------------------------------------------------------- ## Scatter back to per-image tensor ##--------------------------------------------------------------------------------------------- seq_ids = tf.to_int32(rois_per_image - pt2_dense[:, sequence_column]) scatter_ind = tf.stack([hm_indices[:, 0], seq_ids], axis=-1, name='scatter_ind') fcn_scores_by_class = tf.scatter_nd( pt2_ind, fcn_scores_dense, [ batch_size, num_classes, detections_per_image, fcn_scores_dense.shape[-1] ], name='fcn_hm_scores') # fcn_scores_by_image = tf.scatter_nd(scatter_ind, fcn_scores_dense, # [batch_size, detections_per_image, fcn_scores_dense.shape[-1]], name='fcn_hm_scores_by_image') logt('seq_ids ', seq_ids) logt('sscatter_ids ', scatter_ind) logt('fcn_scores_by_class ', fcn_scores_by_class) # logt('fcn_scores_by_image ', fcn_scores_by_image) logt('complete') return fcn_scores_by_class
def build_heatmap_inference(in_tensor, config, names = None): ''' input: ------- pred_tensor: [ Bsz, Num_Classes, 200, 9 : {y1,x1,y2,x2, class, score, det_type, sequence_id, normalized_score}] output: ------- pr_heatmap (None, Heatmap-height, Heatmap_width, num_classes) pr_scores (None, num_classes, 200, 24) [batchSz, Detection_Max_instance, (y1,x1,y2,x2, class, score, det_type, sequence_id, normalized_score, scores-0: gaussian_sum, bbox_area, weighted_norm_sum scores-1: score, mask_sum, score/mask_sum, (score, mask_sum, score/mask_sum) normalized by class scores-2: score, mask_sum, score/mask_sum, (score, mask_sum, score/mask_sum) normalized by class ] ''' verbose = config.VERBOSE num_detections = config.DETECTION_MAX_INSTANCES img_h, img_w = config.IMAGE_SHAPE[:2] batch_size = config.BATCH_SIZE num_classes = config.NUM_CLASSES heatmap_scale = config.HEATMAP_SCALE_FACTOR grid_h, grid_w = config.IMAGE_SHAPE[:2] // heatmap_scale # rois_per_image = config.DETECTION_PER_CLASS rois_per_image = (in_tensor.shape)[2] CLASS_COLUMN = 4 SCORE_COLUMN = 5 DT_TYPE_COLUMN = 6 SEQUENCE_COLUMN = 7 NORM_SCORE_COLUMN = 8 if verbose: print('\n ') print(' > build_inference_heatmap() for ', names ) print(' in_tensor shape : ', in_tensor.shape) print(' num bboxes per class : ', rois_per_image ) print(' heatmap scale : ', heatmap_scale, 'Dimensions: w:', grid_w,' h:', grid_h) ##----------------------------------------------------------------------------- ## Stack non_zero bboxes from in_tensor into pt2_dense ##----------------------------------------------------------------------------- # pt2_ind shape is [?, 3]. pt2_dense shape is [?, 7] # pt2_ind[0] corresponds to image_index pt2_dense[0:3] roi coordinates # pt2_ind[1] corresponds to class_index pt2_dense[4] class id # pt2_ind[2] corresponds to roi row_index pt2_dense[5] score from mrcnn # pt2_dense[6] bbox sequence id # pt2_dense[7] per-class normalized score #----------------------------------------------------------------------------- pt2_sum = tf.reduce_sum(tf.abs(in_tensor[:,:,:,:4]), axis=-1) pt2_ind = tf.where(pt2_sum > 0) pt2_dense = tf.gather_nd( in_tensor, pt2_ind) logt('pt2_sum ', pt2_sum, verbose = verbose) logt('pt2_ind ', pt2_ind, verbose = verbose) logt('pt2_dense ', pt2_dense, verbose = verbose) ##----------------------------------------------------------------------------- ## Build mesh-grid to hold pixel coordinates ##----------------------------------------------------------------------------- X = tf.range(grid_w, dtype=tf.int32) Y = tf.range(grid_h, dtype=tf.int32) X, Y = tf.meshgrid(X, Y) # duplicate (repeat) X and Y into a batch_size x rois_per_image tensor ones = tf.ones([tf.shape(pt2_dense)[0] , 1, 1], dtype = tf.int32) rep_X = ones * X rep_Y = ones * Y if verbose: print(' X/Y shapes :', X.get_shape(), Y.get_shape()) print(' Ones: ', ones.shape) print(' ones_exp * X', ones.shape, '*', X.shape, '= ',rep_X.shape) print(' ones_exp * Y', ones.shape, '*', Y.shape, '= ',rep_Y.shape) # # stack the X and Y grids pos_grid = tf.to_float(tf.stack([rep_X,rep_Y], axis = -1)) logt('pos_grid before transpse ', pos_grid, verbose = verbose) pos_grid = tf.transpose(pos_grid,[1,2,0,3]) logt('pos_grid after transpose ', pos_grid, verbose = verbose) ##----------------------------------------------------------------------------- ## Build mean and convariance tensors for Multivariate Normal Distribution ##----------------------------------------------------------------------------- bboxes_scaled = pt2_dense[:,:4]/heatmap_scale width = bboxes_scaled[:,3] - bboxes_scaled[:,1] # x2 - x1 height = bboxes_scaled[:,2] - bboxes_scaled[:,0] cx = bboxes_scaled[:,1] + ( width / 2.0) cy = bboxes_scaled[:,0] + ( height / 2.0) means = tf.stack((cx,cy),axis = -1) covar = tf.stack((width * 0.5 , height * 0.5), axis = -1) covar = tf.sqrt(covar) ## Added 2019-05-12 to prevent NaN when bounding box is extremely small ## resulting in width or height being equal to zero covar = tf.where(covar < 1.0e-15, tf.ones_like(covar), covar) ##----------------------------------------------------------------------------- ## Compute Normal Distribution for bounding boxes ##----------------------------------------------------------------------------- tfd = tf.contrib.distributions mvn = tfd.MultivariateNormalDiag(loc = means, scale_diag = covar) prob_grid = mvn.prob(pos_grid) logt('Input to MVN.PROB: pos_grid (meshgrid) ', pos_grid, verbose = verbose) logt('Prob_grid shape from mvn.probe ',prob_grid, verbose = verbose) prob_grid = tf.transpose(prob_grid,[2,0,1]) logt('Prob_grid shape after tanspose ', prob_grid, verbose = verbose) logt('Output probabilities shape ' , prob_grid, verbose = verbose) ##-------------------------------------------------------------------------------------------- ## (0) Generate scores using prob_grid and pt2_dense - (NEW METHOD added 09-21-2018) ##-------------------------------------------------------------------------------------------- old_style_scores = tf.map_fn(build_hm_score_v2, [prob_grid, bboxes_scaled, pt2_dense[ :, NORM_SCORE_COLUMN ] ], dtype = tf.float32, swap_memory = True) old_style_scores = tf.scatter_nd(pt2_ind, old_style_scores, [batch_size, num_classes, rois_per_image, KB.int_shape(old_style_scores)[-1]], name = 'scores_scattered') logt('old_style_scores :', old_style_scores, verbose = verbose) ##---------------------------------------------------------------------------------------------------- ## Generate scores using same method as FCN, over the prob_grid ## using (prob_grid_clipped) as input is superfluous == RETURNS EXACT SAME Results AS prob_grid above ##---------------------------------------------------------------------------------------------------- # alt_scores_0 = tf.map_fn(build_hm_score_v3, [prob_grid, cy, cx,covar], dtype=tf.float32) # print(' alt_scores_0 : ', KB.int_shape(alt_scores_0), ' Keras tensor ', KB.is_keras_tensor(alt_scores_0) ) # alt_scores_0 = tf.scatter_nd(pt2_ind, alt_scores_0, # [batch_size, num_classes, rois_per_image, KB.int_shape(alt_scores_0)[-1]], name = 'alt_scores_0') ##--------------------------------------------------------------------------------------------- ## (NEW STEP - Clipped heatmaps) ## (1) Clip heatmap to region surrounding Cy,Cx and Covar X, Y ## Similar ro what is being done for gt_heatmap in CHMLayerTarget ##--------------------------------------------------------------------------------------------- prob_grid_clipped = tf.map_fn(clip_heatmap, [prob_grid, cy,cx, covar], dtype = tf.float32, swap_memory = True) logt(' prob_grid_clipped : ', prob_grid_clipped, verbose = verbose) ##--------------------------------------------------------------------------------------------- ## (2) apply normalization per bbox heatmap instance --> move to [0,1] range ##--------------------------------------------------------------------------------------------- logt('\n normalization ------------------------------------------------------', verbose = verbose) normalizer = tf.reduce_max(prob_grid_clipped, axis=[-2,-1], keepdims = True) normalizer = tf.where(normalizer < 1.0e-15, tf.ones_like(normalizer), normalizer) logt(' normalizer : ', normalizer, verbose = verbose) prob_grid_cns = prob_grid_clipped / normalizer logt(' prob_grid_cns: clipped/normed/scaled : ', prob_grid_cns, verbose = verbose) ## replace above lines with lines below ## x_max = tf.reduce_max(prob_grid_clipped, axis=[-2,-1], keepdims = True) ## x_min = tf.reduce_min(prob_grid_clipped, axis=[-2,-1], keepdims = True) ##logt(' Reduce Max Shape: ', x_max, verbose = verbose) ##logt(' Reduce Min Shape: ', x_min, verbose = verbose) ## prob_grid_cns = (prob_grid_clipped - x_min) / (x_max - x_min) ## logt(' prob_grid_cns: clipped/normed/scaled : ', prob_grid_cns, verbose = verbose) ##--------------------------------------------------------------------------------------------- ## (3) multiply normalized heatmap by normalized score in in_tensor/ (pt2_dense NORM_SCORE_COLUMN) ## broadcasting : https://stackoverflow.com/questions/49705831/automatic-broadcasting-in-tensorflow ##--------------------------------------------------------------------------------------------- prob_grid_cns = tf.transpose(tf.transpose(prob_grid_cns) * pt2_dense[ :, NORM_SCORE_COLUMN ]) logt(' prob_grid_cns: clipped/normed/scaled : ', prob_grid_cns, verbose = verbose) ##--------------------------------------------------------------------------------------------- ## - Build alternative scores based on normalized/scaled/clipped heatmap ##--------------------------------------------------------------------------------------------- alt_scores_1 = tf.map_fn(build_hm_score_v3, [prob_grid_cns, cy, cx,covar], dtype=tf.float32) logt('alt_scores_1 ', alt_scores_1, verbose = verbose) alt_scores_1 = tf.scatter_nd(pt2_ind, alt_scores_1, [batch_size, num_classes, rois_per_image, KB.int_shape(alt_scores_1)[-1]], name = 'alt_scores_1') logt('alt_scores_1(by class) ', alt_scores_1, verbose = verbose) alt_scores_1_norm = normalize_scores(alt_scores_1) logt('alt_scores_1_norm(by_class) ', alt_scores_1_norm, verbose = verbose) # alt_scores_1_norm = tf.gather_nd(alt_scores_1_norm, pt2_ind) # print(' alt_scores_1_norm(by_image) : ', alt_scores_1_norm.shape, KB.int_shape(alt_scores_1_norm)) ##------------------------------------------------------------------------------------- ## (3) scatter out the probability distributions based on class ##------------------------------------------------------------------------------------- gauss_heatmap = tf.scatter_nd(pt2_ind, prob_grid_cns, [batch_size, num_classes, rois_per_image, grid_w, grid_h], name = 'gauss_scatter') logt('\n Scatter out the probability distributions based on class --------------', verbose = verbose) logt('pt2_ind shape ', pt2_ind , verbose = verbose) logt('prob_grid_clippped ', prob_grid_cns, verbose = verbose) logt('gauss_heatmap ', gauss_heatmap, verbose = verbose) # batch_sz , num_classes, num_rois, image_h, image_w ##------------------------------------------------------------------------------------- ## Construction of Gaussian Heatmap output using Reduce SUM ## ## (4) SUM : Reduce and sum up gauss_heatmaps by class ## (5) heatmap normalization (per class) ## (6) Transpose heatmap to shape required for FCN ##------------------------------------------------------------------------------------- gauss_heatmap_sum = tf.reduce_sum(gauss_heatmap, axis=2, name='gauss_heatmap_sum') logt('\n Reduce SUM based on class and normalize within each class -----------------------', verbose = verbose) logt('gaussian_heatmap_sum ', gauss_heatmap_sum , verbose = verbose) ## normalize in class normalizer = tf.reduce_max(gauss_heatmap_sum, axis=[-2,-1], keepdims = True) normalizer = tf.where(normalizer < 1.0e-15, tf.ones_like(normalizer), normalizer) gauss_heatmap_sum = gauss_heatmap_sum / normalizer logt('normalizer shape : ', normalizer, verbose = verbose) logt('normalized heatmap : ', gauss_heatmap_sum, verbose = verbose) ## replaced above with following two lines::: 5-30-19 ## gauss_heatmap_sum = tf.transpose(gauss_heatmap_sum, [0,2,3,1]) ## gauss_heatmap_sum = normalize_heatmaps(gauss_heatmap_sum) ## logt('normalized heatmap : ', gauss_heatmap_sum, verbose = verbose) ##--------------------------------------------------------------------------------------------- ## Score on reduced sum heatmaps. ## ## build indices and extract heatmaps corresponding to each bounding boxes' class id ## build alternative scores# based on normalized/sclaked clipped heatmap ##--------------------------------------------------------------------------------------------- hm_indices = tf.cast(pt2_ind[:, :2],dtype=tf.int32) logt('hm_indices ', hm_indices, verbose = verbose) pt2_heatmaps = tf.gather_nd(gauss_heatmap_sum, hm_indices ) ## added5-30-2019 to replace above line ## pt2_heatmaps = tf.transpose(gauss_heatmap_sum, [0,3,1,2]) ## pt2_heatmaps = tf.gather_nd(pt2_heatmaps, hm_indices ) logt('pt2_heatmaps ', pt2_heatmaps, verbose = verbose) alt_scores_2 = tf.map_fn(build_hm_score_v3, [pt2_heatmaps, cy, cx,covar], dtype=tf.float32) logt(' alt_scores_2 : ', alt_scores_2, verbose = verbose) alt_scores_2 = tf.scatter_nd(pt2_ind, alt_scores_2, [batch_size, num_classes, rois_per_image, KB.int_shape(alt_scores_2)[-1]], name = 'alt_scores_2') logt('alt_scores_2(scattered) ', alt_scores_2, verbose = verbose) alt_scores_2_norm = normalize_scores(alt_scores_2) logt('alt_scores_2_norm(by_class) ', alt_scores_2_norm, verbose = verbose) ##--------------------------------------------------------------------------------------------- ## (6) Transpose heatmaps to shape required for FCN [batchsize , width, height, num_classes] ##--------------------------------------------------------------------------------------------- gauss_heatmap_sum = tf.transpose(gauss_heatmap_sum ,[0,2,3,1], name = names[0]) logt(' gauss_heatmap_sum (final) ', gauss_heatmap_sum, verbose = verbose) # gauss_heatmap_sum_normalized = tf.transpose(gauss_heatmap_sum_normalized,[0,2,3,1], name = names[0]+'_norm') # print(' reshaped heatmap normalized : ', gauss_heatmap_sum_normalized.shape,' Keras tensor ', KB.is_keras_tensor(gauss_heatmap_sum_normalized) ) # gauss_heatmap_max = tf.transpose(gauss_heatmap_max ,[0,2,3,1], name = names[0]+'_max') # print(' reshaped heatmap_max : ', gauss_heatmap_max.shape,' Keras tensor ', KB.is_keras_tensor(gauss_heatmap_max) ) # gauss_heatmap_max_normalized = tf.transpose(gauss_heatmap_max_normalized,[0,2,3,1], name = names[0]+'_max_norm') # print(' reshaped heatmap_max normalized: ', gauss_heatmap_max_normalized.shape,' Keras tensor ', KB.is_keras_tensor(gauss_heatmap_max_normalized) ) ##-------------------------------------------------------------------------------------------- ## APPEND ALL SCORES TO input score tensor TO YIELD output scores tensor ##-------------------------------------------------------------------------------------------- gauss_scores = tf.concat([in_tensor, old_style_scores, alt_scores_1, alt_scores_1_norm, alt_scores_2, alt_scores_2_norm], axis = -1,name = names[0]+'_scores') logt(' gauss_scores : ', gauss_scores, verbose = verbose) logt(' complete', verbose = verbose) return gauss_heatmap_sum, gauss_scores
def build_gt_heatmap(in_tensor, config, names=None): verbose = config.VERBOSE num_detections = config.DETECTION_MAX_INSTANCES img_h, img_w = config.IMAGE_SHAPE[:2] batch_size = config.BATCH_SIZE num_classes = config.NUM_CLASSES heatmap_scale = config.HEATMAP_SCALE_FACTOR grid_h, grid_w = config.IMAGE_SHAPE[:2] // heatmap_scale # rois per image is determined by size of input tensor # detection mode: config.TRAIN_ROIS_PER_IMAGE # ground_truth : config.DETECTION_MAX_INSTANCES # strt_cls = 0 if rois_per_image == 32 else 1 # rois_per_image = config.DETECTION_PER_CLASS rois_per_image = (in_tensor.shape)[2] if verbose: print('\n ') print(' > build_heatmap() for ', names) print(' in_tensor shape : ', in_tensor.shape) print(' num bboxes per class : ', rois_per_image) print(' heatmap scale : ', heatmap_scale, 'Dimensions: w:', grid_w, ' h:', grid_h) ##----------------------------------------------------------------------------- ## Stack non_zero bboxes from in_tensor into pt2_dense ##----------------------------------------------------------------------------- # pt2_ind shape is [?, 3]. # pt2_ind[0] corresponds to image_index # pt2_ind[1] corresponds to class_index # pt2_ind[2] corresponds to roi row_index # pt2_dense shape is [?, 7] # pt2_dense[0:3] roi coordinates # pt2_dense[4] is class id # pt2_dense[5] is score from mrcnn # pt2_dense[6] is bbox sequence id # pt2_dense[7] is normalized score (per class) #----------------------------------------------------------------------------- pt2_sum = tf.reduce_sum(tf.abs(in_tensor[:, :, :, :4]), axis=-1) pt2_ind = tf.where(pt2_sum > 0) pt2_dense = tf.gather_nd(in_tensor, pt2_ind) logt('pt2_sum ', pt2_sum, verbose=verbose) logt('pt2_ind ', pt2_ind, verbose=verbose) logt('pt2_dense ', pt2_dense, verbose=verbose) ##----------------------------------------------------------------------------- ## Build mesh-grid to hold pixel coordinates ##----------------------------------------------------------------------------- # X = tf.range(grid_w, dtype=tf.int32) # Y = tf.range(grid_h, dtype=tf.int32) # X, Y = tf.meshgrid(X, Y) # duplicate (repeat) X and Y into a batch_size x rois_per_image tensor # print(' X/Y shapes :', X.get_shape(), Y.get_shape()) # ones = tf.ones([tf.shape(pt2_dense)[0] , 1, 1], dtype = tf.int32) # rep_X = ones * X # rep_Y = ones * Y # print(' Ones: ', ones.shape) # print(' ones_exp * X', ones.shape, '*', X.shape, '= ',rep_X.shape) # print(' ones_exp * Y', ones.shape, '*', Y.shape, '= ',rep_Y.shape) # # stack the X and Y grids # pos_grid = tf.to_float(tf.stack([rep_X,rep_Y], axis = -1)) # print(' pos_grid before transpose : ', pos_grid.get_shape()) # pos_grid = tf.transpose(pos_grid,[1,2,0,3]) # print(' pos_grid after transpose : ', pos_grid.get_shape()) ##----------------------------------------------------------------------------- ## Build mean and convariance tensors for Multivariate Normal Distribution ##----------------------------------------------------------------------------- pt2_dense_scaled = pt2_dense[:, :4] / heatmap_scale width = pt2_dense_scaled[:, 3] - pt2_dense_scaled[:, 1] # x2 - x1 height = pt2_dense_scaled[:, 2] - pt2_dense_scaled[:, 0] cx = pt2_dense_scaled[:, 1] + (width / 2.0) cy = pt2_dense_scaled[:, 0] + (height / 2.0) means = tf.stack((cx, cy), axis=-1) covar = tf.stack((width * 0.5, height * 0.5), axis=-1) covar = tf.sqrt(covar) ##----------------------------------------------------------------------------- ## Compute Normal Distribution for bounding boxes ##----------------------------------------------------------------------------- prob_grid = tf.ones([tf.shape(pt2_dense)[0], grid_h, grid_w], dtype=tf.float32) logt('Prob_grid ', prob_grid, verbose=verbose) # tfd = tf.contrib.distributions # mvn = tfd.MultivariateNormalDiag(loc = means, scale_diag = covar) # prob_grid = mvn.prob(pos_grid) # print(' >> input to MVN.PROB: pos_grid (meshgrid) shape: ', pos_grid.shape) # print(' box_dims: ', box_dims.shape) # print(' Prob_grid shape from mvn.probe: ', prob_grid.shape) # prob_grid = tf.transpose(prob_grid,[2,0,1]) # print(' Prob_grid shape after tanspose: ', prob_grid.shape) # print(' << output probabilities shape : ', prob_grid.shape) #-------------------------------------------------------------------------------- # Kill distributions of NaN boxes (resulting from bboxes with height/width of zero # which cause singular sigma cov matrices #-------------------------------------------------------------------------------- # prob_grid = tf.where(tf.is_nan(prob_grid), tf.zeros_like(prob_grid), prob_grid) #--------------------------------------------------------------------------------------------- # (1) apply normalization per bbox heatmap instance #--------------------------------------------------------------------------------------------- # print('\n normalization ------------------------------------------------------') # normalizer = tf.reduce_max(prob_grid, axis=[-2,-1], keepdims = True) # normalizer = tf.where(normalizer < 1.0e-15, tf.ones_like(normalizer), normalizer) # print(' normalizer : ', normalizer.shape) # prob_grid_norm = prob_grid / normalizer #--------------------------------------------------------------------------------------------- # (2) multiply normalized heatmap by normalized score in i n_tensor/ (pt2_dense column 7) # broadcasting : https://stackoverflow.com/questions/49705831/automatic-broadcasting-in-tensorflow #--------------------------------------------------------------------------------------------- # prob_grid_norm_scaled = tf.transpose(tf.transpose(prob_grid_norm) * pt2_dense[:,7]) # print(' prob_grid_norm_scaled : ', prob_grid_norm_scaled.shape) ##--------------------------------------------------------------------------------------------- ## (NEW STEP) Clip heatmap to region surrounding Cy,Cx and Covar X, Y ##--------------------------------------------------------------------------------------------- prob_grid_clipped = tf.map_fn(clip_heatmap, [prob_grid, cy, cx, covar], dtype=tf.float32, swap_memory=True) logt('prob_grid_clipped ', prob_grid_clipped, verbose=verbose) ##-------------------------------------------------------------------------------------------- ## (0) Generate scores using prob_grid and pt2_dense - (NEW METHOD added 09-21-2018) ## pt2_dense[:,7] is the per-class-normalized score from in_tensor ## ## 11-27-2018: (note - here, build_hm_score_v2 is being applied to prob_grid_clipped, ## unlilke chm_layer) - Changed to prob_grid to make it consistent with chm_layer.py ## ## When using prob_grid: ## [ 1.0000 1.0000 138.0000 1.0000 4615.0000 4531.1250 4615.0000 ## [ 3.0000 1.0000 179.0000 1.0000 570.0000 547.5000 570.0000 ## ## When using prob_grid_clipped: ## [ 1.0000 1.0000 138.0000 1.0000 144.0000 4531.1250 144.0000 ## [ 3.0000 1.0000 179.0000 1.0000 56.0000 547.5000 56.0000 ##-------------------------------------------------------------------------------------------- old_style_scores = tf.map_fn( build_hm_score_v2, [prob_grid, pt2_dense_scaled, pt2_dense[:, 7]], dtype=tf.float32, swap_memory=True) old_style_scores = tf.scatter_nd( pt2_ind, old_style_scores, [batch_size, num_classes, rois_per_image, 3], name='scores_scattered') logt('old_style_scores ', old_style_scores, verbose=verbose) ##--------------------------------------------------------------------------------------------- ## - Build alternative scores based on normalized/scaled/clipped heatmap ##--------------------------------------------------------------------------------------------- alt_scores_1 = tf.map_fn(build_hm_score_v3, [prob_grid_clipped, cy, cx, covar], dtype=tf.float32) logt('alt_scores_1 ', alt_scores_1, verbose=verbose) alt_scores_1 = tf.scatter_nd(pt2_ind, alt_scores_1, [ batch_size, num_classes, rois_per_image, KB.int_shape(alt_scores_1)[-1] ], name='alt_scores_1') alt_scores_1_norm = normalize_scores(alt_scores_1) logt('alt_scores_1(by class) ', alt_scores_1, verbose=verbose) logt('alt_scores_1_norm(by_class) ', alt_scores_1_norm, verbose=verbose) ##------------------------------------------------------------------------------------- ## (3) scatter out the probability distribution heatmaps based on class ##------------------------------------------------------------------------------------- gauss_heatmap = tf.scatter_nd( pt2_ind, prob_grid_clipped, [batch_size, num_classes, rois_per_image, grid_w, grid_h], name='gauss_heatmap') logt( '\n Scatter out the probability distributions based on class --------------' ) logt('pt2_ind ', pt2_ind, verbose=verbose) logt('prob_grid ', prob_grid, verbose=verbose) logt('gauss_heatmap ', gauss_heatmap, verbose=verbose) # batch_sz , num_classes, num_rois, image_h, image_w ##------------------------------------------------------------------------------------- ## (4) MAX : Reduce_MAX up gauss_heatmaps by class ## Since all values are set to '1' in the 'heatmap', there is no need to ## sum or normalize. We Reduce_max on the class axis, and as a result the ## correspoding areas in the heatmap are set to '1' ##------------------------------------------------------------------------------------- gauss_heatmap = tf.reduce_max(gauss_heatmap, axis=2, name='gauss_heatmap') logt( '\n Reduce MAX based on class -------------------------------------', verbose=verbose) logt(' gaussian_heatmap : ', gauss_heatmap, verbose=verbose) #--------------------------------------------------------------------------------------------- # (5) heatmap normalization # normalizer is set to one when the max of class is zero # this prevents elements of gauss_heatmap_norm computing to nan #--------------------------------------------------------------------------------------------- # print('\n normalization ------------------------------------------------------') # normalizer = tf.reduce_max(gauss_heatmap, axis=[-2,-1], keepdims = True) # normalizer = tf.where(normalizer < 1.0e-15, tf.ones_like(normalizer), normalizer) # gauss_heatmap_norm = gauss_heatmap / normalizer # print(' normalizer shape : ', normalizer.shape) # print(' gauss norm : ', gauss_heatmap_norm.shape ,' Keras tensor ', KB.is_keras_tensor(gauss_heatmap_norm) ) ##--------------------------------------------------------------------------------------------- ## build indices and extract heatmaps corresponding to each bounding boxes' class id ## build alternative scores# based on normalized/sclaked clipped heatmap ##--------------------------------------------------------------------------------------------- hm_indices = tf.cast(pt2_ind[:, :2], dtype=tf.int32) pt2_heatmaps = tf.gather_nd(gauss_heatmap, hm_indices) logt('hm_indices ', hm_indices, verbose=verbose) logt('pt2_heatmaps ', pt2_heatmaps, verbose=verbose) alt_scores_2 = tf.map_fn(build_hm_score_v3, [pt2_heatmaps, cy, cx, covar], dtype=tf.float32) logt('alt_scores_2 ', alt_scores_2, verbose=verbose) alt_scores_2 = tf.scatter_nd(pt2_ind, alt_scores_2, [ batch_size, num_classes, rois_per_image, KB.int_shape(alt_scores_2)[-1] ], name='alt_scores_2') alt_scores_2_norm = normalize_scores(alt_scores_2) logt('alt_scores_2(by class) : ', alt_scores_2, verbose=verbose) logt('alt_scores_2_norm(by_class) : ', alt_scores_2_norm, verbose=verbose) ##-------------------------------------------------------------------------------------------- ## Transpose tensor to [BatchSz, Height, Width, Num_Classes] ##-------------------------------------------------------------------------------------------- gauss_heatmap = tf.transpose(gauss_heatmap, [0, 2, 3, 1], name=names[0]) # gauss_heatmap_norm = tf.transpose(gauss_heatmap_norm,[0,2,3,1], name = names[0]+'_norm') # print(' gauss_heatmap_norm : ', gauss_heatmap_norm.shape,' Keras tensor ', KB.is_keras_tensor(gauss_heatmap_norm) ) # print(' complete') ##-------------------------------------------------------------------------------------------- ## APPEND ALL SCORES TO input score tensor TO YIELD output scores tensor ##-------------------------------------------------------------------------------------------- gauss_scores = tf.concat([ in_tensor, old_style_scores, alt_scores_1, alt_scores_1_norm, alt_scores_2, alt_scores_2_norm ], axis=-1, name=names[0] + '_scores') # alt_scores_2[...,:3], alt_scores_3], logt('gauss_heatmap ', gauss_heatmap, verbose=verbose) logt('gauss_scores', gauss_scores, verbose=verbose) logt('complete ', verbose=verbose) return gauss_heatmap, gauss_scores