Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
bob.fusion.base
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
bob
bob.fusion.base
Commits
6c7e025b
Commit
6c7e025b
authored
4 years ago
by
Amir MOHAMMADI
Browse files
Options
Downloads
Patches
Plain Diff
report the matplotlib backend if scatter fails
parent
de9a8fec
No related branches found
No related tags found
No related merge requests found
Pipeline
#49354
passed
4 years ago
Stage: build
Stage: deploy
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
bob/fusion/base/algorithm/GMM.py
+1
-1
1 addition, 1 deletion
bob/fusion/base/algorithm/GMM.py
bob/fusion/base/script/boundary.py
+232
-164
232 additions, 164 deletions
bob/fusion/base/script/boundary.py
bob/fusion/base/script/fuse.py
+3
-4
3 additions, 4 deletions
bob/fusion/base/script/fuse.py
with
236 additions
and
169 deletions
bob/fusion/base/algorithm/GMM.py
+
1
−
1
View file @
6c7e025b
...
@@ -76,7 +76,7 @@ class GMM(AlgorithmBob):
...
@@ -76,7 +76,7 @@ class GMM(AlgorithmBob):
if
self
.
gaussians
is
None
:
if
self
.
gaussians
is
None
:
self
.
gaussians
=
array
.
shape
[
1
]
+
1
self
.
gaussians
=
array
.
shape
[
1
]
+
1
logger
.
warn
(
"
Number of Gaussians was None.
"
logger
.
warn
ing
(
"
Number of Gaussians was None.
"
"
Using {}.
"
.
format
(
self
.
gaussians
))
"
Using {}.
"
.
format
(
self
.
gaussians
))
# Computes input size
# Computes input size
...
...
This diff is collapsed.
Click to expand it.
bob/fusion/base/script/boundary.py
+
232
−
164
View file @
6c7e025b
"""
Plots the decision boundaries of fusion algorithms.
"""
Plots the decision boundaries of fusion algorithms.
"""
"""
from
__future__
import
print_function
,
absolute_import
,
division
import
os
import
logging
import
logging
import
click
import
click
from
bob.extension.scripts.click_helper
import
verbosity_option
from
bob.extension.scripts.click_helper
import
verbosity_option
...
@@ -10,173 +8,243 @@ import numpy as np
...
@@ -10,173 +8,243 @@ import numpy as np
from
bob.bio.base.score
import
load_score
from
bob.bio.base.score
import
load_score
from
..tools
import
(
from
..tools
import
(
get_gza_from_lines_list
,
check_consistency
,
get_scores
,
remove_nan
,
get_gza_from_lines_list
,
grouping
)
check_consistency
,
get_scores
,
remove_nan
,
grouping
,
)
from
..algorithm
import
Algorithm
from
..algorithm
import
Algorithm
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
def
plot_boundary_decision
(
algorithm
,
scores
,
score_labels
,
threshold
,
def
plot_boundary_decision
(
thres_system1
=
None
,
algorithm
,
thres_system2
=
None
,
scores
,
do_grouping
=
False
,
score_labels
,
resolution
=
2000
,
threshold
,
alpha
=
0.75
,
thres_system1
=
None
,
legends
=
None
,
thres_system2
=
None
,
i1
=
0
,
do_grouping
=
False
,
i2
=
1
,
resolution
=
2000
,
x_label
=
None
,
alpha
=
0.75
,
y_label
=
None
,
legends
=
None
,
**
kwargs
i1
=
0
,
):
i2
=
1
,
if
legends
is
None
:
x_label
=
None
,
legends
=
[
'
Zero Effort Impostor
'
,
'
Presentation Attack
'
,
'
Genuine
'
]
y_label
=
None
,
markers
=
[
'
x
'
,
'
o
'
,
'
s
'
]
**
kwargs
,
):
if
scores
.
shape
[
1
]
>
2
:
if
legends
is
None
:
raise
NotImplementedError
(
legends
=
[
"
Zero Effort Impostor
"
,
"
Presentation Attack
"
,
"
Genuine
"
]
"
Currently plotting the decision boundary for more than two
"
markers
=
[
"
x
"
,
"
o
"
,
"
s
"
]
"
systems is not supported.
"
)
if
scores
.
shape
[
1
]
>
2
:
import
matplotlib.pyplot
as
plt
raise
NotImplementedError
(
plt
.
gca
()
# this is necessary for subplots to work.
"
Currently plotting the decision boundary for more than two
"
"
systems is not supported.
"
X
=
scores
[:,
[
i1
,
i2
]]
)
Y
=
score_labels
x_pad
=
(
X
[:,
i1
].
max
()
-
X
[:,
i1
].
min
())
*
0.1
import
matplotlib
y_pad
=
(
X
[:,
i2
].
max
()
-
X
[:,
i2
].
min
())
*
0.1
import
matplotlib.pyplot
as
plt
x_min
,
x_max
=
X
[:,
i1
].
min
()
-
x_pad
,
X
[:,
i1
].
max
()
+
x_pad
y_min
,
y_max
=
X
[:,
i2
].
min
()
-
y_pad
,
X
[:,
i2
].
max
()
+
y_pad
plt
.
gca
()
# this is necessary for subplots to work.
xx
,
yy
=
np
.
meshgrid
(
np
.
linspace
(
x_min
,
x_max
,
resolution
),
X
=
scores
[:,
[
i1
,
i2
]]
np
.
linspace
(
y_min
,
y_max
,
resolution
))
Y
=
score_labels
x_pad
=
(
X
[:,
i1
].
max
()
-
X
[:,
i1
].
min
())
*
0.1
contourf
=
None
y_pad
=
(
X
[:,
i2
].
max
()
-
X
[:,
i2
].
min
())
*
0.1
if
algorithm
is
not
None
:
x_min
,
x_max
=
X
[:,
i1
].
min
()
-
x_pad
,
X
[:,
i1
].
max
()
+
x_pad
temp
=
np
.
c_
[
xx
.
ravel
(),
yy
.
ravel
()]
y_min
,
y_max
=
X
[:,
i2
].
min
()
-
y_pad
,
X
[:,
i2
].
max
()
+
y_pad
temp
=
algorithm
.
preprocess
(
temp
)
xx
,
yy
=
np
.
meshgrid
(
Z
=
(
algorithm
.
fuse
(
temp
)
>
threshold
).
reshape
(
xx
.
shape
)
np
.
linspace
(
x_min
,
x_max
,
resolution
),
np
.
linspace
(
y_min
,
y_max
,
resolution
)
)
contourf
=
plt
.
contour
(
xx
,
yy
,
Z
,
1
,
alpha
=
1
,
cmap
=
plt
.
cm
.
gray
)
contourf
=
None
if
do_grouping
:
if
algorithm
is
not
None
:
gen
=
grouping
(
X
[
Y
==
0
,
:],
**
kwargs
)
temp
=
np
.
c_
[
xx
.
ravel
(),
yy
.
ravel
()]
zei
=
grouping
(
X
[
Y
==
1
,
:],
**
kwargs
)
temp
=
algorithm
.
preprocess
(
temp
)
atk
=
grouping
(
X
[
Y
==
2
,
:],
**
kwargs
)
Z
=
(
algorithm
.
fuse
(
temp
)
>
threshold
).
reshape
(
xx
.
shape
)
else
:
gen
=
X
[
Y
==
0
,
:]
contourf
=
plt
.
contour
(
xx
,
yy
,
Z
,
1
,
alpha
=
1
,
cmap
=
plt
.
cm
.
gray
)
zei
=
X
[
Y
==
1
,
:]
atk
=
X
[
Y
==
2
,
:]
if
do_grouping
:
for
i
,
(
X
,
color
)
in
enumerate
(((
zei
,
'
C0
'
),
(
atk
,
'
C1
'
),
(
gen
,
'
C2
'
))):
gen
=
grouping
(
X
[
Y
==
0
,
:],
**
kwargs
)
if
X
.
size
==
0
:
zei
=
grouping
(
X
[
Y
==
1
,
:],
**
kwargs
)
continue
atk
=
grouping
(
X
[
Y
==
2
,
:],
**
kwargs
)
plt
.
scatter
(
else
:
X
[:,
0
],
X
[:,
1
],
marker
=
markers
[
i
],
alpha
=
alpha
,
gen
=
X
[
Y
==
0
,
:]
c
=
color
,
label
=
legends
[
i
])
zei
=
X
[
Y
==
1
,
:]
plt
.
legend
(
bbox_to_anchor
=
(
-
0.05
,
1.02
,
1.05
,
.
102
),
loc
=
3
,
atk
=
X
[
Y
==
2
,
:]
ncol
=
3
,
mode
=
"
expand
"
,
borderaxespad
=
0.
,
fontsize
=
14
)
for
i
,
(
X
,
color
)
in
enumerate
(((
zei
,
"
C0
"
),
(
atk
,
"
C1
"
),
(
gen
,
"
C2
"
))):
if
X
.
size
==
0
:
if
thres_system1
is
not
None
:
continue
plt
.
axvline
(
thres_system1
,
color
=
'
red
'
)
try
:
plt
.
axhline
(
thres_system2
,
color
=
'
red
'
)
plt
.
scatter
(
X
[:,
0
],
X
[:,
1
],
marker
=
markers
[
i
],
alpha
=
alpha
,
c
=
color
,
label
=
legends
[
i
]
plt
.
xlim
([
x_min
,
x_max
])
)
plt
.
ylim
([
y_min
,
y_max
])
except
Exception
as
e
:
plt
.
grid
(
True
)
raise
RuntimeError
(
f
"
matplotlib backend:
{
matplotlib
.
get_backend
()
}
"
)
from
e
plt
.
xlabel
(
x_label
)
plt
.
legend
(
plt
.
ylabel
(
y_label
)
bbox_to_anchor
=
(
-
0.05
,
1.02
,
1.05
,
0.102
),
loc
=
3
,
return
contourf
ncol
=
3
,
mode
=
"
expand
"
,
borderaxespad
=
0.0
,
@click.command
(
epilog
=
"""
\b
fontsize
=
14
,
)
if
thres_system1
is
not
None
:
plt
.
axvline
(
thres_system1
,
color
=
"
red
"
)
plt
.
axhline
(
thres_system2
,
color
=
"
red
"
)
plt
.
xlim
([
x_min
,
x_max
])
plt
.
ylim
([
y_min
,
y_max
])
plt
.
grid
(
True
)
plt
.
xlabel
(
x_label
)
plt
.
ylabel
(
y_label
)
return
contourf
@click.command
(
epilog
=
"""
\b
Examples:
Examples:
$ bob fusion boundary -vvv {sys1,sys2}/scores-eval -m /path/to/Model.pkl
$ bob fusion boundary -vvv {sys1,sys2}/scores-eval -m /path/to/Model.pkl
"""
)
"""
@click.argument
(
'
scores
'
,
nargs
=-
1
,
required
=
True
,
)
type
=
click
.
Path
(
exists
=
True
))
@click.argument
(
"
scores
"
,
nargs
=-
1
,
required
=
True
,
type
=
click
.
Path
(
exists
=
True
))
@click.option
(
'
-m
'
,
'
--model-file
'
,
required
=
False
,
@click.option
(
help
=
'
The path to where the algorithm will be loaded from.
'
)
"
-m
"
,
@click.option
(
'
-t
'
,
'
--threshold
'
,
type
=
click
.
FLOAT
,
required
=
False
,
"
--model-file
"
,
help
=
'
The threshold to classify scores after fusion. Usually
'
required
=
False
,
'
calculated from fused development set.
'
)
help
=
"
The path to where the algorithm will be loaded from.
"
,
@click.option
(
'
-g
'
,
'
--group
'
,
type
=
click
.
INT
,
default
=
0
,
show_default
=
True
,
)
help
=
'
If given scores will be grouped into N samples.
'
)
@click.option
(
@click.option
(
'
-G
'
,
'
--grouping
'
,
type
=
click
.
Choice
((
'
random
'
,
'
kmeans
'
)),
"
-t
"
,
default
=
'
kmeans
'
,
show_default
=
True
,
"
--threshold
"
,
help
=
'
The gouping algorithm to be used.
'
)
type
=
click
.
FLOAT
,
@click.option
(
'
-o
'
,
'
--output
'
,
default
=
'
scatter.pdf
'
,
required
=
False
,
show_default
=
True
,
type
=
click
.
Path
(
writable
=
True
),
help
=
"
The threshold to classify scores after fusion. Usually
"
help
=
'
The path to the saved plot.
'
)
"
calculated from fused development set.
"
,
@click.option
(
'
-X
'
,
'
--x-label
'
,
default
=
'
Recognition scores
'
,
)
show_default
=
True
,
help
=
'
The label for the first system.
'
)
@click.option
(
@click.option
(
'
-Y
'
,
'
--y-label
'
,
default
=
'
PAD scores
'
,
show_default
=
True
,
"
-g
"
,
help
=
'
The label for the second system.
'
)
"
--group
"
,
@click.option
(
'
--skip-check
'
,
is_flag
=
True
,
show_default
=
True
,
type
=
click
.
INT
,
help
=
'
If True, it will skip checking for the consistency
'
default
=
0
,
'
between scores.
'
)
show_default
=
True
,
help
=
"
If given scores will be grouped into N samples.
"
,
)
@click.option
(
"
-G
"
,
"
--grouping
"
,
type
=
click
.
Choice
((
"
random
"
,
"
kmeans
"
)),
default
=
"
kmeans
"
,
show_default
=
True
,
help
=
"
The gouping algorithm to be used.
"
,
)
@click.option
(
"
-o
"
,
"
--output
"
,
default
=
"
scatter.pdf
"
,
show_default
=
True
,
type
=
click
.
Path
(
writable
=
True
),
help
=
"
The path to the saved plot.
"
,
)
@click.option
(
"
-X
"
,
"
--x-label
"
,
default
=
"
Recognition scores
"
,
show_default
=
True
,
help
=
"
The label for the first system.
"
,
)
@click.option
(
"
-Y
"
,
"
--y-label
"
,
default
=
"
PAD scores
"
,
show_default
=
True
,
help
=
"
The label for the second system.
"
,
)
@click.option
(
"
--skip-check
"
,
is_flag
=
True
,
show_default
=
True
,
help
=
"
If True, it will skip checking for the consistency
"
"
between scores.
"
,
)
@verbosity_option
()
@verbosity_option
()
def
boundary
(
scores
,
model_file
,
threshold
,
group
,
grouping
,
output
,
x_label
,
def
boundary
(
y_label
,
skip_check
,
**
kwargs
):
scores
,
"""
Plots the decision boundaries of fusion algorithms.
model_file
,
threshold
,
The script takes several scores (usually eval scores) from different
group
,
biometric and pad systems and a trained algorithm and plots the decision
grouping
,
boundary.
output
,
x_label
,
You need to provide two score files from two systems. System 1 will be
y_label
,
plotted on the x-axis.
skip_check
,
"""
**
kwargs
,
# load the algorithm
):
algorithm
=
None
"""
Plots the decision boundaries of fusion algorithms.
if
model_file
:
algorithm
=
Algorithm
().
load
(
model_file
)
The script takes several scores (usually eval scores) from different
assert
threshold
is
not
None
,
"
threshold must be provided with the model
"
biometric and pad systems and a trained algorithm and plots the decision
boundary.
# load the scores
score_lines_list_eval
=
[
load_score
(
path
)
for
path
in
scores
]
You need to provide two score files from two systems. System 1 will be
plotted on the x-axis.
# genuine, zero effort impostor, and attack list
"""
idx1
,
gen_le
,
zei_le
,
atk_le
=
get_gza_from_lines_list
(
# load the algorithm
score_lines_list_eval
)
algorithm
=
None
if
model_file
:
# check if score lines are consistent
algorithm
=
Algorithm
().
load
(
model_file
)
if
not
skip_check
:
assert
threshold
is
not
None
,
"
threshold must be provided with the model
"
check_consistency
(
gen_le
,
zei_le
,
atk_le
)
# load the scores
# concatenate the scores and create the labels
score_lines_list_eval
=
[
load_score
(
path
)
for
path
in
scores
]
scores
=
get_scores
(
gen_le
,
zei_le
,
atk_le
)
score_labels
=
np
.
zeros
((
scores
.
shape
[
0
],))
# genuine, zero effort impostor, and attack list
gensize
=
gen_le
[
0
].
shape
[
0
]
idx1
,
gen_le
,
zei_le
,
atk_le
=
get_gza_from_lines_list
(
score_lines_list_eval
)
zeisize
=
zei_le
[
0
].
shape
[
0
]
score_labels
[:
gensize
]
=
0
# check if score lines are consistent
score_labels
[
gensize
:
gensize
+
zeisize
]
=
1
if
not
skip_check
:
score_labels
[
gensize
+
zeisize
:]
=
2
check_consistency
(
gen_le
,
zei_le
,
atk_le
)
found_nan
,
nan_idx
,
scores
=
remove_nan
(
scores
,
False
)
score_labels
=
score_labels
[
~
nan_idx
]
# concatenate the scores and create the labels
scores
=
get_scores
(
gen_le
,
zei_le
,
atk_le
)
if
found_nan
:
score_labels
=
np
.
zeros
((
scores
.
shape
[
0
],))
logger
.
warn
(
'
{} nan values were removed.
'
.
format
(
np
.
sum
(
nan_idx
)))
gensize
=
gen_le
[
0
].
shape
[
0
]
zeisize
=
zei_le
[
0
].
shape
[
0
]
# plot the decision boundary
score_labels
[:
gensize
]
=
0
do_grouping
=
True
score_labels
[
gensize
:
gensize
+
zeisize
]
=
1
if
group
<
1
:
score_labels
[
gensize
+
zeisize
:]
=
2
do_grouping
=
False
found_nan
,
nan_idx
,
scores
=
remove_nan
(
scores
,
False
)
score_labels
=
score_labels
[
~
nan_idx
]
import
matplotlib
if
not
hasattr
(
matplotlib
,
'
backends
'
):
if
found_nan
:
matplotlib
.
use
(
'
pdf
'
)
logger
.
warn
(
"
{} nan values were removed.
"
.
format
(
np
.
sum
(
nan_idx
)))
import
matplotlib.pyplot
as
plt
plot_boundary_decision
(
# plot the decision boundary
algorithm
,
scores
,
score_labels
,
threshold
,
do_grouping
=
True
do_grouping
=
do_grouping
,
if
group
<
1
:
npoints
=
group
,
do_grouping
=
False
seed
=
0
,
gformat
=
grouping
,
import
matplotlib.pyplot
as
plt
x_label
=
x_label
,
y_label
=
y_label
,
plot_boundary_decision
(
)
algorithm
,
plt
.
savefig
(
output
,
transparent
=
True
)
scores
,
plt
.
close
()
score_labels
,
threshold
,
do_grouping
=
do_grouping
,
npoints
=
group
,
seed
=
0
,
gformat
=
grouping
,
x_label
=
x_label
,
y_label
=
y_label
,
)
plt
.
savefig
(
output
,
transparent
=
True
)
plt
.
close
()
This diff is collapsed.
Click to expand it.
bob/fusion/base/script/fuse.py
+
3
−
4
View file @
6c7e025b
...
@@ -8,7 +8,6 @@ from bob.extension.scripts.click_helper import (
...
@@ -8,7 +8,6 @@ from bob.extension.scripts.click_helper import (
import
os
import
os
import
numpy
as
np
import
numpy
as
np
from
bob.io.base
import
create_directories_safe
from
bob.bio.base.score
import
load_score
,
dump_score
from
bob.bio.base.score
import
load_score
,
dump_score
from
bob.bio.base
import
utils
from
bob.bio.base
import
utils
...
@@ -45,7 +44,7 @@ kwargs: %s
...
@@ -45,7 +44,7 @@ kwargs: %s
def
save_fused_scores
(
save_path
,
fused_scores
,
score_lines
):
def
save_fused_scores
(
save_path
,
fused_scores
,
score_lines
):
score_lines
[
'
score
'
]
=
fused_scores
score_lines
[
'
score
'
]
=
fused_scores
gen
,
zei
,
atk
,
_
,
_
,
_
=
get_2negatives_1positive
(
score_lines
)
gen
,
zei
,
atk
,
_
,
_
,
_
=
get_2negatives_1positive
(
score_lines
)
create_directories_safe
(
os
.
path
.
dirname
(
save_path
))
os
.
makedirs
(
os
.
path
.
dirname
(
save_path
)
,
exist_ok
=
True
)
dump_score
(
save_path
,
score_lines
)
dump_score
(
save_path
,
score_lines
)
dump_score
(
save_path
+
'
-licit
'
,
np
.
append
(
gen
,
zei
))
dump_score
(
save_path
+
'
-licit
'
,
np
.
append
(
gen
,
zei
))
dump_score
(
save_path
+
'
-spoof
'
,
np
.
append
(
gen
,
atk
))
dump_score
(
save_path
+
'
-spoof
'
,
np
.
append
(
gen
,
atk
))
...
@@ -190,7 +189,7 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force,
...
@@ -190,7 +189,7 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force,
click.MissingParameter
click.MissingParameter
If the algorithm is not provided.
If the algorithm is not provided.
"""
"""
create_directories_safe
(
output_dir
)
os
.
makedirs
(
output_dir
,
exist_ok
=
True
)
if
not
model_file
:
if
not
model_file
:
do_training
=
True
do_training
=
True
model_file
=
os
.
path
.
join
(
output_dir
,
'
Model.pkl
'
)
model_file
=
os
.
path
.
join
(
output_dir
,
'
Model.pkl
'
)
...
@@ -303,7 +302,7 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force,
...
@@ -303,7 +302,7 @@ def fuse(scores, algorithm, groups, output_dir, model_file, skip_check, force,
scores_eval_lines
=
scores_eval_lines
[
~
nan_eval
]
scores_eval_lines
=
scores_eval_lines
[
~
nan_eval
]
if
found_nan
:
if
found_nan
:
logger
.
warn
(
'
Some nan values were removed.
'
)
logger
.
warn
ing
(
'
Some nan values were removed.
'
)
routine_fusion
(
routine_fusion
(
algorithm
,
model_file
,
scores_train_lines
,
scores_train
,
algorithm
,
model_file
,
scores_train_lines
,
scores_train
,
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment