Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
bob.bio.face
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.bio.face
Merge requests
!185
Add pre-commit
Code
Review changes
Check out branch
Download
Patches
Plain diff
Merged
Add pre-commit
deprecation
into
master
Overview
0
Commits
5
Pipelines
2
Changes
161
Merged
Amir MOHAMMADI
requested to merge
deprecation
into
master
3 years ago
Overview
0
Commits
5
Pipelines
2
Changes
161
Expand
0
0
Merge request reports
Compare
master
version 1
0039aded
3 years ago
master (base)
and
latest version
latest version
44c7862e
5 commits,
3 years ago
version 1
0039aded
2 commits,
3 years ago
161 files
+
4558
−
2483
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
Files
161
Search (e.g. *.vue) (Ctrl+P)
bob/bio/face/algorithm/Histogram.py
+
211
−
190
Options
@@ -6,210 +6,231 @@ import numpy
from
bob.bio.base.algorithm
import
Algorithm
def
chi_square
(
*
args
):
"""
Calculates the chi-square distance between two histograms.
@param hist1 The first histogram
@param hist2 The second histogram
@returns The chi-square distance between the two histograms
"""
if
len
(
args
)
==
2
:
h1
,
h2
=
args
d
=
0
for
i
in
range
(
h1
.
shape
[
0
]):
if
h1
[
i
]
!=
h2
[
i
]:
d
+=
int
(((
h1
[
i
]
-
h2
[
i
])
**
2
)
/
(
h1
[
i
]
+
h2
[
i
]))
return
d
else
:
# histogram intersection with sparse histograms
index_1
,
value_1
,
index_2
,
value_2
=
args
raise
NotImplementedError
(
"
chi_square distance with sparse histograms is not implemented yet
"
)
def
histogram_intersection
(
*
args
):
"""
Calculates the histogram intersection between two histograms.
@param hist1 The first histogram
@param hist2 The second histogram
@returns The histogram intersection between the two histograms
"""
if
len
(
args
)
==
2
:
hist1
,
hist2
=
args
return
numpy
.
sum
(
numpy
.
minimum
(
hist1
,
hist2
))
else
:
# histogram intersection with sparse histograms
index_1
,
value_1
,
index_2
,
value_2
=
args
i1
,
i2
,
i1_end
,
i2_end
=
0
,
0
,
index_1
.
shape
[
0
],
index_2
.
shape
[
0
]
p1
,
p2
=
index_1
[
i1
],
index_2
[
i2
]
sum
=
0
while
i1
<
i1_end
and
i2
<
i2_end
:
p1
=
index_1
[
i1
]
p2
=
index_2
[
i2
]
if
p1
==
p2
:
sum
+=
numpy
.
minimum
(
value_1
[
i1
],
value_2
[
i2
])
i1
+=
1
i2
+=
1
elif
p1
<
p2
:
i1
+=
1
else
:
i2
+=
1
return
sum
class
Histogram
(
Algorithm
):
"""
Computes the distance between histogram sequences.
Both sparse and non-sparse representations of histograms are supported.
For enrollment, to date only the averaging of histograms is implemented.
**Parameters:**
distance_function : function
The function to be used to compare two histograms.
This function should accept sparse histograms.
is_distance_function : bool
Is the given ``distance_function`` distance function (lower values are better) or a similarity function (higher values are better)?
multiple_probe_scoring : str or ``None``
The way, scores are fused when multiple probes are available.
See :py:func:`bob.bio.base.score_fusion_strategy` for possible values.
"""
def
__init__
(
self
,
distance_function
=
chi_square
,
is_distance_function
=
True
,
multiple_probe_scoring
=
'
average
'
):
# call base class constructor
Algorithm
.
__init__
(
self
,
distance_function
=
str
(
distance_function
),
is_distance_function
=
is_distance_function
,
multiple_model_scoring
=
None
,
multiple_probe_scoring
=
multiple_probe_scoring
)
# remember distance function
self
.
distance_function
=
distance_function
self
.
factor
=
-
1.
if
is_distance_function
else
1
def
chi_square
(
*
args
):
"""
Calculates the chi-square distance between two histograms.
def
_is_sparse
(
self
,
feature
):
assert
isinstance
(
feature
,
numpy
.
ndarray
)
return
feature
.
ndim
==
2
@param hist1 The first histogram
@param hist2 The second histogram
def
_check_feature
(
self
,
feature
,
sparse
):
assert
isinstance
(
feature
,
numpy
.
ndarray
)
if
sparse
:
# check that we have a 2D array
assert
feature
.
ndim
==
2
assert
feature
.
shape
[
0
]
==
2
@returns The chi-square distance between the two histograms
"""
if
len
(
args
)
==
2
:
h1
,
h2
=
args
d
=
0
for
i
in
range
(
h1
.
shape
[
0
]):
if
h1
[
i
]
!=
h2
[
i
]:
d
+=
int
(((
h1
[
i
]
-
h2
[
i
])
**
2
)
/
(
h1
[
i
]
+
h2
[
i
]))
return
d
else
:
assert
feature
.
ndim
==
1
# histogram intersection with sparse histograms
index_1
,
value_1
,
index_2
,
value_2
=
args
raise
NotImplementedError
(
"
chi_square distance with sparse histograms is not implemented yet
"
)
def
enroll
(
self
,
enroll_features
):
"""
enroll(enroll_features) -> model
Enrolls a model by taking the average of all histograms.
enroll_features : [1D or 2D :py:class:`numpy.ndarray`]
The histograms that should be averaged.
Histograms can be specified sparse (2D) or non-sparse (1D)
def
histogram_intersection
(
*
args
):
"""
Calculates the histogram intersection between two histograms.
**Returns:**
@param hist1 The first histogram
@param hist2 The second histogram
model : 1D or 2D :py:class:`numpy.ndarray`
The averaged histogram, sparse (2D) or non-sparse (1D).
@returns The histogram intersection between the two histograms
"""
assert
len
(
enroll_features
)
sparse
=
self
.
_is_sparse
(
enroll_features
[
0
])
[
self
.
_check_feature
(
feature
,
sparse
)
for
feature
in
enroll_features
]
if
sparse
:
# get all indices for the sparse model
values
=
{}
# iterate through all sparse features
for
feature
in
enroll_features
:
# collect the values by index
for
j
in
range
(
feature
.
shape
[
1
]):
index
=
int
(
feature
[
0
,
j
])
value
=
feature
[
1
,
j
]
/
float
(
len
(
enroll_features
))
# add up values
if
index
in
values
:
values
[
index
]
+=
value
else
:
values
[
index
]
=
value
# create model containing all the used indices
model
=
numpy
.
ndarray
((
2
,
len
(
values
)),
dtype
=
numpy
.
float64
)
for
i
,
index
in
enumerate
(
sorted
(
values
.
keys
())):
model
[
0
,
i
]
=
index
model
[
1
,
i
]
=
values
[
index
]
if
len
(
args
)
==
2
:
hist1
,
hist2
=
args
return
numpy
.
sum
(
numpy
.
minimum
(
hist1
,
hist2
))
else
:
model
=
numpy
.
zeros
(
enroll_features
[
0
].
shape
,
dtype
=
numpy
.
float64
)
# add up models
for
feature
in
enroll_features
:
model
+=
feature
# normalize by number of models
model
/=
float
(
len
(
enroll_features
))
# return averaged model
return
model
def
score
(
self
,
model
,
probe
):
"""
score(model, probe) -> score
Computes the score of the probe and the model using the desired histogram distance function.
The resulting score is the negative distance, if ``is_distance_function = True``.
Both sparse and non-sparse models and probes are accepted, but their sparseness must agree.
# histogram intersection with sparse histograms
index_1
,
value_1
,
index_2
,
value_2
=
args
i1
,
i2
,
i1_end
,
i2_end
=
0
,
0
,
index_1
.
shape
[
0
],
index_2
.
shape
[
0
]
p1
,
p2
=
index_1
[
i1
],
index_2
[
i2
]
sum
=
0
while
i1
<
i1_end
and
i2
<
i2_end
:
p1
=
index_1
[
i1
]
p2
=
index_2
[
i2
]
if
p1
==
p2
:
sum
+=
numpy
.
minimum
(
value_1
[
i1
],
value_2
[
i2
])
i1
+=
1
i2
+=
1
elif
p1
<
p2
:
i1
+=
1
else
:
i2
+=
1
return
sum
class
Histogram
(
Algorithm
):
"""
Computes the distance between histogram sequences.
Both sparse and non-sparse representations of histograms are supported.
For enrollment, to date only the averaging of histograms is implemented.
**Parameters:**
model : 1D or 2D :py:class:`numpy.ndarray`
The model enrolled by the :py:meth:`enroll` function.
probe : 1D or 2D :py:class:`numpy.ndarray`
The probe histograms, which can be specified sparse (2D) or non-sparse (1D)
distance_function : function
The function to be used to compare two histograms.
This function should accept sparse histograms.
**Returns:**
is_distance_function : bool
Is the given ``distance_function`` distance function (lower values are better) or a similarity function (higher values are better)?
score : float
The resulting similarity score.
multiple_probe_scoring : str or ``None``
The way, scores are fused when multiple probes are available.
See :py:func:`bob.bio.base.score_fusion_strategy` for possible values.
"""
sparse
=
self
.
_is_sparse
(
probe
)
self
.
_check_feature
(
model
,
sparse
)
self
.
_check_feature
(
probe
,
sparse
)
if
sparse
:
# assure that the probe is sparse as well
return
self
.
factor
*
self
.
distance_function
(
model
[
0
,:],
model
[
1
,:],
probe
[
0
,:],
probe
[
1
,:])
else
:
return
self
.
factor
*
self
.
distance_function
(
model
,
probe
)
# overwrite functions to avoid them being documented.
def
train_projector
(
*
args
,
**
kwargs
)
:
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
load_projector
(
*
args
,
**
kwargs
)
:
pass
def
project
(
*
args
,
**
kwargs
)
:
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
write_feature
(
*
args
,
**
kwargs
)
:
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
read_feature
(
*
args
,
**
kwargs
)
:
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
train_enroller
(
*
args
,
**
kwargs
)
:
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
load_enroller
(
*
args
,
**
kwargs
)
:
pass
def
score_for_multiple_models
(
self
,
models
,
probe
):
self
.
_check_feature
(
probe
,
self
.
_is_sparse
(
probe
))
scores
=
[
self
.
score
(
m
,
probe
)
for
m
in
models
]
return
scores
def
__init__
(
self
,
distance_function
=
chi_square
,
is_distance_function
=
True
,
multiple_probe_scoring
=
"
average
"
,
):
# call base class constructor
Algorithm
.
__init__
(
self
,
distance_function
=
str
(
distance_function
),
is_distance_function
=
is_distance_function
,
multiple_model_scoring
=
None
,
multiple_probe_scoring
=
multiple_probe_scoring
,
)
# remember distance function
self
.
distance_function
=
distance_function
self
.
factor
=
-
1.0
if
is_distance_function
else
1
def
_is_sparse
(
self
,
feature
):
assert
isinstance
(
feature
,
numpy
.
ndarray
)
return
feature
.
ndim
==
2
def
_check_feature
(
self
,
feature
,
sparse
):
assert
isinstance
(
feature
,
numpy
.
ndarray
)
if
sparse
:
# check that we have a 2D array
assert
feature
.
ndim
==
2
assert
feature
.
shape
[
0
]
==
2
else
:
assert
feature
.
ndim
==
1
def
enroll
(
self
,
enroll_features
):
"""
enroll(enroll_features) -> model
Enrolls a model by taking the average of all histograms.
enroll_features : [1D or 2D :py:class:`numpy.ndarray`]
The histograms that should be averaged.
Histograms can be specified sparse (2D) or non-sparse (1D)
**Returns:**
model : 1D or 2D :py:class:`numpy.ndarray`
The averaged histogram, sparse (2D) or non-sparse (1D).
"""
assert
len
(
enroll_features
)
sparse
=
self
.
_is_sparse
(
enroll_features
[
0
])
[
self
.
_check_feature
(
feature
,
sparse
)
for
feature
in
enroll_features
]
if
sparse
:
# get all indices for the sparse model
values
=
{}
# iterate through all sparse features
for
feature
in
enroll_features
:
# collect the values by index
for
j
in
range
(
feature
.
shape
[
1
]):
index
=
int
(
feature
[
0
,
j
])
value
=
feature
[
1
,
j
]
/
float
(
len
(
enroll_features
))
# add up values
if
index
in
values
:
values
[
index
]
+=
value
else
:
values
[
index
]
=
value
# create model containing all the used indices
model
=
numpy
.
ndarray
((
2
,
len
(
values
)),
dtype
=
numpy
.
float64
)
for
i
,
index
in
enumerate
(
sorted
(
values
.
keys
())):
model
[
0
,
i
]
=
index
model
[
1
,
i
]
=
values
[
index
]
else
:
model
=
numpy
.
zeros
(
enroll_features
[
0
].
shape
,
dtype
=
numpy
.
float64
)
# add up models
for
feature
in
enroll_features
:
model
+=
feature
# normalize by number of models
model
/=
float
(
len
(
enroll_features
))
# return averaged model
return
model
def
score
(
self
,
model
,
probe
):
"""
score(model, probe) -> score
Computes the score of the probe and the model using the desired histogram distance function.
The resulting score is the negative distance, if ``is_distance_function = True``.
Both sparse and non-sparse models and probes are accepted, but their sparseness must agree.
**Parameters:**
model : 1D or 2D :py:class:`numpy.ndarray`
The model enrolled by the :py:meth:`enroll` function.
probe : 1D or 2D :py:class:`numpy.ndarray`
The probe histograms, which can be specified sparse (2D) or non-sparse (1D)
**Returns:**
score : float
The resulting similarity score.
"""
sparse
=
self
.
_is_sparse
(
probe
)
self
.
_check_feature
(
model
,
sparse
)
self
.
_check_feature
(
probe
,
sparse
)
if
sparse
:
# assure that the probe is sparse as well
return
self
.
factor
*
self
.
distance_function
(
model
[
0
,
:],
model
[
1
,
:],
probe
[
0
,
:],
probe
[
1
,
:]
)
else
:
return
self
.
factor
*
self
.
distance_function
(
model
,
probe
)
# overwrite functions to avoid them being documented.
def
train_projector
(
*
args
,
**
kwargs
):
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
load_projector
(
*
args
,
**
kwargs
):
pass
def
project
(
*
args
,
**
kwargs
):
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
write_feature
(
*
args
,
**
kwargs
):
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
read_feature
(
*
args
,
**
kwargs
):
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
train_enroller
(
*
args
,
**
kwargs
):
raise
NotImplementedError
(
"
This function is not implemented and should not be called.
"
)
def
load_enroller
(
*
args
,
**
kwargs
):
pass
def
score_for_multiple_models
(
self
,
models
,
probe
):
self
.
_check_feature
(
probe
,
self
.
_is_sparse
(
probe
))
scores
=
[
self
.
score
(
m
,
probe
)
for
m
in
models
]
return
scores
Loading