FisherVectorEmbedder¶
The Fisher Vector solves both problems of the BoW model. First, it encodes higher-order statistics, such as the first and optionally second-order differences, instead of just counting the occurrences of visual words like BoW. This method is derived from the Fisher kernel framework, which describes a sample set’s deviation from an average distribution. Secondly, the distribution of the local descriptors, unlike BoW and VLAD, is modeled by a Gaussian Mixture Model. This mitigates the hard assignment problem introduced by the K-Means algorithm, since each descriptor is assigned to multiple Gaussian components with different probabilities.
Computation¶
Fisher kernel framework¶
Given a set of \(T\) local descriptors \(X = \{x_t; t = 1, \ldots, T\}\) extracted from an image, it is assumed that the generation process of \(X\) can be modeled by an image-independent probability density function \(u_{\lambda}\) with parameters \(\lambda\) [Jégou et al., 2012]. The gradient vector \(G^{X}_{\lambda}\) is obtained by computing the gradient of the log-likelihood of the sample set \(X\) with respect to the parameters \(\lambda\):
where \(G^{X}_{\lambda}\) describes the contribution of the parameters to the generation process [Perronnin & Dance, 2010].
The Fisher kernel is then defined as:
where \(F_{\lambda}\) is the Fisher information matrix, defined by:
\(\mathcal{G}^{X}_{\lambda}\) is the Fisher Vector after applying the Cholesky decomposition on \(F_{\lambda}^{-1} = L_{\lambda}^T L_{\lambda}\), and is computed as:
Fisher Vector computation¶
As discussed, the Fisher Vector encodes each descriptor to multiple Gaussian components (also called “soft assignment”). The probability of a descriptor \(x_t\) belonging to the \(i\)-th Gaussian is computed with the Gaussian Mixture Model.
The Gaussian Mixture Model is chosen for \(u_{\lambda}(x) = \sum_{i=1}^{K} w_i u_i(x)\), where \(w_i, \mu_i, \Sigma_i\) are the mixture weights, mean vectors, and variance matrices of the Gaussian \(u_i\). The Fisher Vector is then computed as:
where:
\(\gamma_t(i)\) is the soft assignment of descriptor \(x_t\) to the \(i\)-th Gaussian.
\(w_i\), \(\mu_i\), and \(\Sigma_i\) are the mixture weight, mean vector, and covariance matrix of the \(i\)-th Gaussian component.
The final Fisher Vector \(G^{X}_{\lambda}\) is the concatenation of the vectors \(G^{X}_{i}\) for \(i = 1, \ldots, K\), resulting in a \(K \times d\)-dimensional vector. This vector captures both the occurrence and distributional properties of the local descriptors.
The resulting vector has shape (2 * K * D + K,), where K is the number
of GMM components and D is the local descriptor dimension (after optional
PCA).
Usage¶
from pyvisim.classic import FisherVectorEmbedder
fisher = FisherVectorEmbedder(
n_components=256, # number of mixture components
gmm_params={"rng": 0}, # forwarded to the GMM
pca_params={"n_components": 64}, # optional, omit for no PCA
)
fisher.learn(images) # fits the PCA (if any) then the GMM
embedding = fisher.embed(image) # Embed image into a Fisher Vector
# Cosine similarity between two images
similarity = fisher.similarity_score(image1, image2)
fisher.save_to_disk("fisher.safetensors") # Save the embedder to disk
# Load the embedder from disk
fisher = FisherVectorEmbedder.load_from_disk("fisher.safetensors")
References¶
H. Jégou et al. “Aggregating Local Image Descriptors into Compact Codes”. In: IEEE Transactions on Pattern Analysis and Machine Intelligence 34.9 (2012), pp. 1704-1716. doi: 10.1109/TPAMI.2011.235.
API reference¶
- class pyvisim.classic.FisherVectorEmbedder(feature_extractor=None, n_components=256, gmm_params=None, pca_params=None, power_norm_weight=0.5, norm_order=2, epsilon=1e-09, similarity_func='cosine', *, normalize=True, batch_size=16)[source]¶
Bases:
ClusteringBasedEmbedderThis class serves as an embedder that transforms input images into Fisher Vector descriptors.
The Fisher Vector representation is based on the gradients of the GMM parameters (weights, means, and covariances) with respect to the feature descriptors extracted from the images. The representation is optionally power-normalized and L2-normalized.
The Gaussian Mixture Model is configured from the parameters passed to this constructor (
n_componentsplus the optionalgmm_paramsdictionary) and fitted by callinglearn(). An optional PCA model for dimensionality reduction is configured the same way viapca_params.The output when calling embed has shape (2 * num_clusters * feature_dim + num_clusters,).
For more information, see the documentation:
https://mechacritter.github.io/Python-Visual-Similarity/classic/fisher_vector/fisher_vector.html.- Parameters:
feature_extractor (FeatureExtractorBase | None) – Feature extractor instance. If
None, RootSIFT is used.n_components (int) – Number of Gaussian mixture components (visual words) to use.
gmm_params (dict[str, Any] | None) –
Arguments for Gaussian Mixture Model during vocabulary learning:
Parameter
Default
Meaning
n_init1Number of k-means++ seeded EM runs. The run with the highest final log-likelihood is kept. Raise it for better, more stable vocabularies.
max_iter100Maximum number of EM iterations per run.
tol1e-3Convergence threshold: a run stops when the change of the mean per-sample log-likelihood between iterations falls below it.
reg_covar1e-6Non-negative regularisation added to (and floored on) the per-feature variances, keeping them strictly positive when a component collapses or dies.
rngNoneSeed (
int) ornumpy.random.Generatorfor reproducible fitting.pca_params (dict[str, Any] | None) –
Arguments for the Principal Component Analysis during vocabulary learning:
Parameter
Default
Meaning
n_components(required)
Number of components to keep. Must be at most
min(n_samples, n_features)of the training descriptors.whitenFalseScale each projected component to unit variance. Components with near-zero variance (rank-deficient descriptors) are floored at machine epsilon so the output stays finite.
svd_solver"auto""full"(economy SVD),"covariance_eigh"(eigendecomposition of the feature covariance, fastest for many samples with few features),"arpack"(truncated SVD, computes onlyn_componentssingular triplets), or"auto", which picks between them based on the training shape.tol0.0Convergence tolerance of the
"arpack"solver (0 means machine precision). Ignored by the other solvers.rngNoneSeed (
int) ornumpy.random.Generatorfor the"arpack"solver’s starting vector. Ignored by the other solvers.power_norm_weight (float) – Exponent for power normalization
norm_order (int) – Norm order for normalization.
epsilon (float) – Small constant to avoid division by zero.
similarity_func (str) – Name of the built-in similarity metric to use. One of
"cosine","euclidean","l1"or"manhattan".normalize (bool) – Whether
embedL2-normalizes the embeddings it returns.batch_size (int) – Maximum number of images processed in a single batch. Set to
-1to process all images as a single batch.
References
[1] Hervé Jégou, Florent Perronnin, Matthijs Douze, Jorge Sánchez, Patrick Pérez, and Cordelia Schmid, “Aggregating Local Image Descriptors into Compact Codes,” IEEE.
- embed(images, *, dims='HWC', value_range=(0.0, 255.0))¶
Embeds one or more images into a batch of vector representations.
Each image is normalized to a canonical
uint8(H, W, C)array before feature extraction, so NumPy arrays, torch tensors and other array-like inputs are all accepted. When a batch axis is present (viadims), every image in the batch is embedded. The resulting vectors are L2-normalized row by row whennormalizeis True.- Parameters:
images (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | Iterable[_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]]) – A single
MatLikeimage, a batched array, or an iterable of images. Consider using an iterator for large datasets.dims (str) – Axis-label string, one character per array axis in order:
"H"= height (rows),"W"= width (columns),"C"= channels (e.g. RGB),"B"= batch size. For example,"HWC"is height × width × channels (NumPy/OpenCV single-image layout);"CHW"is channels × height × width (PyTorch single-image layout);"BCHW"is batch × channels × height × width (PyTorch batched layout). Seepyvisim.typing.value_range (tuple[float, float]) – The
(low, high)range the input values live in; converted into the canonical[0, 255]range.
- Returns:
vector representations of the given images, L2-normalized row by row if
normalizeis True.- Raises:
ValueError – If
imagesholds no image.- Return type:
- classmethod from_dict(state, *, feature_extractor_params=None, **kwargs)¶
Rebuilds the embedder a state dictionary describes.
- Parameters:
- Returns:
The reconstructed embedder.
- Raises:
TypeError – If
kwargsis not empty, or the feature extractor does not take one offeature_extractor_params.ValueError – If the feature extractor cannot be rebuilt from its state and
feature_extractor_params.
- Return type:
_ClusteringEmbedderT
- learn(images, /, *, dim_reduction_factor=None, dims='HWC', value_range=(0.0, 255.0))¶
Learns the visual vocabulary from the given images.
The clustering model configured at initialization is fitted on the extracted features. If a PCA model is configured, the features are reduced with it first (fitting it beforehand if necessary).
- Parameters:
images (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | Iterable[_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]]) – A single
MatLikeimage, a batched array, or an iterable of images. Each image is normalized to a canonicaluint8(H, W, C)array before feature extraction.dim_reduction_factor (int | None) – If a value is provided, a new PCA model will be used to reduce the dimensionality of the feature space
dims (str) – Axis-label string, one character per array axis in order:
"H"= height (rows),"W"= width (columns),"C"= channels (e.g. RGB),"B"= batch size. For example,"HWC"is height × width × channels (NumPy/OpenCV single-image layout);"CHW"is channels × height × width (PyTorch single-image layout);"BCHW"is batch × channels × height × width (PyTorch batched layout). Seepyvisim.typing.value_range (tuple[float, float]) – The
(low, high)range the input values live in; converted into the canonical[0, 255]range.
- Raises:
RuntimeError – If the embedder has no clustering model configured.
ValueError – If dim_reduction_factor is provided but is not a positive integer.
- Return type:
None
- load_clustering_model_from_sklearn(model)¶
Replaces this embedder’s clustering model with one created from a scikit-learn estimator.
The estimator’s constructor arguments are translated by the clustering model class’
from_sklearnmethod; if the estimator is fitted, its learned state is adopted and validated against the configured feature extractor / PCA. Only clustering model classes exposingfrom_sklearnsupport this — currentlyKMeans(VLAD) andDiagCovarGaussianMixture(Fisher Vector).- Parameters:
model (Any) – A scikit-learn estimator of the type matching this embedder’s clustering model (e.g.
sklearn.cluster.KMeansfor VLAD), fitted or not.- Raises:
NotImplementedError – If this embedder’s clustering model class cannot be created from a scikit-learn estimator.
TypeError – If
modelis not of the supported estimator type.RuntimeError – If the fitted estimator’s input size is incompatible with the configured feature extractor or PCA.
- Return type:
None
- classmethod load_from_disk(path, **kwargs)¶
Loads an object previously saved with
save_to_disk().Not every part of an object survives serialization: an arbitrary callable such as a torchvision transform has no portable description, so it is left out of the file. Pass such an object back here as a keyword argument.
- Parameters:
kwargs (Any) – Objects the file cannot hold, forwarded to
from_dict().
- Returns:
A ready-to-use instance.
- Raises:
FileNotFoundError – If
pathdoes not exist.ValueError – If the file is not a valid file of this kind or was saved by a different class.
TypeError – If the class does not take one of
kwargs.
- Return type:
_SerializableT
- save_to_disk(path)¶
Saves the serialized state of this object to a file.
- set_batch_size(batch_size)¶
Sets the number of items processed per batch.
- Parameters:
batch_size (int) – Maximum number of images processed in a single batch. Set to
-1to process all images as a single batch.- Raises:
ValueError – If
batch_sizeis neither-1nor a positive integer.- Return type:
None
- similarity_score(images1, images2, *, dims='HWC', value_range=(0.0, 255.0))¶
Compute the similarity scores matrix between two (batches of) images.
- Parameters:
images1 (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | Iterable[_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]]) – First (batch of) image(s) as
MatLike(NumPy array, torch tensor or array-like).images2 (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | Iterable[_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]]) – Second (batch of) image(s) as
MatLike.dims (str) – Axis-label string, one character per array axis in order:
"H"= height (rows),"W"= width (columns),"C"= channels (e.g. RGB),"B"= batch size. For example,"HWC"is height × width × channels (NumPy/OpenCV single-image layout);"CHW"is channels × height × width (PyTorch single-image layout);"BCHW"is batch × channels × height × width (PyTorch batched layout). Seepyvisim.typing.value_range (tuple[float, float]) – The
(low, high)range the input values live in; converted into the canonical[0, 255]range.
- Returns:
The similarity score matrix of shape
(len(images1), len(images2)).- Return type:
- to_dict()¶
Serializes this object into a JSON-safe state dictionary.
The mapping holds the output of
_state()plus the format version under"format_version"and the class name under"__class__". Arrays may be embedded as__ndarray__nodes, which the serialization layer stores as binary tensors.- Returns:
A JSON-safe description suitable for
from_dict().- Return type: