scoring.py 18 KB
Newer Older
Anthony Larcher's avatar
Anthony Larcher committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
#
# This file is part of s4d.
#
# s4d is a python package for speaker diarization.
# Home page: http://www-lium.univ-lemans.fr/s4d/
#
# s4d is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# s4d is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with s4d.  If not, see <http://www.gnu.org/licenses/>.


"""
Anthony Larcher's avatar
Anthony Larcher committed
23
Copyright 2014-2020 Sylvain Meignier
Anthony Larcher's avatar
Anthony Larcher committed
24
25
"""

Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
26
27
import copy
import numpy as np
Anthony Larcher's avatar
Anthony Larcher committed
28
29
30

# from sklearn.utils.linear_assignment_ import linear_assignment deprecated
from scipy.optimize import linear_sum_assignment
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
31

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
32

Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
33
34
35
36
37
38
39
40
41
42
class DER:
    """ Class to compute a s4d error rate.
    Error is computed at a rate of 10ms.

    :attr uem_set: a set of index to evaluate
    :attr collar: is the no-score zone around reference speaker segment
    boundaries.  (Speaker Diarization output is not evaluated within +/- collar
    seconds of a reference speaker segment boundary.)
    :attr uem_set_collar: a set of index on with the collar is applied
    :attr length: the last index in the reference
43
    :attr ref_cluster_list: the list of reference cluster_list
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
44
    :attr ref_mat: the numpy.ndarry of reference indexes, each row correspond
45
46
    to a reference cluster (same order as ref_cluster_list)
    :attr hyp_cluster_list: the list of hypothesis cluster_list
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
47
    :attr hyp_mat: the numpy.ndarry of reference indexes, each row correspond
48
    to a hypothesis cluster (same order as ref_cluster_list)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
49
50
    :attr conf: the confusion matrix
    :attr assigned: a numpy.ndarray given the association between the references and hypotheses
51
    :attr not_assigned: a list of hypothesis cluster_list not associated to a reference cluster
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
52
53

    """
54
    def __init__(self, hyp_diarization, ref_diarization, uem_diarization = None, collar=0, no_overlap=False):
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
55
56
        """

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
57
58
        :param hyp_diarization: a hypothesis Diar object
        :param ref_diarization: a reference Diar object
59
        :param uem_diarization: a uem Diar object
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
60
61
62
63
64
        :param collar: is the no-score zone around reference speaker segments
        :param no_overlap: to limit scoring to those time regions in which only
        a single speaker is speaking

        """
65
        uem = uem_diarization
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
66
        self.collar = collar
Sylvain Meignier's avatar
Sylvain Meignier committed
67
        self.match = None
68
69
        if uem_diarization is not None:
            uem = copy.deepcopy(uem_diarization)
70
71
            if len(uem.unique('cluster')) > 1:
                raise Exception('UEM contains more than one cluster')
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
72
73
74
75
            if len(uem.unique('show')) > 1:
                raise Exception('UEM contains more than one show')
            self.uem_set = set(uem.features())

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
76
        ref = copy.deepcopy(ref_diarization)
Sylvain Meignier's avatar
Sylvain Meignier committed
77
        ref.pack()
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
78
79
80
81
82
        lst = ref.unique('show')
        if len(lst) > 1:
            raise Exception('REF contains more than one show')
        else:
            self.show = lst[0]
83
        if uem_diarization is None:
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
84
            print('uem from ref')
85
            self.uem_set = set([i for i in range(min(ref.unique('start')), max(ref.unique('stop')))])
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
86
87
88
89

        if collar > 0:
            rem = list()
            for row in ref:
90
91
                rem += [i for i in range(row['start']-collar, row['start']+collar)]
                rem += [i for i in range(row['stop']-collar, row['stop']+collar)]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
92
93
94
95
96
            self.uem_set_collar = self.uem_set - set(rem)
        else:
            self.uem_set_collar = self.uem_set

        self.length = max(self.uem_set_collar)+1
97
98
99
        self.ref_cluster_list = ref.unique('cluster')
        ref_idx = ref.features_by_cluster()
        self.ref_mat = self._to_bool(ref_idx, self.ref_cluster_list, self.uem_set_collar)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
100
101
102
103
104
105
        if no_overlap:
            print('remove overlap')
            sum = np.sum(self.ref_mat, axis=0)
            idx = np.squeeze(np.asarray(np.argwhere(sum == 1))).tolist()
            self.uem_set_collar = self.uem_set & set(idx)
            self.length = max(self.uem_set_collar)+1
