Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into feature/dynamic-aggreg…
Browse files Browse the repository at this point in the history
…ation

# Conflicts:
#	vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColor.kt
#	vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColorExtractor.kt
#	vitrivr-engine-module-fes/src/main/kotlin/org/vitrivr/engine/base/features/external/implementations/classification/ImageClassificationExtractor.kt
  • Loading branch information
lucaro committed Aug 23, 2024
2 parents f09db70 + b04f878 commit 7fd87b5
Show file tree
Hide file tree
Showing 51 changed files with 1,801 additions and 418 deletions.
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
version_boofcv=1.1.5
version_caffeine=3.1.8
version_clikt=4.2.0
version_commonsmath3=3.6.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class BlackholeConnectionProvider : ConnectionProvider {
override val databaseName: String = "blackhole"
override val version: String = "1.0.0"
override fun openConnection(schemaName: String, parameters: Map<String, String>) = BlackholeConnection(schemaName, this, parameters["log"]?.toBoolean() == true)
override fun <T : Descriptor<*>> register(descriptorClass: KClass<T>, provider: DescriptorProvider<*>) { /* No op. */ }
override fun <T : Descriptor<*>> register(descriptorClass: KClass<T>, provider: DescriptorProvider<*>) { /* No op. */
}

override fun <T : Descriptor<*>> obtain(descriptorClass: KClass<T>) = BlackholeDescriptionProvider<T>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import org.vitrivr.engine.core.model.metamodel.Schema
* @author Ralph Gasser
* @version 1.0.0
*/
class BlackholeDescriptionProvider<T: Descriptor<*>>: DescriptorProvider<T> {
override fun newInitializer(connection: Connection, field: Schema.Field<*, T>)= BlackholeDescriptorInitializer(connection as BlackholeConnection, field)
class BlackholeDescriptionProvider<T : Descriptor<*>> : DescriptorProvider<T> {
override fun newInitializer(connection: Connection, field: Schema.Field<*, T>) = BlackholeDescriptorInitializer(connection as BlackholeConnection, field)
override fun newReader(connection: Connection, field: Schema.Field<*, T>): DescriptorReader<T> = BlackholeDescriptorReader(connection as BlackholeConnection, field)
override fun newWriter(connection: Connection, field: Schema.Field<*, T>): DescriptorWriter<T> = BlackholeDescriptorWriter(connection as BlackholeConnection, field)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.vitrivr.engine.core.model.metamodel.Schema
* @author Ralph Gasser
* @version 1.0.0
*/
class BlackholeDescriptorInitializer<T: Descriptor<*>>(private val connection: BlackholeConnection, override val field: Schema.Field<*, T>): DescriptorInitializer<T> {
class BlackholeDescriptorInitializer<T : Descriptor<*>>(private val connection: BlackholeConnection, override val field: Schema.Field<*, T>) : DescriptorInitializer<T> {
override fun initialize() = this.connection.logIf("Initializing descriptor entity '${this.field.fieldName}'.")
override fun deinitialize() = this.connection.logIf("De-initializing descriptor entity '${this.field.fieldName}'.")
override fun isInitialized(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import org.vitrivr.engine.core.model.retrievable.Retrieved
* @author Ralph Gasser
* @version 1.0.0
*/
class BlackholeDescriptorReader<T: Descriptor<*>>(override val connection: BlackholeConnection, override val field: Schema.Field<*, T>) : DescriptorReader<T> {
class BlackholeDescriptorReader<T : Descriptor<*>>(override val connection: BlackholeConnection, override val field: Schema.Field<*, T>) : DescriptorReader<T> {
override fun exists(descriptorId: DescriptorId): Boolean = false
override fun get(descriptorId: DescriptorId): T? = null
override fun getAll(descriptorIds: Iterable<DescriptorId>) = emptySequence<T>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.vitrivr.engine.core.model.metamodel.Schema
* @author Ralph Gasser
* @version 1.0.0
*/
class BlackholeDescriptorWriter<T: Descriptor<*>>(override val connection: BlackholeConnection, override val field: Schema.Field<*, T>): DescriptorWriter<T> {
class BlackholeDescriptorWriter<T : Descriptor<*>>(override val connection: BlackholeConnection, override val field: Schema.Field<*, T>) : DescriptorWriter<T> {
override fun add(item: T): Boolean {
this.connection.logIf("Adding descriptor '${item.id}' to entity '${this.field.fieldName}'.")
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.vitrivr.engine.core.database.retrievable.RetrievableInitializer
* @author Ralph Gasser
* @version 1.0.0
*/
class BlackholeRetrievableInitializer(private val connection: BlackholeConnection): RetrievableInitializer {
class BlackholeRetrievableInitializer(private val connection: BlackholeConnection) : RetrievableInitializer {
override fun initialize() = this.connection.logIf("Initializing entities 'retrievable' and 'relationship'.")
override fun deinitialize() = this.connection.logIf("De-initializing entities 'retrievable' and 'relationship'.")
override fun isInitialized(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import org.vitrivr.engine.core.model.retrievable.RetrievableId
*/
class BlackholeRetrievableReader(override val connection: BlackholeConnection) : RetrievableReader {
override fun get(id: RetrievableId): Retrievable? = null
override fun exists(id: RetrievableId): Boolean = false
override fun exists(id: RetrievableId): Boolean = false
override fun getAll(ids: Iterable<RetrievableId>) = emptySequence<Retrievable>()
override fun getAll() = emptySequence<Retrievable>()
override fun getConnections(subjectIds: Collection<RetrievableId>, predicates: Collection<String>, objectIds: Collection<RetrievableId>) = emptySequence<Triple<RetrievableId, String, RetrievableId>>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import org.vitrivr.engine.core.model.retrievable.Retrievable
* @author Ralph Gasser
* @version 1.0.0
*/
class BlackholeRetrievableWriter(override val connection: BlackholeConnection): RetrievableWriter {
class BlackholeRetrievableWriter(override val connection: BlackholeConnection) : RetrievableWriter {
override fun connect(relationship: Relationship): Boolean {
this.connection.logIf("Adding relationship ${relationship.subjectId} >[${relationship.predicate}] ${relationship.objectId}.")
return false
}

override fun connectAll(relationships: Iterable<Relationship>): Boolean {
relationships.forEach { relationship -> this.connection.logIf("Adding relationship ${relationship.subjectId} >[${relationship.predicate}] ${relationship.objectId}.")}
relationships.forEach { relationship -> this.connection.logIf("Adding relationship ${relationship.subjectId} >[${relationship.predicate}] ${relationship.objectId}.") }
return false
}

Expand All @@ -29,7 +29,7 @@ class BlackholeRetrievableWriter(override val connection: BlackholeConnection):
}

override fun disconnectAll(relationships: Iterable<Relationship>): Boolean {
relationships.forEach { relationship -> this.connection.logIf("Removing relationship ${relationship.subjectId} >[${relationship.predicate}] ${relationship.objectId}.")}
relationships.forEach { relationship -> this.connection.logIf("Removing relationship ${relationship.subjectId} >[${relationship.predicate}] ${relationship.objectId}.") }
return false
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.vitrivr.engine.core.features

import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
Expand All @@ -20,13 +22,16 @@ import org.vitrivr.engine.core.operators.retrieve.Retriever
* @see [AbstractRetriever]
*
* @author Rahel Arnold
* @version 1.0.0
* @version 1.0.1
*/
abstract class AbstractRetriever<C : ContentElement<*>, D : Descriptor<*>>(override val field: Schema.Field<C, D>, val query: Query, val context: QueryContext) : Retriever<C, D> {

/** The [DescriptorReader] instance used by this [AbstractRetriever]. */
protected val reader: DescriptorReader<D> by lazy { this.field.getReader() }

/** The [KLogger] instance used by this [AbstractRetriever]. */
protected val logger: KLogger = KotlinLogging.logger {}

/**
* Simplest implementation of the retrieval logic simply hand the [Query] to the [DescriptorReader] and emit the results.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package org.vitrivr.engine.core.features.averagecolor

import org.vitrivr.engine.core.context.IndexContext
import org.vitrivr.engine.core.context.QueryContext
import org.vitrivr.engine.core.model.color.MutableRGBFloatColorContainer
import org.vitrivr.engine.core.model.color.RGBByteColorContainer
import org.vitrivr.engine.core.model.color.RGBFloatColorContainer
import org.vitrivr.engine.core.features.dense.DenseRetriever
import org.vitrivr.engine.core.math.correspondence.LinearCorrespondence
import org.vitrivr.engine.core.model.color.RGBColorContainer
import org.vitrivr.engine.core.model.content.Content
import org.vitrivr.engine.core.model.content.element.ImageContent
import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor
Expand All @@ -13,7 +13,6 @@ import org.vitrivr.engine.core.model.metamodel.Schema
import org.vitrivr.engine.core.model.query.Query
import org.vitrivr.engine.core.model.query.proximity.ProximityQuery
import org.vitrivr.engine.core.model.retrievable.Retrievable
import org.vitrivr.engine.core.model.retrievable.RetrievableId
import org.vitrivr.engine.core.model.types.Value
import org.vitrivr.engine.core.operators.Operator
import org.vitrivr.engine.core.operators.ingest.Extractor
Expand All @@ -31,28 +30,9 @@ import java.util.*
* @version 1.0.0
*/
class AverageColor : Analyser<ImageContent, FloatVectorDescriptor> {

override val contentClasses = setOf(ImageContent::class)
override val descriptorClass = FloatVectorDescriptor::class

companion object {
/**
* Performs the [AverageColor] analysis on the provided [List] of [ImageContent] elements.
*
* @param content The [List] of [ImageContent] elements.
* @return [List] of [FloatVectorDescriptor]s.
*/
fun analyse(content: Collection<ImageContent>, retrievableId: RetrievableId? = null, field: Schema.Field<*, FloatVectorDescriptor>? = null): List<FloatVectorDescriptor> = content.map {
val color = MutableRGBFloatColorContainer()
val rgb = it.content.getRGBArray()
rgb.forEach { c -> color += RGBByteColorContainer(c) }

/* Generate descriptor. */
val averageColor = RGBFloatColorContainer(color.red / rgb.size, color.green / rgb.size, color.blue / rgb.size)
FloatVectorDescriptor(UUID.randomUUID(), retrievableId, averageColor.toVector(), field)
}
}

/**
* Generates a prototypical [FloatVectorDescriptor] for this [AverageColor].
*
Expand Down Expand Up @@ -86,33 +66,30 @@ class AverageColor : Analyser<ImageContent, FloatVectorDescriptor> {
override fun newExtractor(name: String, input: Operator<Retrievable>, context: IndexContext): Extractor<ImageContent, FloatVectorDescriptor> = AverageColorExtractor(input, this, name)

/**
* Generates and returns a new [AverageColorRetriever] instance for this [AverageColor].
* Generates and returns a new [DenseRetriever] instance for this [AverageColor].
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param query The [Query] to use with the [Retriever].
* @param context The [QueryContext] to use with the [Retriever].
*
* @return A new [Retriever] instance for this [Analyser]
*/
override fun newRetrieverForQuery(field: Schema.Field<ImageContent, FloatVectorDescriptor>, query: Query, context: QueryContext): AverageColorRetriever {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }
override fun newRetrieverForQuery(field: Schema.Field<ImageContent, FloatVectorDescriptor>, query: Query, context: QueryContext): DenseRetriever<ImageContent> {
require(query is ProximityQuery<*> && query.value is Value.FloatVector) { "The query is not a ProximityQuery<Value.FloatVector>." }
@Suppress("UNCHECKED_CAST")
return AverageColorRetriever(field, query as ProximityQuery<Value.FloatVector>)
return DenseRetriever(field, query as ProximityQuery<Value.FloatVector>, context, LinearCorrespondence(3f))
}

/**
* Generates and returns a new [AverageColorRetriever] instance for this [AverageColor].
* Generates and returns a new [DenseRetriever] instance for this [AverageColor].
*
* Invoking this method involves converting the provided [FloatVectorDescriptor] into a [ProximityQuery] that can be used to retrieve similar [ImageContent] elements.
*
* @param field The [Schema.Field] to create an [Retriever] for.
* @param descriptors An array of [FloatVectorDescriptor] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*/
override fun newRetrieverForDescriptors(field: Schema.Field<ImageContent, FloatVectorDescriptor>, descriptors: Collection<FloatVectorDescriptor>, context: QueryContext): AverageColorRetriever {
require(field.analyser == this) { "The field '${field.fieldName}' analyser does not correspond with this analyser. This is a programmer's error!" }

override fun newRetrieverForDescriptors(field: Schema.Field<ImageContent, FloatVectorDescriptor>, descriptors: Collection<FloatVectorDescriptor>, context: QueryContext): DenseRetriever<ImageContent> {
/* Prepare query parameters. */
val k = context.getProperty(field.fieldName, "limit")?.toLongOrNull() ?: 1000L
val fetchVector = context.getProperty(field.fieldName, "returnDescriptor")?.toBooleanStrictOrNull() ?: false
Expand All @@ -122,7 +99,7 @@ class AverageColor : Analyser<ImageContent, FloatVectorDescriptor> {
}

/**
* Generates and returns a new [AverageColorRetriever] instance for this [AverageColor].
* Generates and returns a new [DenseRetriever] instance for this [AverageColor].
*
* Invoking this method involves converting the provided [ImageContent] and the [QueryContext] into a [FloatVectorDescriptor]
* that can be used to retrieve similar [ImageContent] elements.
Expand All @@ -131,6 +108,27 @@ class AverageColor : Analyser<ImageContent, FloatVectorDescriptor> {
* @param content An array of [Content] elements to use with the [Retriever]
* @param context The [QueryContext] to use with the [Retriever]
*/
override fun newRetrieverForContent(field: Schema.Field<ImageContent, FloatVectorDescriptor>, content: Collection<ImageContent>, context: QueryContext): AverageColorRetriever =
this.newRetrieverForDescriptors(field, analyse(content), context)
override fun newRetrieverForContent(field: Schema.Field<ImageContent, FloatVectorDescriptor>, content: Collection<ImageContent>, context: QueryContext): DenseRetriever<ImageContent> =
this.newRetrieverForDescriptors(field, content.map { this.analyse(it) }, context)

/**
* Performs the [AverageColor] analysis on the provided [List] of [ImageContent] elements.
*
* @param content The [List] of [ImageContent] elements.
* @return [List] of [FloatVectorDescriptor]s.
*/
fun analyse(content: ImageContent): FloatVectorDescriptor {
val color = floatArrayOf(0f, 0f, 0f)
val rgb = content.content.getRGBArray()
for (c in rgb) {
val container = RGBColorContainer(c)
color[0] += container.red
color[1] += container.green
color[2] += container.blue
}

/* Generate descriptor. */
val averageColor = RGBColorContainer(color[0] / rgb.size, color[1] / rgb.size, color[2] / rgb.size)
return FloatVectorDescriptor(UUID.randomUUID(), null, averageColor.toVector())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ class AverageColorExtractor : AbstractExtractor<ImageContent, FloatVectorDescrip
*/
override fun extract(retrievable: Retrievable): List<FloatVectorDescriptor> {
val content = retrievable.content.filterIsInstance<ImageContent>()
return AverageColor.analyse(content, retrievable.id, this@AverageColorExtractor.field)
return content.map { (this.analyser as AverageColor).analyse(it).copy(retrievableId = retrievable.id, field = this.field) }
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flow
import org.vitrivr.engine.core.context.QueryContext
import org.vitrivr.engine.core.features.AbstractRetriever
import org.vitrivr.engine.core.math.correspondence.CorrespondenceFunction
import org.vitrivr.engine.core.model.content.element.ContentElement
import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor
import org.vitrivr.engine.core.model.metamodel.Schema
import org.vitrivr.engine.core.model.query.proximity.ProximityQuery
import org.vitrivr.engine.core.util.math.ScoringFunctions
import org.vitrivr.engine.core.model.retrievable.attributes.DistanceAttribute
import org.vitrivr.engine.core.model.retrievable.attributes.ScoreAttribute

/**
* [DenseRetriever] implementation for proximity-based retrieval on float vector embeddings.
Expand All @@ -24,10 +26,17 @@ import org.vitrivr.engine.core.util.math.ScoringFunctions
* @author Fynn Faber
* @version 1.0.0
*/
class DenseRetriever<C : ContentElement<*>>(field: Schema.Field<C, FloatVectorDescriptor>, query: ProximityQuery<*>, context: QueryContext) : AbstractRetriever<C, FloatVectorDescriptor>(field, query, context) {
class DenseRetriever<C : ContentElement<*>>(field: Schema.Field<C, FloatVectorDescriptor>, query: ProximityQuery<*>, context: QueryContext, val correspondence: CorrespondenceFunction) :
AbstractRetriever<C, FloatVectorDescriptor>(field, query, context) {
override fun toFlow(scope: CoroutineScope) = flow {
this@DenseRetriever.reader.queryAndJoin(this@DenseRetriever.query).forEach {
it.addAttribute(ScoringFunctions.bounded(it, 0.0f, 2.0f))
val distance = it.filteredAttribute<DistanceAttribute>()
if (distance != null) {
it.addAttribute(this@DenseRetriever.correspondence(distance))
} else {
this@DenseRetriever.logger.warn { "No distance attribute found for descriptor ${it.id}." }
it.addAttribute(ScoreAttribute.Similarity(0.0f))
}
emit(it)
}
}
Expand Down
Loading

0 comments on commit 7fd87b5

Please sign in to comment.