File size: 3,653 Bytes
9ace58a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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