106
            self.ref_mat = self._to_bool(ref_idx, self.ref_cluster_list, self.uem_set_collar)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
107

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
108
        hyp = copy.deepcopy(hyp_diarization)
Sylvain Meignier's avatar
Sylvain Meignier committed
109
        hyp.pack()
110
111
112
        self.hyp_cluster_list = hyp.unique('cluster')
        hyp_idx = hyp.features_by_cluster()
        self.hyp_mat = self._to_bool(hyp_idx, self.hyp_cluster_list, self.uem_set_collar)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
113
114
115
116
117

        self.conf = None
        self.assigned = None
        self.not_assigned = None

118
119
    def _to_bool(self, map, cluster_list, uem_set):
        """Transform the dict of segments indexed by cluster into a matrix
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
120
121
122
        of booleans.

        :param map: dict of segments indexed by speaker
123
        :param cluster_list: list of cluster_list
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
124
125
126
        :param uem_set: the segments is filtered according to the uem


127
        :return: a numpy.ndarray object of shape (#cluster, #features)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
128
        """
129
        mat = np.zeros((len(cluster_list), self.length))
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
130

131
132
        for i, cluster in enumerate(cluster_list):
            mat[i, list(set(map[cluster]) & uem_set)] = True
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
133
134
135
136
137
138
139
        return mat

    def confusion(self):
        """Compute the confusion matrix between reference and hypothesis

        :return: a numpy.ndarray object of shape (#reference, #hypothesis)
        """
Sylvain Meignier's avatar
stable    
Sylvain Meignier committed
140
141
142
143
144
145
146
147
        #nh = len(self.hyp_cluster_list)
        #nr = len(self.ref_cluster_list)
        #self.conf = np.full((nr, nh), 0.0)
        #for i in range(nr):
        #    for j in range(nh):
        #        self.conf[i, j] = np.sum(np.logical_and(self.ref_mat[i], self.hyp_mat[j]))

        self.conf = np.dot(self.ref_mat, self.hyp_mat.transpose())
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
148
149
150
151
152
153
154
155
156
157
158
159

        return self.conf

    def confusion2str(self, all_value=False):
        """Generate a string version of the confusion matrix

        :param all_value: if false, only the confision score upper to 0 is
        generated

        :return: a str
        """
        mr = mh = 0
160
        for ch in self.ref_cluster_list+['reference']:
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
161
162
            if mr < len(ch):
                mr = len(ch)
163
        for ch in self.hyp_cluster_list+['hypothesis']:
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
164
165
166
167
168
169
            if mh < len(ch):
                mh = len(ch)

        f = '{:'+str(mr)+'} {:'+str(mh)+'} {:>9}\n'
        line = f.format('reference', 'hypothesis', 'conf')
        f = '{:'+str(mr)+'} {:'+str(mh)+'} {:>8.2f}s\n'
170
171
        nh = len(self.hyp_cluster_list)
        nr = len(self.ref_cluster_list)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
172
173
174
175

        for i in range(nr):
            for j in range(nh):
                if self.conf[i, j] > 0 or all_value == True:
176
177
                    r = self.ref_cluster_list[i]
                    h = self.hyp_cluster_list[j]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
178
179
180
181
182
                    s = self.conf[i, j]/100
                    line += f.format(r, h, s)
        return line

    def assignment(self):
183
        """Compute the reference to hypothese association using the hungarian
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
184
185
        algorithm

186
        :return: a dict object with key equal to (reference cluster, hypothesis cluster)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
187
        and value equal to the confusion score
188
        :return: a list object containing the hypothesis cluster_list that are not assigned
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
189
        """
Anthony Larcher's avatar
Anthony Larcher committed
190
        self.assigned = linear_sum_assignment(-1.0*self.conf)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
191
192
        name_assigned = dict()
        self.match = 0;
193
        self.not_assigned = copy.deepcopy(self.hyp_cluster_list)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
194
195

        for i in range(self.assigned.shape[0]):
196
197
            r = self.ref_cluster_list[self.assigned[i, 0]]
            h = self.hyp_cluster_list[self.assigned[i, 1]]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
198
199
200
201
202
203
204
205
206
207
208
209
            s = self.conf[self.assigned[i, 0], self.assigned[i, 1]]
            if s > 0:
                self.not_assigned.remove(h)
                name_assigned[(r, h)] = s
                self.match += s
            else:
                self.assigned[i, 0] = -1
                self.assigned[i, 1] = -1

        return name_assigned, self.not_assigned

    def set_assignment(self, name_assigned):
