v0.10.0¶
Upgrade Notes¶
The British spelling
neighbouris replaced byneighborthroughout the library. One public name changes with it:InMemoryImageEmbeddingStore.retrieve_top_k_similarnow takesexpansion_neighborswhere it tookexpansion_neighbours.The British spelling
serialiseis replaced byserializethroughout the library. Only documentation, docstrings and comments are affected.DeepConvFeaturemoved topyvisim.neural_networks.features, sofrom pyvisim.features import DeepConvFeaturenow raisesImportError. If your code imports the class, change the import:# before from pyvisim.features import DeepConvFeature # after from pyvisim.neural_networks.features import DeepConvFeature
pyvisim.structuralandpyvisim.pixelwisemoved under the newpyvisim.densepackage. Imports ofSSIM,MSSSIMandPSNRfrom the old paths fail withModuleNotFoundError, so change them topyvisim.dense.structuralandpyvisim.dense.pixelwise:from pyvisim.dense.pixelwise import PSNR from pyvisim.dense.structural import MSSSIM, SSIM
pyvisim.features.feature_extractor_from_dictwas removed. Feature extractors are now rebuilt throughFeatureExtractorBase.from_dict, which takes the same mapping and returns the same extractor. You are affected if you import the function by name:# 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
.embedderfiles are unaffected, since the serialized format did not change.The first parameter of
FeatureExtractorBase.from_dictwas renamed fromdatatostate. You are affected if you pass it by keyword, andfrom_dict(data=...)becomesfrom_dict(state=...).The mapping returned by a feature extractor’s
to_dictnow also holds a"format_version"key.The
_serialization_confighook ofFeatureExtractorBasewas removed. A custom extractor that overrides it now overrides_stateinstead and returns{"config": ...}, otherwise its arguments are no longer saved.InMemoryImageEmbeddingStoreno longer embeds its gallery in the constructor, which affects every store built through it. Callbuild_store()right after the constructor, or passlazy_build=Falseto have the constructor build the store.Until
build_store()has run,search,retrieve_top_k_similar,embeddings,embeddings_of,index,dim,to_dictandsave_to_diskraiseRuntimeError. A store that adopts anExternalSearchIndexor is rebuilt byload_from_diskorfrom_dictcomes 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 unknownsearch_index, a rejectedindex_paramsentry, a non-string path and an empty gallery still raise in the constructor.pyvisim.neural_networksno longer re-exportsDeepConvFeature, sofrom pyvisim.neural_networks import DeepConvFeaturenow raisesImportError. Import the class frompyvisim.neural_networks.featuresinstead:# before from pyvisim.neural_networks import DeepConvFeature # after from pyvisim.neural_networks.features import DeepConvFeature
Importing
pyvisimno longer configures logging, and its loggers are named after their modules (e.g.pyvisim.datasets.datasets) instead ofData_Set,Feature_Extractor,PipelineandSimilarity_Metrics. Configure thepyvisimlogger to see the library’s messages.from pyvisim import *no longer importsdatasets, which needs thennextra. Access it aspyvisim.datasetsor import it explicitly.VLADEmbedderandFisherVectorEmbedderdefault toraise_error_when_pca_incompatible=True, so a clustering model the fitted PCA cannot feed raises aRuntimeErrorinstead of silently dropping the PCA. PassFalseto keep the old behavior.pyvisim._base_classesis removed. ImportSimilarityMetric,FeatureExtractorBase,ImageEmbedderBaseandSerializableImageEmbedderfrompyvisim.baseinstead.VLADEmbedderandFisherVectorEmbedderno longer takeraise_error_when_pca_incompatible. A clustering model the fitted PCA cannot feed always raises aRuntimeError, so drop the argument.VLADEmbedderandFisherVectorEmbedderno longer takeflatten.embedalways returns one flat row per image, so drop the argument.Embedders saved by
VLADEmbedderandFisherVectorEmbedderare written under format version 4. Files of version 3 still load, files of version 4 do not load with an olderpyvisim.OxfordFlowerDatasetno longer accepts atransformargument. Passing one raisedNotImplementedErrorbefore, so only calls that spelled outtransform=Noneor passedpurposepositionally are affected: drop the argument and passpurposeby keyword. Every item stays the raw RGBuint8array that the embedders and metrics accept, and any preprocessing belongs to a wrappingDataset.save_to_diskwrites to the given path as is, and.embedderor.safetensorsis 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 fromSerializerMixinand every serializable class. Subclasses outside the library drop their__file_format__.InMemoryImageEmbeddingStore.searchis removed. Code that searched a store by vectors callsstore.index.search(query_vectors, k)instead.EmbeddingStoreprotocol class declaresretrieve_top_k_similar(query_images, k)in place ofsearch(query_vectors, k). Custom stores implement the new method.pyvisim.image_storeis nowpyvisim.retrieval, split into the subpackagesimage_store(InMemoryImageEmbeddingStore,BruteForceIndex,HnswIndex,ExternalSearchIndex),reranking(KReciprocalReranker) anddata(Candidate), sofrom pyvisim.image_store import ...now raisesModuleNotFoundError. If your code imports frompyvisim.image_store, change the import:# 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
vectorsparameter ofInMemoryImageEmbeddingStore.save_to_diskis renamed toembeddings. Code that passesvectors=...passesembeddings=...instead.Notebooks are committed without outputs. Run
make strip-notebooksbefore committing one, since the CI rejects notebooks that still carry outputs or execution metadata.Every
.embedderfile 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 withsave_to_disk.The
__class_key__class attribute is removed fromSerializerMixinand every serializable class. Subclasses outside the library drop their__class_key__, since the class name is always stored under"__class__".
New Features¶
Feature extractors can be saved on their own with
save_to_diskand loaded back withload_from_disk, as.safetensorsfiles.InMemoryImageEmbeddingStore.build_storeembeds the gallery and builds the search index. It does nothing on a store that is already built.InMemoryImageEmbeddingStore.is_builtreports whether the store has been built.
Enhancement Notes¶
The new tutorial
Custom Feature Extractor with ORBunder the classical methods shows how to write a feature extractor by inheriting fromFeatureExtractorBase, train aVLADEmbedderwith it and compare two images.The
tutorialsdependency group installsopencv-python-headless, which the custom feature extractor tutorial uses forORB.FeatureExtractorBasenow owns both halves of its serialization contract:to_dictand the newfrom_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_confighook, asDeepConvFeaturedoes to rebuild its backbone by name.FeatureExtractorBase.from_dictforwards its keyword arguments to the_from_confighook, so a custom extractor can take objects its state cannot hold.A
DeepConvFeaturebuilt on a user-supplied model or a custom transform can be rebuilt by passing them back asbackbone=andtransform=. AFutureWarningis emitted if the transform differs from the saved one.VLADEmbedderandFisherVectorEmbeddertake afeature_extractor_paramsdict infrom_dictandload_from_disk, which is forwarded to the feature extractor. An embedder on aDeepConvFeaturewith a user-supplied model is loaded withfeature_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.
OxfordFlowerDatasetreads its.matfiles withscipy.io.loadmatagain. The internal MAT-file readerpyvisim.datasets._matloaderwas removed since SciPy is a runtime dependency.Simplified the
[dependency-groups]inpyproject.toml:Merged the
releasegroup intodocs, sincerenoanddulwichare only used to build the release notes pages of the documentation.Merged
types,dead-codeandfmtinto a singlelintgroup, sincemypy,vultureandruffare all static analysis tools.
Contributors running
maketargets are unaffected. Anyone invokinguv run/uv syncwith--group release,--group types,--group dead-codeor--group fmtdirectly should use--group docsor--group lintinstead.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
Docsworkflow also builds pull requests, executes the tutorial notebooks they change and uploads the rendered HTML as thedocs-htmlartifact.The new
Tutorialsworkflow 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 <heading>under its chapter.make docsrenders the tutorial notebooks as they are on disk instead of executing them.make test-notebookswrites the executed notebooks back in place, so that a followingmake docsshows their outputs.
Bug Fixes¶
A
try-exceptblock is added in classOxfordFlowerDatasetso that it raises aTypeErrorwhen it is indexed with anything that is not an integer, for example a slice such asdataset[:20], instead of failing inside Pillow with anAttributeErrorabout alistobject, which made it hard to understand the cause. NumPy integers keep working as indices.Importing
pyvisimno longer creates ares/logsfolder next to the installed package, which failed on read-only installs.from pyvisim import *works without thennextra, and submodules such aspyvisim.classicare reachable after a plainimport pyvisim.The warning emitted when a clustering model resets an incompatible PCA is a
FutureWarningand namesraise_error_when_pca_incompatible=Trueas the way to raise instead.SIFTandRootSIFTkeep their constructor arguments when an embedder using them is saved and loaded, instead of coming back with the default arguments.Pipeline([])raises aValueErrorwhen it is built, instead of failing later insideembedwith a NumPy error.FisherVectorEmbedderraises aValueErrorfor an image that yields no descriptor, likeVLADEmbedderdoes, instead of returning a row of NaNs when that image shares a batch.top_k_mapdivides each average precision bymin(R, k), withRthe gallery images sharing the query label, instead of by the relevant images found in the topk. mAP@k values were inflated before and come out lower now.KMeans.fitrejects 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
pyvisimfrom it no longer fails with'pyvisim/pixelwise/_kernel/_ssd_kernel.pyx' doesn't match any files.InMemoryImageEmbeddingStore.from_dictaccepts the state returned byto_dict. Before, it only accepted a state read from a file and raisedTypeErrorotherwise.