.. _relnotes_v0.10.0: v0.10.0 ======= .. _relnotes_v0.10.0_Upgrade Notes: Upgrade Notes ------------- - The British spelling ``neighbour`` is replaced by ``neighbor`` throughout the library. One public name changes with it: ``InMemoryImageEmbeddingStore.retrieve_top_k_similar`` now takes ``expansion_neighbors`` where it took ``expansion_neighbours``. - The British spelling ``serialise`` is replaced by ``serialize`` throughout the library. Only documentation, docstrings and comments are affected. - ``DeepConvFeature`` moved to ``pyvisim.neural_networks.features``, so ``from pyvisim.features import DeepConvFeature`` now raises ``ImportError``. If your code imports the class, change the import: .. code:: python # before from pyvisim.features import DeepConvFeature # after from pyvisim.neural_networks.features import DeepConvFeature - ``pyvisim.structural`` and ``pyvisim.pixelwise`` moved under the new ``pyvisim.dense`` package. Imports of ``SSIM``, ``MSSSIM`` and ``PSNR`` from the old paths fail with ``ModuleNotFoundError``, so change them to ``pyvisim.dense.structural`` and ``pyvisim.dense.pixelwise``: .. code:: python from pyvisim.dense.pixelwise import PSNR from pyvisim.dense.structural import MSSSIM, SSIM - ``pyvisim.features.feature_extractor_from_dict`` was removed. Feature extractors are now rebuilt through ``FeatureExtractorBase.from_dict``, which takes the same mapping and returns the same extractor. You are affected if you import the function by name: .. code:: python # Before from pyvisim.features import feature_extractor_from_dict extractor = feature_extractor_from_dict(data) # After from pyvisim.base import FeatureExtractorBase extractor = FeatureExtractorBase.from_dict(data) Saved ``.embedder`` files are unaffected, since the serialized format did not change. - The first parameter of ``FeatureExtractorBase.from_dict`` was renamed from ``data`` to ``state``. You are affected if you pass it by keyword, and ``from_dict(data=...)`` becomes ``from_dict(state=...)``. - The mapping returned by a feature extractor's ``to_dict`` now also holds a ``"format_version"`` key. - The ``_serialization_config`` hook of ``FeatureExtractorBase`` was removed. A custom extractor that overrides it now overrides ``_state`` instead and returns ``{"config": ...}``, otherwise its arguments are no longer saved. - ``InMemoryImageEmbeddingStore`` no longer embeds its gallery in the constructor, which affects every store built through it. Call ``build_store()`` right after the constructor, or pass ``lazy_build=False`` to have the constructor build the store. - Until ``build_store()`` has run, ``search``, ``retrieve_top_k_similar``, ``embeddings``, ``embeddings_of``, ``index``, ``dim``, ``to_dict`` and ``save_to_disk`` raise ``RuntimeError``. A store that adopts an ``ExternalSearchIndex`` or is rebuilt by ``load_from_disk`` or ``from_dict`` comes back built. - ``build_store()`` now raises for an unreadable image file and for a gallery none of whose images could be embedded, which the constructor used to report. An unknown ``search_index``, a rejected ``index_params`` entry, a non-string path and an empty gallery still raise in the constructor. - ``pyvisim.neural_networks`` no longer re-exports ``DeepConvFeature``, so ``from pyvisim.neural_networks import DeepConvFeature`` now raises ``ImportError``. Import the class from ``pyvisim.neural_networks.features`` instead: .. code:: python # before from pyvisim.neural_networks import DeepConvFeature # after from pyvisim.neural_networks.features import DeepConvFeature - Importing ``pyvisim`` no longer configures logging, and its loggers are named after their modules (e.g. ``pyvisim.datasets.datasets``) instead of ``Data_Set``, ``Feature_Extractor``, ``Pipeline`` and ``Similarity_Metrics``. Configure the ``pyvisim`` logger to see the library's messages. - ``from pyvisim import *`` no longer imports ``datasets``, which needs the ``nn`` extra. Access it as ``pyvisim.datasets`` or import it explicitly. - ``VLADEmbedder`` and ``FisherVectorEmbedder`` default to ``raise_error_when_pca_incompatible=True``, so a clustering model the fitted PCA cannot feed raises a ``RuntimeError`` instead of silently dropping the PCA. Pass ``False`` to keep the old behavior. - ``pyvisim._base_classes`` is removed. Import ``SimilarityMetric``, ``FeatureExtractorBase``, ``ImageEmbedderBase`` and ``SerializableImageEmbedder`` from ``pyvisim.base`` instead. - ``VLADEmbedder`` and ``FisherVectorEmbedder`` no longer take ``raise_error_when_pca_incompatible``. A clustering model the fitted PCA cannot feed always raises a ``RuntimeError``, so drop the argument. - ``VLADEmbedder`` and ``FisherVectorEmbedder`` no longer take ``flatten``. ``embed`` always returns one flat row per image, so drop the argument. - Embedders saved by ``VLADEmbedder`` and ``FisherVectorEmbedder`` are written under format version 4. Files of version 3 still load, files of version 4 do not load with an older ``pyvisim``. - ``OxfordFlowerDataset`` no longer accepts a ``transform`` argument. Passing one raised ``NotImplementedError`` before, so only calls that spelled out ``transform=None`` or passed ``purpose`` positionally are affected: drop the argument and pass ``purpose`` by keyword. Every item stays the raw RGB ``uint8`` array that the embedders and metrics accept, and any preprocessing belongs to a wrapping ``Dataset``. - ``save_to_disk`` writes to the given path as is, and ``.embedder`` or ``.safetensors`` is no longer appended to it. Code that saves under a name without a suffix and loads the file with the suffix added passes the same full file name to both calls. - The ``__file_format__`` class attribute is removed from ``SerializerMixin`` and every serializable class. Subclasses outside the library drop their ``__file_format__``. - ``InMemoryImageEmbeddingStore.search`` is removed. Code that searched a store by vectors calls ``store.index.search(query_vectors, k)`` instead. - ``EmbeddingStore`` protocol class declares ``retrieve_top_k_similar(query_images, k)`` in place of ``search(query_vectors, k)``. Custom stores implement the new method. - ``pyvisim.image_store`` is now ``pyvisim.retrieval``, split into the subpackages ``image_store`` (``InMemoryImageEmbeddingStore``, ``BruteForceIndex``, ``HnswIndex``, ``ExternalSearchIndex``), ``reranking`` (``KReciprocalReranker``) and ``data`` (``Candidate``), so ``from pyvisim.image_store import ...`` now raises ``ModuleNotFoundError``. If your code imports from ``pyvisim.image_store``, change the import: .. code:: python # before from pyvisim.image_store import ( Candidate, InMemoryImageEmbeddingStore, KReciprocalReranker, ) # after from pyvisim.retrieval.data import Candidate from pyvisim.retrieval.image_store import InMemoryImageEmbeddingStore from pyvisim.retrieval.reranking import KReciprocalReranker - The ``vectors`` parameter of ``InMemoryImageEmbeddingStore.save_to_disk`` is renamed to ``embeddings``. Code that passes ``vectors=...`` passes ``embeddings=...`` instead. - Notebooks are committed without outputs. Run ``make strip-notebooks`` before committing one, since the CI rejects notebooks that still carry outputs or execution metadata. - Every ``.embedder`` file and image store file now names its class under ``"__class__"``, and their format versions go up by one. Files saved by 0.9.5 or earlier no longer load, so rebuild the embedder or store and save it again with ``save_to_disk``. - The ``__class_key__`` class attribute is removed from ``SerializerMixin`` and every serializable class. Subclasses outside the library drop their ``__class_key__``, since the class name is always stored under ``"__class__"``. .. _relnotes_v0.10.0_New Features: New Features ------------ - Feature extractors can be saved on their own with ``save_to_disk`` and loaded back with ``load_from_disk``, as ``.safetensors`` files. - ``InMemoryImageEmbeddingStore.build_store`` embeds the gallery and builds the search index. It does nothing on a store that is already built. - ``InMemoryImageEmbeddingStore.is_built`` reports whether the store has been built. .. _relnotes_v0.10.0_Enhancement Notes: Enhancement Notes ----------------- - The new tutorial ``Custom Feature Extractor with ORB`` under the classical methods shows how to write a feature extractor by inheriting from ``FeatureExtractorBase``, train a ``VLADEmbedder`` with it and compare two images. - The ``tutorials`` dependency group installs ``opencv-python-headless``, which the custom feature extractor tutorial uses for ``ORB``. - ``FeatureExtractorBase`` now owns both halves of its serialization contract: ``to_dict`` and the new ``from_dict``. Subclasses register themselves as they are defined, so a new feature extractor is rebuilt from a serialized description without being added to a hand-maintained table. Extractors that need more than their stored constructor arguments override the ``_from_config`` hook, as ``DeepConvFeature`` does to rebuild its backbone by name. - ``FeatureExtractorBase.from_dict`` forwards its keyword arguments to the ``_from_config`` hook, so a custom extractor can take objects its state cannot hold. - A ``DeepConvFeature`` built on a user-supplied model or a custom transform can be rebuilt by passing them back as ``backbone=`` and ``transform=``. A ``FutureWarning`` is emitted if the transform differs from the saved one. - ``VLADEmbedder`` and ``FisherVectorEmbedder`` take a ``feature_extractor_params`` dict in ``from_dict`` and ``load_from_disk``, which is forwarded to the feature extractor. An embedder on a ``DeepConvFeature`` with a user-supplied model is loaded with ``feature_extractor_params={"backbone": model}``. - The tutorial notebooks define their plotting helpers themselves instead of importing them from a shared module, so that a change to a helper no longer executes every notebook again. - ``OxfordFlowerDataset`` reads its ``.mat`` files with ``scipy.io.loadmat`` again. The internal MAT-file reader ``pyvisim.datasets._matloader`` was removed since SciPy is a runtime dependency. - Simplified the ``[dependency-groups]`` in ``pyproject.toml``: - Merged the ``release`` group into ``docs``, since ``reno`` and ``dulwich`` are only used to build the release notes pages of the documentation. - Merged ``types``, ``dead-code`` and ``fmt`` into a single ``lint`` group, since ``mypy``, ``vulture`` and ``ruff`` are all static analysis tools. Contributors running ``make`` targets are unaffected. Anyone invoking ``uv run``/``uv sync`` with ``--group release``, ``--group types``, ``--group dead-code`` or ``--group fmt`` directly should use ``--group docs`` or ``--group lint`` instead. - The tutorial notebooks moved from the separate examples repository into ``docs/tutorials/notebooks/``, together with their plotting helpers. - The Siamese, Triplet and VLAD/Fisher Vector tutorials train on a subset of Oxford Flowers, so that they also finish on a CPU. - The documentation renders every tutorial notebook as a page with its executed outputs. - The ``Docs`` workflow also builds pull requests, executes the tutorial notebooks they change and uploads the rendered HTML as the ``docs-html`` artifact. - The new ``Tutorials`` workflow executes every tutorial notebook from scratch every three days and on demand. - The clustering helper of the tutorials reports normalized mutual information under nmi instead of adjusted mutual information. - The image search tutorial builds its FAISS index from the embeddings of the store instead of embedding the gallery a second time. - The pipeline tutorial trains on every twelfth training image, which covers all 102 classes instead of the three the first 500 images belong to. - The Image Search and Retrieval Evaluation tutorials embed every fourth training image, and the evaluation queries every fourth validation and test image, so that they also finish on a CPU. - GitHub alert blocks such as > [!NOTE] in the tutorial notebooks render as admonitions in the documentation. - The tutorials are grouped into the numbered chapters Introduction, Classical methods, Metric learning methods and Image similarity search. Every notebook carries a single top-level heading, which the sidebar lists as ``1.1 `` under its chapter. - ``make docs`` renders the tutorial notebooks as they are on disk instead of executing them. ``make test-notebooks`` writes the executed notebooks back in place, so that a following ``make docs`` shows their outputs. .. _relnotes_v0.10.0_Bug Fixes: Bug Fixes --------- - A ``try-except`` block is added in class ``OxfordFlowerDataset`` so that it raises a ``TypeError`` when it is indexed with anything that is not an integer, for example a slice such as ``dataset[:20]``, instead of failing inside Pillow with an ``AttributeError`` about a ``list`` object, which made it hard to understand the cause. NumPy integers keep working as indices. - Importing ``pyvisim`` no longer creates a ``res/logs`` folder next to the installed package, which failed on read-only installs. - ``from pyvisim import *`` works without the ``nn`` extra, and submodules such as ``pyvisim.classic`` are reachable after a plain ``import pyvisim``. - The warning emitted when a clustering model resets an incompatible PCA is a ``FutureWarning`` and names ``raise_error_when_pca_incompatible=True`` as the way to raise instead. - ``SIFT`` and ``RootSIFT`` keep their constructor arguments when an embedder using them is saved and loaded, instead of coming back with the default arguments. - ``Pipeline([])`` raises a ``ValueError`` when it is built, instead of failing later inside ``embed`` with a NumPy error. - ``FisherVectorEmbedder`` raises a ``ValueError`` for an image that yields no descriptor, like ``VLADEmbedder`` does, instead of returning a row of NaNs when that image shares a batch. - ``top_k_map`` divides each average precision by ``min(R, k)``, with ``R`` the gallery images sharing the query label, instead of by the relevant images found in the top ``k``. mAP@k values were inflated before and come out lower now. - ``KMeans.fit`` rejects a feature matrix that is not 2-D or holds no sample, and a refit asks for the configured number of clusters again after SciPy dropped empty clusters on an earlier fit. - The source distribution now ships the Cython source of the PSNR kernel, so installing ``pyvisim`` from it no longer fails with ``'pyvisim/pixelwise/_kernel/_ssd_kernel.pyx' doesn't match any files``. - ``InMemoryImageEmbeddingStore.from_dict`` accepts the state returned by ``to_dict``. Before, it only accepted a state read from a file and raised ``TypeError`` otherwise.