210
        """Set assignment from an external source (from a collection of cluster_list)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
211
212

        :param: name_assigned is a dict object with key equal to
213
        (reference cluster, hypothesis cluster) and value equal to the confusion
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
214
215
216
        score
        """
        i = 0;
217
        self.assigned = np.ones((len(self.ref_cluster_list), 2)) * -1
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
218
219

        for key in name_assigned:
Sylvain Meignier's avatar
Sylvain Meignier committed
220
            # print(key)
221
222
223
            if key[0] in self.ref_cluster_list and key[1] in self.hyp_cluster_list:
                r = self.ref_cluster_list.index(key[0])
                h = self.hyp_cluster_list.index(key[1])
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
224
225
226
227
228
229
230
231
232
233
234
235
236
                self.assigned[i, 0] = r
                self.assigned[i, 1] = h
                i += 1

    def assignment2str(self):
        """Generate a string version of the assignment matrix attribut

        :return: a str
        """
        if self.assigned is None:
            return "Assignment is not set"
        mr = mh = 0

237
        for ch in self.ref_cluster_list+['reference']:
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
238
239
            if mr < len(ch):
                mr = len(ch)
240
        for ch in self.hyp_cluster_list+['hypothesis']:
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
241
242
243
244
245
246
247
248
            if mh < len(ch):
                mh = len(ch)
        f = '{:'+str(mr)+'} {:'+str(mh)+'} {:>9}\n'
        line = f.format('reference', 'hypothesis', 'matching')
        f = '{:'+str(mr)+'} {:'+str(mh)+'} {:>8.2f}s {:d} {:d}\n'

        for i in range(self.assigned.shape[0]):
            if self.assigned[i, 0] >= 0:
249
250
                r = self.ref_cluster_list[self.assigned[i, 0]]
                h = self.hyp_cluster_list[self.assigned[i, 1]]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
                s = self.conf[self.assigned[i, 0], self.assigned[i, 1]]/100
                line += f.format(r, h, s, self.assigned[i, 0], self.assigned[i, 1])

        r = 'None'
        s = 0.0
        for h in self.not_assigned:
            line += f.format(r, h, s, -1, -1)

        return line

    def _correct(self):
        """compute the correct decision

        :return: a numpy.ndarray
        """
        c = np.zeros(self.length)
        for i in range(self.assigned.shape[0]):
            if self.assigned[i, 0] >= 0 and self.assigned[i, 1] >= 0:
                diff = np.logical_and(self.ref_mat[self.assigned[i, 0]], self.hyp_mat[self.assigned[i, 1]])
                c += diff
        return c

    def error(self):
        """compute the s4d error

        :return: a DER_result object
        """
        h_sum = np.sum(self.hyp_mat, axis=0)
        r_sum = np.sum(self.ref_mat, axis=0)
        c = self._correct()

        res = DER_result(self.show)
        res.uem_time = len(self.uem_set)
        res.uem_with_collar_time = len(self.uem_set_collar)
        res.uem_collar_time = res.uem_time - res.uem_with_collar_time

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
        z = np.zeros(r_sum.shape[0])

        mrh = np.sum(np.maximum(r_sum - h_sum, z))
        mhr = np.sum(np.maximum(h_sum - r_sum, z))

        res.speech_time = np.count_nonzero(r_sum)
        res.sns_miss_time = np.count_nonzero(np.logical_and(r_sum, np.logical_not(h_sum)))
        res.sns_fa_time = np.count_nonzero(np.logical_and(h_sum, np.logical_not(r_sum)))
        res.spk_time = r_sum.sum()
        res.spk_miss_time = mrh
        res.spk_fa_time = mhr
        res.spk_conf_time = np.sum(np.minimum(h_sum, r_sum) - c)

        # for i in range(c.shape[0]):
        #     ref = r_sum[i]
        #     hyp = h_sum[i]
        #     mrh = max(ref - hyp, 0)
        #     mhr = max(hyp - ref, 0)
        #     res.speech_time += bool(ref)
        #     res.sns_miss_time += bool(ref and not hyp)
        #     res.sns_fa_time += bool(hyp and not ref)
        #     if ref > 1:
        #         res.overlap_time += ref
        #         res.overlap_miss_time += mrh
        #         res.overlap_fa_time += mhr
        #     res.spk_time += ref
        #     res.spk_miss_time += mrh
        #     res.spk_fa_time += mhr
        #     res.spk_conf_time += min(hyp, ref) - c[i]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
316
317
318
319
        res.compute_rate()
        return res

class DER_coll(DER):
320
321
322
323
324
325
    def __init__(self, ref_cluster_list, hyp_cluster_list):
        self.ref_cluster_list = ref_cluster_list
        self.hyp_cluster_list = hyp_cluster_list
        hyp_nb_clusters = len(hyp_cluster_list)
        ref_nb_clusters = len(ref_cluster_list)
        self.conf = np.full((ref_nb_clusters, hyp_nb_clusters), 0.0)
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
326
327

    def merge_confusion(self, der):
328
329
        for i in range(len(der.ref_cluster_list)):
            for j in range(len(der.hyp_cluster_list)):
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
330
331
                v = der.conf[i, j]
                if v > 0:
332
333
                    ig = self.ref_cluster_list.index(der.ref_cluster_list[i])
                    jg = self.hyp_cluster_list.index(der.hyp_cluster_list[j])
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
                    self.conf[ig, jg] += v

class DER_result:
    """
    Class to store the s4d error results
    """
    def __init__(self, show):
        """

        :param show: a string
        """
        self.show = show

        self.uem_time = 0
        self.uem_with_collar_time = 0
        self.uem_collar_time = 0
        self.spk_time = 0
        self.speech_time = 0
        self.sns_time = 0
        self.overlap_time = 0
        self.overlap_miss_time = 0
        self.overlap_fa_time = 0
        self.spk_conf_time = 0
        self.sns_fa_time = 0
        self.sns_miss_time = 0
        self.overlap_miss = 0
360
        self.overlap_fa = 0
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
361
362
363
364
365
366
367
        self.spk_miss_time = 0
        self.spk_fa_time = 0
        self.spk_miss = 0
        self.spk_fa = 0
        self.spk_conf = 0
        self.speech_fa = 0
        self.speech_miss = 0
Sylvain Meignier's avatar
Sylvain Meignier committed
368
        self.len_line = 0
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

    def accumulate(self, result):
        """
        Accumulate the scores
        :param result: a DER_result object to accumulate
        :return:
        """
        self.uem_time += result.uem_time
        self.uem_with_collar_time += result.uem_with_collar_time
        self.uem_collar_time += result.uem_collar_time
        self.spk_time += result.spk_time
        self.speech_time += result.speech_time
        self.sns_time += result.sns_time
        self.overlap_time += result.overlap_time
        self.overlap_miss_time += result.overlap_miss_time
        self.overlap_fa_time += result.overlap_fa_time
        self.spk_conf_time += result.spk_conf_time
        self.sns_fa_time += result.sns_fa_time
        self.sns_miss_time += result.sns_miss_time
        self.overlap_miss += result.overlap_miss
389
        self.overlap_fa += result.overlap_fa
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
        self.spk_miss_time += result.spk_miss_time
        self.spk_fa_time += result.spk_fa_time
        self.spk_miss += result.spk_miss
        self.spk_fa += result.spk_fa
        self.spk_conf += result.spk_conf
        self.speech_fa += result.speech_fa
        self.speech_miss += result.speech_miss

    def compute_rate(self):
        """
        Compute the various rates
        """
        self.sns_fa = self.sns_fa_time / self.uem_time * 100
        self.sns_miss = self.sns_miss_time / self.uem_time * 100

        if self.overlap_time > 0:
            self.overlap_fa = self.overlap_fa_time / self.overlap_time * 100
            self.overlap_miss = self.overlap_miss_time / self.overlap_time * 100
        else:
            self.overlap_fa = 0
            self.overlap_miss = 0

        self.spk_fa = self.spk_fa_time / self.spk_time * 100
        self.spk_miss = self.spk_miss_time / self.spk_time * 100
        self.spk_conf = self.spk_conf_time / self.spk_time * 100

Sylvain Meignier's avatar
Sylvain Meignier committed
416
417
    @classmethod
    def header(cls, speech=True, overlap=False, speaker=True):
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
418
419
420
421
422
423
424
        """
        Make the header
        :param speech: add speech/non-speech results
        :param overlap: add overlap result
        :param speaker: add speaker result (DER)
        :return: a str
        """
Sylvain Meignier's avatar
Sylvain Meignier committed
425
        line = ['show', 'type']
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
426
        if speech:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
427
            line += ['fa', 'miss', 'sns']
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
428
        if overlap:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
429
            line += ['fa', 'miss', 'overlap']
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
430
        if speaker:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
