#pragma once #include "IAsyncContext.h" #include "IDescriptor.h" namespace fsdk { #ifndef DOXYGEN_SHOULD_SKIP_THIS DECLARE_SMARTPTR(IStaticDescriptorStorage); DECLARE_SMARTPTR(IDynamicDescriptorStorage); DECLARE_SMARTPTR(IIndex); DECLARE_SMARTPTR(IDenseIndex); DECLARE_SMARTPTR(IDynamicIndex); DECLARE_SMARTPTR(IIndexBuilder); #endif /// Intergral type used as identification of descriptor in internal storage. using DescriptorId = size_t; /** * @brief Result of index search. * */ struct SearchResult : MatchingResult { /// Identificator of descriptor in some storage. DescriptorId index; /// Default constructor. SearchResult() noexcept : MatchingResult() , index(std::numeric_limits::max()) {} /** * @brief Construct structure with parameters. * @param [in] distance Distance between descriptors. * @param [in] similarity Similarity between descriptors. * @param [in] index Index of found descriptors in some storage. * */ SearchResult( float distance, float similarity, DescriptorId index) noexcept : MatchingResult(distance, similarity) , index(index) {} }; /** * @brief Static descriptor storage interface. * @details You may think of it as read only access to some internal container. * */ struct IStaticDescriptorStorage { /** * @brief Requests descriptor data out of internal storage. * @param [in] index Identification value of some descriptor. Might be received either * by using @see IDynamicDescriptorStorage::append methods, or as output of * @see IIndex::search query. Must be less than @see size(). * @param [out] descriptor Ptr to created descriptor object with correctly set * version and length. Only changes data of passed descriptor. * @return Result with error code. * @see IDescriptor, DescriptorId, Result and FSDKError for details. * */ virtual Result descriptorByIndex( const DescriptorId index, IDescriptor* descriptor) const noexcept = 0; /** * @brief Return version of stored descriptors. * @return Version of stored descriptors. If not initialized, 0 is returned. * */ virtual uint32_t getDescriptorVersion() const noexcept = 0; /** * @brief Return size of internal storage. * @return Size of internal storage. If not initialized, 0 is returned. * */ virtual uint64_t size() const noexcept = 0; }; /** * @brief Dynamic descriptor storage interface. * @details You may think of it as read+write access to some internal container. * */ struct IDynamicDescriptorStorage : IStaticDescriptorStorage { /** * @brief Appends descriptor to internal storage. * If used on @see IDynamicIndex graph updates itself too. * @param [in] descriptor Ptr to created descriptor with correct length, version * and data. * @return ResultValue with error code and identification * of appended descriptor. Such identification might be used to query descriptor * with @see IStaticDescriptorStorage::descriptorByIndex or remove it from storage * with @see removeDescriptor. * @see IDescriptor, DescriptorId, ResultValue and FSDKError for details. * */ virtual ResultValue appendDescriptor( const IDescriptor* descriptor) noexcept = 0; /** * @brief Appends batch of descriptors to internal storage. * If used on @see IDynamicIndex graph updates itself too. * @param [in] batch Batch of descriptors with correct length, version and data. * @return ResultValue with error code and identification * of the first appended descriptor. Other descriptors from batch are appended * sequentially in the same order as they are in the batch. Such identification * might be used to query descriptor with * @see IStaticDescriptorStorage::descriptorByIndex or remove it from storage * with @see removeDescriptor. * @see IDescriptorBatch, DescriptorId, ResultValue and FSDKError for details. * */ virtual ResultValue appendBatch( const IDescriptorBatch* batch) noexcept = 0; /** * @brief Removes descriptor out of internal storage. * If used on @see IDynamicIndex graph updates itself too. * @note IMPORTANT: If used on @see IDynamicIndex it will NOT actually erase * descriptor with given index out of internal storage. Instead, it will * remove it out of graph, so it is not searchable. * @note If used on @see IIndexBuilder, it WILL actually erase it. But beware: * if your storage is big enough performance might be very poor, because * descriptors are stored sequentially in vector-like data structure, so every * element after erased will be moved. * @param [in] index Identification of descriptors position in internal storage. * Is received by using append methods or @IIndex::search. * @return Result with error code. * @see DescriptorId, Result and FSDKError for details. * */ virtual Result removeDescriptor(const DescriptorId index) noexcept = 0; }; /** * @brief Base index interface. * @details You may think of index as some data structure optimized for search queries. * */ struct IIndex : IRefCounted { /** * @brief Search for descriptors with the shorter distance to passed descriptor. * @param [in] reference Descriptor to match against index. * @param [in] maxResultsCount Maximum count of results. It is upper bound value, it * does not guarantee to return exactly this amount of results. * @param [out] results C-Array of at least @see maxResultsCount size. Is filled with * query results. * @return ResultValue with error code and count of found descriptors. * @see IDescriptor, SearchResult, ResultValue and FSDKError for details. * */ virtual ResultValue search( const IDescriptor* reference, int maxResultsCount, SearchResult* results) const noexcept = 0; }; /** * @brief Dense (read only) index interface. * */ struct IDenseIndex : IStaticDescriptorStorage , IIndex { }; /** * @brief Dynamic index interface. * */ struct IDynamicIndex : IDynamicDescriptorStorage , IIndex { /** * @brief Saves index as dense. * @details To load saved index use @see IFaceEngine::loadDenseIndex method. * Dense index cannot be loaded as dynamic. * @param [in] path Path to file to be created and filled with index data. Any * extension is acceptable. * @return Result with error code. * @see Result and FSDKError for details. * */ virtual Result saveToDenseIndex(const char* path) const noexcept = 0; /** * @brief Saves index as dynamic. * @details To load saved index use @see IFaceEngine::loadDynamicIndex method. * Dynamic index cannot be loaded as dense. * @param [in] path Path to file to be created and filled with index data. Any * extension is acceptable. * @return Result with error code. * @see Result and FSDKError for details. * */ virtual Result saveToDynamicIndex(const char* path) const noexcept = 0; /** * @brief Returns count of indexed descriptors. * @details You may wonder why this method exists if IDynamicIndex already * inherits IStaticDescriptorStorage::size method. The reason is that * @see IDynamicDescriptorStorage::removeDescriptor behaves differently on * @see IIndexBuild and IDynamicIndex. On builder it does actually erases * descriptor out of internal storage, but it does not erase it if used on * IDynamicIndex. The reason is that graph data structure relies on indexes * being constant, so removeDescriptor only removes it out of graph, so it is not * discoverable by @see IIndex::search. So this methods returns actuall data * storage size minus count of removed descriptors. * @return Count of indexed descriptors. * */ virtual uint64_t countOfIndexedDescriptors() const noexcept = 0; }; /** * @brief Progress tracker interface. * @details Implement this interface to be able to get progress info on some operation. * */ struct IProgressTracker { /** * @brief Function is called on some operation progress change. * @param [in] completion float value in [0..1] range. * */ virtual void progress(const float completion) const noexcept = 0; }; /** * @brief Index builder interface. * */ struct IIndexBuilder : IDynamicDescriptorStorage , IRefCounted { /** * @brief Builds index with every descriptor appended. Blocks until completed. * @details Is very heavy method in terms of computing load. * @param [in] progressTracker Some object that is being reported to with progress. * If its nullptr, dont report progress. * @return ResultValue with error code and created index object. * @see IProgressTracker, IDynamicIndex, ResultValue and FSDKError for details. * */ virtual ResultValue buildIndex( const IProgressTracker* const progressTracker = nullptr) noexcept = 0; /** * @brief Builds index with every descriptor appended. Non blocking operation. * @details Is very heavy method in terms of computing load. * @param [in] asyncContext Asynchronous context to run build on. * @param [in] progressTracker Some object that is being reported to with progress. * If its nullptr, dont report progress. * @return ResultValue with error code and created index object. * @see IAsyncContext, IProgressTracker, Future, IDynamicIndex, ResultValue and FSDKError for details. * */ virtual ResultValue>> buildIndexAsync( IAsyncContext* const asyncContext, const IProgressTracker* const progressTracker = nullptr) noexcept = 0; }; }