import numpy as np def convert_int_to_freq(spec_ind, spec_height, min_freq, max_freq): spec_ind = spec_height - spec_ind return round( (spec_ind / float(spec_height)) * (max_freq - min_freq) + min_freq, 2 ) def extract_spec_slices(spec, pred_nms, params): """ Extracts spectrogram slices from spectrogram based on detected call locations. """ x_pos = pred_nms["x_pos"] y_pos = pred_nms["y_pos"] bb_width = pred_nms["bb_width"] bb_height = pred_nms["bb_height"] slices = [] # add 20% padding either side of call pad = bb_width * 0.2 x_pos_pad = x_pos - pad bb_width_pad = bb_width + 2 * pad for ff in range(len(pred_nms["det_probs"])): x_start = int(np.maximum(0, x_pos_pad[ff])) x_end = int( np.minimum( spec.shape[1] - 1, np.round(x_pos_pad[ff] + bb_width_pad[ff]) ) ) slices.append(spec[:, x_start:x_end].astype(np.float16)) return slices def get_feature_names(): feature_names = [ "duration", "low_freq_bb", "high_freq_bb", "bandwidth", "max_power_bb", "max_power", "max_power_first", "max_power_second", "call_interval", ] return feature_names def get_feats(spec, pred_nms, params): """ Extracts features from spectrogram based on detected call locations. Condsider re-extracting spectrogram for this to get better temporal resolution. For more possible features check out: https://github.com/YvesBas/Tadarida-D/blob/master/Manual_Tadarida-D.odt """ x_pos = pred_nms["x_pos"] y_pos = pred_nms["y_pos"] bb_width = pred_nms["bb_width"] bb_height = pred_nms["bb_height"] feature_names = get_feature_names() num_detections = len(pred_nms["det_probs"]) features = ( np.ones((num_detections, len(feature_names)), dtype=np.float32) * -1 ) for ff in range(num_detections): x_start = int(np.maximum(0, x_pos[ff])) x_end = int( np.minimum(spec.shape[1] - 1, np.round(x_pos[ff] + bb_width[ff])) ) # y low is the lowest freq but it will have a higher value due to array starting at 0 at top y_low = int(np.minimum(spec.shape[0] - 1, y_pos[ff])) y_high = int(np.maximum(0, np.round(y_pos[ff] - bb_height[ff]))) spec_slice = spec[:, x_start:x_end] if spec_slice.shape[1] > 1: features[ff, 0] = round( pred_nms["end_times"][ff] - pred_nms["start_times"][ff], 5 ) features[ff, 1] = int(pred_nms["low_freqs"][ff]) features[ff, 2] = int(pred_nms["high_freqs"][ff]) features[ff, 3] = int( pred_nms["high_freqs"][ff] - pred_nms["low_freqs"][ff] ) features[ff, 4] = int( convert_int_to_freq( y_high + spec_slice[y_high:y_low, :].sum(1).argmax(), spec.shape[0], params["min_freq"], params["max_freq"], ) ) features[ff, 5] = int( convert_int_to_freq( spec_slice.sum(1).argmax(), spec.shape[0], params["min_freq"], params["max_freq"], ) ) hlf_val = spec_slice.shape[1] // 2 features[ff, 6] = int( convert_int_to_freq( spec_slice[:, :hlf_val].sum(1).argmax(), spec.shape[0], params["min_freq"], params["max_freq"], ) ) features[ff, 7] = int( convert_int_to_freq( spec_slice[:, hlf_val:].sum(1).argmax(), spec.shape[0], params["min_freq"], params["max_freq"], ) ) if ff > 0: features[ff, 8] = round( pred_nms["start_times"][ff] - pred_nms["start_times"][ff - 1], 5, ) return features