431
            line += ['fa', 'miss', 'conf', 'speaker']
Sylvain Meignier's avatar
Sylvain Meignier committed
432

Sylvain Meignier's avatar
Sylvain Meignier committed
433
        return [line]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
434

Sylvain Meignier's avatar
Sylvain Meignier committed
435
    def rate(self, name=None, speech=True, overlap=False, speaker=True):
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
436
437
438
439
440
441
442
        """
        Make the rate results
        :param speech: add speech/non-speech results
        :param overlap: add overlap result
        :param speaker: add speaker result (DER)
        :return: a str
        """
Sylvain Meignier's avatar
Sylvain Meignier committed
443
444
445
        if name is None:
            name = ''
        line = [name, 'rate']
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
446
        if speech:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
447
            line += [self.sns_fa, self.sns_miss, self.sns_fa+self.sns_miss]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
448
        if overlap:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
449
            line += [self.overlap_fa, self.overlap_miss, self.overlap_fa+self.overlap_miss]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
450
        if speaker:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
451
            line += [self.spk_fa, self.spk_miss, self.spk_conf, self.get_der()]
Sylvain Meignier's avatar
Sylvain Meignier committed
452
        return [line]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
453

Sylvain Meignier's avatar
Sylvain Meignier committed
454
    def time(self, name=None, speech=True, overlap=False, speaker=True):
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
455
456
457
458
459
460
461
        """
        Make the time results
        :param speech: add speech/non-speech results
        :param overlap: add overlap result
        :param speaker: add speaker result (DER)
        :return: a str
        """
Sylvain Meignier's avatar
Sylvain Meignier committed
462
463
464
        if name is None:
            name = ''
        line = [name, 'time']
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
465
        if speech:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
466
            line += [self.sns_fa_time/100, self.sns_miss_time/100, self.uem_with_collar_time/100]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
467
        if overlap:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
468
            line += [self.overlap_fa_time/100, self.overlap_miss_time/100, self.overlap_time/100]
Sylvain Meignier's avatar
Origin  
Sylvain Meignier committed
469
        if speaker:
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
470
            line += [self.spk_fa_time/100, self.spk_miss_time/100, self.spk_conf_time/100, self.spk_time/100]
Sylvain Meignier's avatar
Sylvain Meignier committed
471
472
473
474
475
        return [line]

    def get_table(self, name=None, time=True, rate=True, overlap=False):
        lst = []
        add_name = False
Sylvain Meignier's avatar
Sylvain Meignier committed
476
477
        if rate:
            lst += self.rate(name=name, overlap=overlap)
Sylvain Meignier's avatar
Sylvain Meignier committed
478
479
            add_name = True

Sylvain Meignier's avatar
Sylvain Meignier committed
480
481
482
483
484
        if time:
            if not add_name:
                lst += self.time(name=name, overlap=overlap)
            else:
                lst += self.time(name=None)
Sylvain Meignier's avatar
Sylvain Meignier committed
485

Sylvain Meignier's avatar
Sylvain Meignier committed
486
        #if add_name == False and overlap:
Sylvain Meignier's avatar
Sylvain Meignier committed
487
        #    lst += self.overlap(speaker=speaker)
Sylvain Meignier's avatar
Sylvain Meignier committed
488
        #    add_name = True
Sylvain Meignier's avatar
??    
Sylvain Meignier committed
489
        #else:
Sylvain Meignier's avatar
Sylvain Meignier committed
490
            #lst += self.overlap(speaker=None)
Sylvain Meignier's avatar
Sylvain Meignier committed
491
        return lst
Sylvain Meignier's avatar
Sylvain Meignier committed
492

Sylvain Meignier's avatar
Sylvain Meignier committed
493
494
    def get_der(self):
        return self.spk_fa+self.spk_miss+self.spk_conf
Sylvain Meignier's avatar
Sylvain Meignier committed
495

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
496

Sylvain Meignier's avatar
Sylvain Meignier committed
497
498
def get_header():
    return DER_result.header()
Sylvain Meignier's avatar
Sylvain Meignier committed
499

Sylvain Meignier's avatar
new    
Sylvain Meignier committed
500

Sylvain Meignier's avatar
Sylvain Meignier committed
501
502
503
504
505
def compute_der(hyp, ref, uem=None, collar=0, no_overlap=True):
    cder = DER(hyp, ref, uem, collar=collar, no_overlap=no_overlap)
    cder.confusion()
    cder.assignment()
    return cder.error()