diff --git a/build.gradle b/build.gradle index 31bcb8c51..3f4169278 100644 --- a/build.gradle +++ b/build.gradle @@ -103,6 +103,7 @@ subprojects { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: version_junit testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: version_junit testImplementation group: 'org.junit.platform', name: 'junit-platform-commons', version: version_junit_platform + testImplementation(group: "org.jetbrains.kotlinx", "name": "kotlinx-coroutines-test", version: version_kotlinx_coroutines) } } diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractBatchedExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractBatchedExtractor.kt index 6056a2ef4..89fc34449 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractBatchedExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractBatchedExtractor.kt @@ -3,12 +3,17 @@ 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.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.descriptor.Descriptor import org.vitrivr.engine.core.model.metamodel.Analyser import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.retrievable.Retrievable +import org.vitrivr.engine.core.model.retrievable.attributes.CONTENT_AUTHORS_KEY +import org.vitrivr.engine.core.model.retrievable.attributes.ContentAuthorAttribute import org.vitrivr.engine.core.operators.Operator import org.vitrivr.engine.core.operators.ingest.Extractor @@ -19,15 +24,27 @@ import org.vitrivr.engine.core.operators.ingest.Extractor * @author Ralph Gasser * @version 1.0.0 */ -abstract class AbstractBatchedExtractor, D : Descriptor<*>>(final override val input: Operator, final override val analyser: Analyser, final override val field: Schema.Field?, private val bufferSize: Int = 100) : Extractor { - - private val logger: KLogger = KotlinLogging.logger {} +abstract class AbstractBatchedExtractor, D : Descriptor<*>>(final override val input: Operator, final override val analyser: Analyser, final override val field: Schema.Field?, protected val parameters: Map) : Extractor { + companion object { + const val BATCH_SIZE_KEY = "batchSize" + } init { require(field == null || this.field.analyser == this.analyser) { "Field and analyser do not match! This is a programmer's error!" } } + /** The [KLogger] instance used by this [AbstractExtractor]. */ + protected val logger: KLogger = KotlinLogging.logger {} + + /** The names of the content source to consider during processing. */ + protected val contentSources : Set? + get() = this.parameters[CONTENT_AUTHORS_KEY]?.split(",")?.toSet() + + /** The buffer- and batch size. */ + private val bufferSize : Int + get() = this.parameters[BATCH_SIZE_KEY]?.toIntOrNull() ?: 1 + /** * A default [Extractor] implementation for batched extraction. It executes the following steps: * @@ -101,4 +118,23 @@ abstract class AbstractBatchedExtractor, D : Descriptor<*> */ protected abstract fun extract(retrievables: List): List> + /** + * Filters the content of a [Retrievable] based on the [ContentAuthorAttribute] and the [contentSources] parameter. + * + * @param retrievable [Retrievable] to extract content from. + */ + @Suppress("UNCHECKED_CAST") + protected fun filterContent(retrievable: Retrievable): List { + val contentIds = this.contentSources?.let { + retrievable.filteredAttribute(ContentAuthorAttribute::class.java)?.getContentIds(it) + } + return retrievable.content.filter { content -> + if (this.analyser.contentClasses.none { it.isInstance(content) }) return@filter false + if (contentIds == null) { + return@filter true + } else { + return@filter contentIds.contains(content.id) + } + }.map { it as C } + } } \ No newline at end of file diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractExtractor.kt index 616924647..9bfc3e91c 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/AbstractExtractor.kt @@ -10,6 +10,8 @@ import org.vitrivr.engine.core.model.descriptor.Descriptor import org.vitrivr.engine.core.model.metamodel.Analyser import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.retrievable.Retrievable +import org.vitrivr.engine.core.model.retrievable.attributes.CONTENT_AUTHORS_KEY +import org.vitrivr.engine.core.model.retrievable.attributes.ContentAuthorAttribute import org.vitrivr.engine.core.operators.Operator import org.vitrivr.engine.core.operators.ingest.Extractor @@ -17,16 +19,21 @@ import org.vitrivr.engine.core.operators.ingest.Extractor * An abstract [Extractor] implementation that is suitable for most default [Extractor] implementations. * * @author Ralph Gasser - * @version 1.2.0 + * @version 1.3.0 */ -abstract class AbstractExtractor, D : Descriptor<*>>(final override val input: Operator, final override val analyser: Analyser, final override val field: Schema.Field? = null) : Extractor { - - protected val logger: KLogger = KotlinLogging.logger {} +abstract class AbstractExtractor, D : Descriptor<*>>(final override val input: Operator, final override val analyser: Analyser, final override val field: Schema.Field? = null, protected val parameters: Map) : Extractor { init { require(field == null || this.field.analyser == this.analyser) { "Field and analyser do not match! This is a programmer's error!" } } + /** The [KLogger] instance used by this [AbstractExtractor]. */ + protected val logger: KLogger = KotlinLogging.logger {} + + /** The names of the content source to consider during processing. */ + protected val contentSources : Set? + get() = this.parameters[CONTENT_AUTHORS_KEY]?.split(",")?.toSet() + /** * A default [Extractor] implementation. It executes the following steps: * @@ -36,7 +43,7 @@ abstract class AbstractExtractor, D : Descriptor<*>>(final * * @return [Flow] of [Retrievable] */ - final override fun toFlow(scope: CoroutineScope): Flow = this.input.toFlow(scope).onEach { retrievable -> + override fun toFlow(scope: CoroutineScope): Flow = this.input.toFlow(scope).onEach { retrievable -> if (this.matches(retrievable)) { /* Perform extraction. */ val descriptors = try { @@ -74,4 +81,24 @@ abstract class AbstractExtractor, D : Descriptor<*>>(final * @return List of resulting [Descriptor]s. */ protected abstract fun extract(retrievable: Retrievable): List + + /** + * Filters the content of a [Retrievable] based on the [ContentAuthorAttribute] and the [contentSources] parameter. + * + * @param retrievable [Retrievable] to extract content from. + */ + @Suppress("UNCHECKED_CAST") + protected fun filterContent(retrievable: Retrievable): List { + val contentIds = this.contentSources?.let { + retrievable.filteredAttribute(ContentAuthorAttribute::class.java)?.getContentIds(it) + } + return retrievable.content.filter { content -> + if (this.analyser.contentClasses.none { it.isInstance(content) }) return@filter false + if (contentIds == null) { + return@filter true + } else { + return@filter contentIds.contains(content.id) + } + }.map { it as C } + } } diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColor.kt index fc307eaf0..ff63750f7 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColor.kt @@ -9,6 +9,7 @@ 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 import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge 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 @@ -51,7 +52,7 @@ class AverageColor : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = AverageColorExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = AverageColorExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [AverageColorExtractor] instance for this [AverageColor]. @@ -63,7 +64,7 @@ class AverageColor : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(name: String, input: Operator, context: IndexContext): Extractor = AverageColorExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext): Extractor = AverageColorExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [DenseRetriever] instance for this [AverageColor]. diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColorExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColorExtractor.kt index 41ffcd315..303315e27 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColorExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/averagecolor/AverageColorExtractor.kt @@ -8,6 +8,7 @@ import org.vitrivr.engine.core.model.descriptor.Descriptor import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.retrievable.Retrievable +import org.vitrivr.engine.core.model.retrievable.attributes.CONTENT_AUTHORS_KEY import org.vitrivr.engine.core.operators.Operator import org.vitrivr.engine.core.operators.ingest.Extractor import org.vitrivr.engine.core.source.file.FileSource @@ -20,7 +21,9 @@ import org.vitrivr.engine.core.source.file.FileSource * @author Luca Rossetto * @version 1.2.0 */ -class AverageColorExtractor(input: Operator, analyser: AverageColor, field: Schema.Field?) : AbstractExtractor(input, analyser, field) { +class AverageColorExtractor(input: Operator, analyser: AverageColor, field: Schema.Field?, parameters : Map) : AbstractExtractor(input, analyser, field, parameters) { + + /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -38,7 +41,7 @@ class AverageColorExtractor(input: Operator, analyser: AverageColor * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { (this.analyser as AverageColor).analyse(it).copy(retrievableId = retrievable.id, field = this.field) } } } \ No newline at end of file diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadata.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadata.kt index d745d6dfc..880243ed8 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadata.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadata.kt @@ -6,6 +6,7 @@ import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.descriptor.Attribute import org.vitrivr.engine.core.model.descriptor.struct.AnyMapStructDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.bool.SimpleBooleanQuery @@ -39,7 +40,7 @@ class ExifMetadata : Analyser, AnyMapStructDescriptor> { * * @return A new [Extractor] instance for this [Analyser] */ - override fun newExtractor(name: String, input: Operator, context: IndexContext) = ExifMetadataExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext) = ExifMetadataExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [ExifMetadataExtractor] instance for this [ExifMetadata]. @@ -50,7 +51,7 @@ class ExifMetadata : Analyser, AnyMapStructDescriptor> { * * @return A new [Extractor] instance for this [Analyser] */ - override fun newExtractor(field: Schema.Field, AnyMapStructDescriptor>, input: Operator, context: IndexContext) = ExifMetadataExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, AnyMapStructDescriptor>, input: Operator, context: IndexContext) = ExifMetadataExtractor(input, this, field, merge(field, context)) /** * diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadataExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadataExtractor.kt index 5bf2ef731..d56233df9 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadataExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/exif/ExifMetadataExtractor.kt @@ -87,7 +87,7 @@ private fun JsonObject.convertType(type: Type): Value<*>? { } } -class ExifMetadataExtractor(input: Operator, analyser: ExifMetadata, field: Schema.Field, AnyMapStructDescriptor>?) : AbstractExtractor, AnyMapStructDescriptor>(input, analyser, field) { +class ExifMetadataExtractor(input: Operator, analyser: ExifMetadata, field: Schema.Field, AnyMapStructDescriptor>?, parameters: Map) : AbstractExtractor, AnyMapStructDescriptor>(input, analyser, field, parameters) { override fun matches(retrievable: Retrievable): Boolean = diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadata.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadata.kt index 66990071c..493cabb6c 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadata.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadata.kt @@ -5,6 +5,7 @@ import org.vitrivr.engine.core.context.QueryContext import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.descriptor.struct.metadata.source.FileSourceMetadataDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.bool.SimpleBooleanQuery @@ -39,7 +40,7 @@ class FileSourceMetadata : Analyser, FileSourceMetadataDescrip * * @return [FileSourceMetadataExtractor] */ - override fun newExtractor(field: Schema.Field, FileSourceMetadataDescriptor>, input: Operator, context: IndexContext) = FileSourceMetadataExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, FileSourceMetadataDescriptor>, input: Operator, context: IndexContext) = FileSourceMetadataExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [FileSourceMetadataExtractor] for the provided [Schema.Field]. @@ -50,7 +51,7 @@ class FileSourceMetadata : Analyser, FileSourceMetadataDescrip * * @return [FileSourceMetadataExtractor] */ - override fun newExtractor(name: String, input: Operator, context: IndexContext) = FileSourceMetadataExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext) = FileSourceMetadataExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [FileSourceMetadataRetriever] for the provided [Schema.Field]. diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadataExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadataExtractor.kt index 52c473795..ed6cdb02e 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadataExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/file/FileSourceMetadataExtractor.kt @@ -22,8 +22,8 @@ import kotlin.io.path.absolutePathString * @author Ralph Gasser * @version 1.2.0 */ -class FileSourceMetadataExtractor(input: Operator, analyser: FileSourceMetadata, field: Schema.Field, FileSourceMetadataDescriptor>?) : - AbstractExtractor, FileSourceMetadataDescriptor>(input, analyser, field) { +class FileSourceMetadataExtractor(input: Operator, analyser: FileSourceMetadata, field: Schema.Field, FileSourceMetadataDescriptor>?, parameters: Map) : + AbstractExtractor, FileSourceMetadataDescriptor>(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadata.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadata.kt index 13bdb7591..576b1fd2b 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadata.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadata.kt @@ -9,6 +9,7 @@ import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.descriptor.struct.metadata.source.FileSourceMetadataDescriptor import org.vitrivr.engine.core.model.descriptor.struct.metadata.source.VideoSourceMetadataDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.bool.BooleanQuery @@ -43,7 +44,7 @@ class VideoSourceMetadata : Analyser, VideoSourceMetadataDescr * * @return [FileSourceMetadataExtractor] */ - override fun newExtractor(field: Schema.Field, VideoSourceMetadataDescriptor>, input: Operator, context: IndexContext) = VideoSourceMetadataExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, VideoSourceMetadataDescriptor>, input: Operator, context: IndexContext) = VideoSourceMetadataExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [FileSourceMetadataExtractor] for the provided [Schema.Field]. @@ -54,7 +55,7 @@ class VideoSourceMetadata : Analyser, VideoSourceMetadataDescr * * @return [FileSourceMetadataExtractor] */ - override fun newExtractor(name: String, input: Operator, context: IndexContext): Extractor, VideoSourceMetadataDescriptor> = VideoSourceMetadataExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext): Extractor, VideoSourceMetadataDescriptor> = VideoSourceMetadataExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [VideoSourceMetadataRetriever] for the provided [Schema.Field]. diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadataExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadataExtractor.kt index d8acad29e..1782a46e1 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadataExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/source/video/VideoSourceMetadataExtractor.kt @@ -21,8 +21,8 @@ import java.util.* * @author Ralph Gasser * @version 1.1.0 */ -class VideoSourceMetadataExtractor(input: Operator, analyser: VideoSourceMetadata, field: Schema.Field, VideoSourceMetadataDescriptor>?) : - AbstractExtractor, VideoSourceMetadataDescriptor>(input, analyser, field) { +class VideoSourceMetadataExtractor(input: Operator, analyser: VideoSourceMetadata, field: Schema.Field, VideoSourceMetadataDescriptor>?, parameters: Map) : + AbstractExtractor, VideoSourceMetadataDescriptor>(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadata.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadata.kt index b34f8c03f..b1b1c0a57 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadata.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadata.kt @@ -9,6 +9,7 @@ import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.descriptor.struct.metadata.TemporalMetadataDescriptor import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.bool.BooleanQuery @@ -42,7 +43,7 @@ class TemporalMetadata : Analyser, TemporalMetadataDescriptor> * * @return [TemporalMetadataExtractor] */ - override fun newExtractor(field: Schema.Field, TemporalMetadataDescriptor>, input: Operator, context: IndexContext) = TemporalMetadataExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, TemporalMetadataDescriptor>, input: Operator, context: IndexContext) = TemporalMetadataExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [FileSourceMetadataExtractor] for the provided [Schema.Field]. @@ -53,7 +54,7 @@ class TemporalMetadata : Analyser, TemporalMetadataDescriptor> * * @return [TemporalMetadataExtractor] */ - override fun newExtractor(name: String, input: Operator, context: IndexContext) = TemporalMetadataExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext) = TemporalMetadataExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [TemporalMetadataRetriever] for the provided [Schema.Field]. diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadataExtractor.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadataExtractor.kt index e56817991..56ed800ec 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadataExtractor.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/features/metadata/temporal/TemporalMetadataExtractor.kt @@ -20,8 +20,8 @@ import java.util.* * @author Ralph Gasser * @version 1.3.0 */ -class TemporalMetadataExtractor(input: Operator, analyser: TemporalMetadata, field: Schema.Field, TemporalMetadataDescriptor>?) : - AbstractExtractor, TemporalMetadataDescriptor>(input, analyser, field) { +class TemporalMetadataExtractor(input: Operator, analyser: TemporalMetadata, field: Schema.Field, TemporalMetadataDescriptor>?, parameters: Map) : + AbstractExtractor, TemporalMetadataDescriptor>(input, analyser, field, parameters) { override fun matches(retrievable: Retrievable): Boolean = retrievable.hasAttribute(TimePointAttribute::class.java) || retrievable.hasAttribute(TimeRangeAttribute::class.java) diff --git a/vitrivr-engine-index/build.gradle b/vitrivr-engine-index/build.gradle index 5c386ab6d..fa4523819 100644 --- a/vitrivr-engine-index/build.gradle +++ b/vitrivr-engine-index/build.gradle @@ -83,8 +83,8 @@ publishing { email = 'rahel.arnold@unibas.ch' } developer { - id = 'rahelarnold98' - name = 'faberf' + id = 'faberf' + name = 'Fynn Faber' email = 'fynnfirouz.faber@unibas.ch' } } diff --git a/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/AbstractAggregator.kt b/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/AbstractAggregator.kt index bfc173ce7..97dec1d8e 100644 --- a/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/AbstractAggregator.kt +++ b/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/AbstractAggregator.kt @@ -19,7 +19,7 @@ import java.util.* * @author Ralph Gasser * @version 1.1.0 */ -abstract class AbstractAggregator(override val input: Operator, protected open val context: Context, protected val name: String, val newContent: Boolean = true) : Transformer { +abstract class AbstractAggregator(override val input: Operator, protected open val context: Context, protected val name: String, val newContent: Boolean = false) : Transformer { /** * Creates a flow for this [AbstractAggregator]. * diff --git a/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/LastContentAggregator.kt b/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/LastContentAggregator.kt index a58a09a47..3557b1f8c 100644 --- a/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/LastContentAggregator.kt +++ b/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/aggregators/LastContentAggregator.kt @@ -30,7 +30,7 @@ class LastContentAggregator : TransformerFactory { /** * The [Instance] returns by the [LastContentAggregator] */ - private class Instance(override val input: Operator, context: Context, name: String) : AbstractAggregator(input, context, name, newContent = false) { + private class Instance(override val input: Operator, context: Context, name: String) : AbstractAggregator(input, context, name) { override fun aggregate(content: List>): List> = content.groupBy { it.type }.mapNotNull { (_, elements) -> elements.lastOrNull() } } } diff --git a/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/exporters/ThumbnailExporter.kt b/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/exporters/ThumbnailExporter.kt index 31212163e..2648699c4 100644 --- a/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/exporters/ThumbnailExporter.kt +++ b/vitrivr-engine-index/src/main/kotlin/org/vitrivr/engine/index/exporters/ThumbnailExporter.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.onEach import org.vitrivr.engine.core.context.IndexContext import org.vitrivr.engine.core.model.content.element.ImageContent import org.vitrivr.engine.core.model.retrievable.Retrievable +import org.vitrivr.engine.core.model.retrievable.attributes.ContentAuthorAttribute import org.vitrivr.engine.core.operators.Operator import org.vitrivr.engine.core.operators.general.Exporter import org.vitrivr.engine.core.operators.general.ExporterFactory @@ -46,20 +47,24 @@ class ThumbnailExporter : ExporterFactory { } } ?: MimeType.JPG logger.debug { "Creating new ThumbnailExporter with maxSideResolution=$maxSideResolution and mimeType=$mimeType." } - return Instance(input, context, maxSideResolution, mimeType) + return Instance(input, context, maxSideResolution, mimeType, context[name, "contentSources"]?.split(",")?.toSet() ?: emptySet()) } /** * The [Exporter] generated by this [ThumbnailExporter]. */ - private class Instance(override val input: Operator, private val context: IndexContext, private val maxResolution: Int, private val mimeType: MimeType) : Exporter { + private class Instance(override val input: Operator, private val context: IndexContext, private val maxResolution: Int, private val mimeType: MimeType, private val contentSources:Set) : Exporter { init { require(mimeType in SUPPORTED) { "ThumbnailExporter only support image formats JPEG and PNG." } } + override fun toFlow(scope: CoroutineScope): Flow = this.input.toFlow(scope).onEach { retrievable -> val resolvable = this.context.resolver.resolve(retrievable.id) - val content = retrievable.content.filterIsInstance() + + val contentIds = retrievable.filteredAttribute(ContentAuthorAttribute::class.java)?.getContentIds(contentSources) + + val content = retrievable.content.filterIsInstance().filter { contentIds?.contains(it.id) ?: true } if (resolvable != null && content.isNotEmpty()) { val writer = when (mimeType) { MimeType.JPEG, diff --git a/vitrivr-engine-index/src/test/kotlin/org/vitrivr/engine/index/PipelineTest.kt b/vitrivr-engine-index/src/test/kotlin/org/vitrivr/engine/index/PipelineTest.kt new file mode 100644 index 000000000..a20a71d49 --- /dev/null +++ b/vitrivr-engine-index/src/test/kotlin/org/vitrivr/engine/index/PipelineTest.kt @@ -0,0 +1,100 @@ +package org.vitrivr.engine.index + +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.vitrivr.engine.core.config.IndexContextFactory +import org.vitrivr.engine.core.context.IngestionContextConfig +import org.vitrivr.engine.core.database.blackhole.BlackholeConnection +import org.vitrivr.engine.core.database.blackhole.BlackholeConnectionProvider +import org.vitrivr.engine.core.features.averagecolor.AverageColor +import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor +import org.vitrivr.engine.core.model.metamodel.Schema +import org.vitrivr.engine.core.operators.transform.shape.BroadcastOperator +import org.vitrivr.engine.core.operators.transform.shape.CombineOperator +import org.vitrivr.engine.core.resolver.impl.DiskResolver +import org.vitrivr.engine.core.source.MediaType +import org.vitrivr.engine.index.aggregators.MiddleContentAggregator +import org.vitrivr.engine.index.decode.VideoDecoder +import org.vitrivr.engine.index.enumerate.FileSystemEnumerator +import kotlin.time.Duration + +/** + * Test cases for the extaction pipeline. + */ +class PipelineTest { + + /** + * A small test case that tests the functionality of the pipeline and the ability of operators to filter content by content source. + * + * Idea of the test is as follows: + * - We construct a manual pipeline that extracts a 3s example video showing a red, green and blue screen for 1s each. + * - We extract the [AverageColor] value for the content (once for each frame, and once aggregated). + * - We check that the extracted values are correct. + */ + @Test + fun testContentSources() = runTest(timeout = Duration.INFINITE) { + val schema = Schema("test", BlackholeConnection("test", BlackholeConnectionProvider())) + + schema.addResolver("test", DiskResolver().newResolver(schema, mapOf())) + + + val contextConfig = IngestionContextConfig( + "CachedContentFactory", "test", global = emptyMap(), + local = mapOf( + "enumerator" to mapOf("path" to "./src/test/resources/"), + "decoder" to mapOf("timeWindowMs" to "1000"), + "averageColorAgg" to mapOf("contentSources" to "middleAgg"), + ) + ) + + contextConfig.schema = schema + + val context = IndexContextFactory.newContext(contextConfig) + + val fileSystemEnumerator = FileSystemEnumerator().newEnumerator("enumerator", context, listOf(MediaType.VIDEO)) + + val decoder = BroadcastOperator(VideoDecoder().newDecoder("decoder", input = fileSystemEnumerator, context = context)) + + val middleAgg = MiddleContentAggregator().newTransformer("middleAgg", input = decoder, context = context) + + val averageColor = AverageColor() + + val averageColorAll = averageColor.newExtractor(schema.Field("averageColorAll", averageColor), input = decoder, context = context) + + val averageColorAgg = averageColor.newExtractor(schema.Field("averageColorAgg", averageColor), input = middleAgg, context = context) + + val mergeOp = CombineOperator(listOf(averageColorAll, averageColorAgg)) + + /* We expect three results (6s video, 1000ms window). */ + val out = mergeOp.toFlow(this).take(3).toList() + + /* Now extract all content elements and descriptors for each branch. */ + val aggDescriptors = out.flatMap { it.descriptors }.filterIsInstance().filter { it.field?.fieldName == "averageColorAgg" } + val allDescriptors = out.flatMap { it.descriptors }.filterIsInstance().filter { it.field?.fieldName == "averageColorAll" } + + /* The number of total descriptors should be equal to the number of content elements (72 frames for 3s). */ + Assertions.assertEquals(72, allDescriptors.size) + + /* The first 24 frames should be R, followed by G and B. */ + for (i in allDescriptors.indices) { + if (i < 24) { + Assertions.assertArrayEquals(floatArrayOf(1.0f, 0.0f, 0.0f, 1.0f), allDescriptors[i].vector.value, 0.05f) + } else if (i < 48) { + Assertions.assertArrayEquals(floatArrayOf(0.0f, 1.0f, 0.0f, 1.0f), allDescriptors[i].vector.value, 0.05f) + } else { + Assertions.assertArrayEquals(floatArrayOf(0.0f, 0.0f, 1.0f, 1.0f), allDescriptors[i].vector.value, 0.05f) + } + } + + /* The number of aggregated descriptors should be equal to the number of content elements. */ + Assertions.assertEquals(3, aggDescriptors.size) + + /* The descriptors should find a full R value followed by G and B. */ + Assertions.assertArrayEquals(floatArrayOf(1.0f, 0.0f, 0.0f, 1.0f), aggDescriptors[0].vector.value, 0.05f) + Assertions.assertArrayEquals(floatArrayOf(0.0f, 1.0f, 0.0f, 1.0f), aggDescriptors[1].vector.value, 0.05f) + Assertions.assertArrayEquals(floatArrayOf(0.0f, 0.0f, 1.0f, 1.0f), aggDescriptors[2].vector.value, 0.05f) + } +} \ No newline at end of file diff --git a/vitrivr-engine-index/src/test/resources/color_test.mp4 b/vitrivr-engine-index/src/test/resources/color_test.mp4 new file mode 100644 index 000000000..8e9f84700 Binary files /dev/null and b/vitrivr-engine-index/src/test/resources/color_test.mp4 differ diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLD.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLD.kt index da9d6ca86..f493dbc8e 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLD.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLD.kt @@ -11,6 +11,7 @@ import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.content.element.ImageContent import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge 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 @@ -69,7 +70,7 @@ class CLD : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(name: String, input: Operator, context: IndexContext) = CLDExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext) = CLDExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [CLDExtractor] instance for this [CLD]. @@ -81,7 +82,7 @@ class CLD : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = CLDExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = CLDExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [DenseRetriever] instance for this [CLD]. diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLDExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLDExtractor.kt index 68e128570..0f6ec0d76 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLDExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/cld/CLDExtractor.kt @@ -20,7 +20,7 @@ import org.vitrivr.engine.core.source.file.FileSource * @author Ralph Gasser * @version 1.0.0 */ -class CLDExtractor(input: Operator, analyser: CLD, field: Schema.Field?) : AbstractExtractor(input, analyser, field) { +class CLDExtractor(input: Operator, analyser: CLD, field: Schema.Field?, parameters: Map) : AbstractExtractor(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -38,7 +38,7 @@ class CLDExtractor(input: Operator, analyser: CLD, field: Schema.Fi * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { (this.analyser as CLD).analyse(it).copy(retrievableId = retrievable.id, field = this.field) } } } \ No newline at end of file diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColor.kt index f102b92ff..e61c3d5dd 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColor.kt @@ -7,6 +7,7 @@ import org.vitrivr.engine.core.model.content.element.ImageContent import org.vitrivr.engine.core.model.descriptor.struct.LabelDescriptor import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.bool.BooleanQuery @@ -89,13 +90,13 @@ class DominantColor : Analyser { field: Schema.Field, input: Operator, context: IndexContext - ): Extractor = DominantColorExtractor(input, this, field) + ): Extractor = DominantColorExtractor(input, this, field, merge(field, context)) override fun newExtractor( name: String, input: Operator, context: IndexContext - ): Extractor = DominantColorExtractor(input, this, null) //TODO name + ): Extractor = DominantColorExtractor(input, this, null, context.local[name]?: emptyMap()) //TODO name /** * Performs the [DominantColor] analysis on the provided [List] of [ImageContent] elements. diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColorExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColorExtractor.kt index 69743a151..e080f285a 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColorExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/dominantcolor/DominantColorExtractor.kt @@ -11,7 +11,7 @@ import org.vitrivr.engine.core.operators.Operator import org.vitrivr.engine.core.operators.ingest.Extractor import org.vitrivr.engine.core.source.file.FileSource -class DominantColorExtractor(input: Operator, analyser: DominantColor, field: Schema.Field?) : AbstractExtractor(input, analyser, field) { +class DominantColorExtractor(input: Operator, analyser: DominantColor, field: Schema.Field?, parameters:Map) : AbstractExtractor(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. @@ -26,7 +26,7 @@ class DominantColorExtractor(input: Operator, analyser: DominantCol override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return (this.analyser as DominantColor).analyse(content).map { it.copy(retrievableId = retrievable.id, field = this@DominantColorExtractor.field) } } diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHD.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHD.kt index 533770e77..a4da35af8 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHD.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHD.kt @@ -11,6 +11,7 @@ import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.content.element.ImageContent import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge 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 @@ -89,7 +90,7 @@ class EHD : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(name: String, input: Operator, context: IndexContext) = EHDExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext) = EHDExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [EHDExtractor] instance for this [EHD]. @@ -101,7 +102,7 @@ class EHD : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = EHDExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = EHDExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [DenseRetriever] instance for this [EHD]. diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHDExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHDExtractor.kt index 3de500929..8e3d8279e 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHDExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/ehd/EHDExtractor.kt @@ -20,7 +20,7 @@ import org.vitrivr.engine.core.source.file.FileSource * @author Ralph Gasser * @version 1.0.0 */ -class EHDExtractor(input: Operator, analyser: EHD, field: Schema.Field?) : AbstractExtractor(input, analyser, field) { +class EHDExtractor(input: Operator, analyser: EHD, field: Schema.Field?, parameters: Map) : AbstractExtractor(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -38,7 +38,7 @@ class EHDExtractor(input: Operator, analyser: EHD, field: Schema.Fi * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { (this.analyser as EHD).analyse(it).copy(retrievableId = retrievable.id, field = this.field) } } } \ No newline at end of file diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIP.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIP.kt index ab4f3dcd4..82f2a2f9f 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIP.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIP.kt @@ -9,6 +9,7 @@ import org.vitrivr.engine.core.model.content.element.ImageContent import org.vitrivr.engine.core.model.content.element.TextContent import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.basics.Distance @@ -78,7 +79,7 @@ class CLIP : ExternalAnalyser, FloatVectorDescriptor>() { */ override fun newExtractor(field: Schema.Field, FloatVectorDescriptor>, input: Operator, context: IndexContext): CLIPExtractor { val host: String = field.parameters[HOST_PARAMETER_NAME] ?: HOST_PARAMETER_DEFAULT - return CLIPExtractor(input, this, field, host) + return CLIPExtractor(input, this, field, host, merge(field, context)) } /** @@ -93,7 +94,7 @@ class CLIP : ExternalAnalyser, FloatVectorDescriptor>() { */ override fun newExtractor(name: String, input: Operator, context: IndexContext): CLIPExtractor { val host: String = context.getProperty(name, HOST_PARAMETER_NAME) ?: HOST_PARAMETER_DEFAULT - return CLIPExtractor(input, this, null, host) + return CLIPExtractor(input, this, null, host, context.local[name] ?: emptyMap()) } /** diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIPExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIPExtractor.kt index 1c64a19d7..4cf4edcf8 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIPExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/clip/CLIPExtractor.kt @@ -20,7 +20,7 @@ import org.vitrivr.engine.core.operators.ingest.Extractor * @author Rahel Arnold * @version 1.3.0 */ -class CLIPExtractor(input: Operator, analyser: CLIP, field: Schema.Field, FloatVectorDescriptor>?, private val host: String) : AbstractExtractor, FloatVectorDescriptor>(input, analyser, field) { +class CLIPExtractor(input: Operator, analyser: CLIP, field: Schema.Field, FloatVectorDescriptor>?, private val host: String, parameters:Map) : AbstractExtractor, FloatVectorDescriptor>(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -36,7 +36,7 @@ class CLIPExtractor(input: Operator, analyser: CLIP, field: Schema. * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { c -> CLIP.analyse(c, this.host).copy(retrievableId = retrievable.id, field = this@CLIPExtractor.field) } diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINO.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINO.kt index e23f1f19b..46e467670 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINO.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINO.kt @@ -67,7 +67,7 @@ class DINO : ExternalAnalyser() { */ override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext): DINOExtractor { val host: String = field.parameters[HOST_PARAMETER_NAME] ?: HOST_PARAMETER_DEFAULT - return DINOExtractor(input, this, field, host) + return DINOExtractor(input, this, field, host, Analyser.merge(field, context)) } /** @@ -80,7 +80,7 @@ class DINO : ExternalAnalyser() { */ override fun newExtractor(name: String, input: Operator, context: IndexContext): DINOExtractor { val host: String = context.getProperty(name, HOST_PARAMETER_NAME) ?: HOST_PARAMETER_DEFAULT - return DINOExtractor(input, this, null, host) + return DINOExtractor(input, this, null, host, context.local[name] ?: emptyMap()) } /** diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINOExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINOExtractor.kt index 333ff855b..408e48ca2 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINOExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/external/implementations/dino/DINOExtractor.kt @@ -19,7 +19,7 @@ import org.vitrivr.engine.core.operators.ingest.Extractor * @author Rahel Arnold * @version 1.2.0 */ -class DINOExtractor(input: Operator, analyser: DINO, field: Schema.Field?, private val host: String) : AbstractExtractor(input, analyser, field) { +class DINOExtractor(input: Operator, analyser: DINO, field: Schema.Field?, private val host: String, parameters:Map) : AbstractExtractor(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -36,7 +36,7 @@ class DINOExtractor(input: Operator, analyser: DINO, field: Schema. * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { c -> DINO.analyse(c, this.host).copy(retrievableId = retrievable.id, field = this@DINOExtractor.field) } diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogram.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogram.kt index 27c8ba374..ed861bb8d 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogram.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogram.kt @@ -11,6 +11,7 @@ import org.vitrivr.engine.core.model.content.element.ContentElement import org.vitrivr.engine.core.model.content.element.ImageContent import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge 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 @@ -58,7 +59,7 @@ class HueHistogram : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(name: String, input: Operator, context: IndexContext) = HueHistogramExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext) = HueHistogramExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [HueHistogramExtractor] instance for this [HueHistogram]. @@ -70,7 +71,7 @@ class HueHistogram : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = HueHistogramExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = HueHistogramExtractor(input, this, field, merge(field, context)) /** * Generates and returns a new [DenseRetriever] instance for this [HueHistogram]. diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogramExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogramExtractor.kt index f16974cb5..68db85144 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogramExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/huehistogram/HueHistogramExtractor.kt @@ -21,7 +21,7 @@ import org.vitrivr.engine.module.features.feature.ehd.EHD * @author Ralph Gasser * @version 1.0.0 */ -class HueHistogramExtractor(input: Operator, analyser: HueHistogram, field: Schema.Field?) : AbstractExtractor(input, analyser, field) { +class HueHistogramExtractor(input: Operator, analyser: HueHistogram, field: Schema.Field?, parameters:Map) : AbstractExtractor(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -39,7 +39,7 @@ class HueHistogramExtractor(input: Operator, analyser: HueHistogram * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { (this.analyser as HueHistogram).analyse(it).copy(retrievableId = retrievable.id, field = this.field) } } } \ No newline at end of file diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColor.kt index b95507a00..ca59c0d75 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColor.kt @@ -1,5 +1,6 @@ package org.vitrivr.engine.module.features.feature.mediancolor +import kotlinx.coroutines.flow.merge import org.vitrivr.engine.core.context.IndexContext import org.vitrivr.engine.core.context.QueryContext import org.vitrivr.engine.core.features.dense.DenseRetriever @@ -57,7 +58,7 @@ class MedianColor : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = MedianColorExtractor(input, this, field) + override fun newExtractor(field: Schema.Field, input: Operator, context: IndexContext) = MedianColorExtractor(input, this, field, Analyser.merge(field, context)) /** * Generates and returns a new [MedianColorExtractor] instance for this [MedianColor]. @@ -69,7 +70,7 @@ class MedianColor : Analyser { * @return A new [Extractor] instance for this [Analyser] * @throws [UnsupportedOperationException], if this [Analyser] does not support the creation of an [Extractor] instance. */ - override fun newExtractor(name: String, input: Operator, context: IndexContext): Extractor = MedianColorExtractor(input, this, null) + override fun newExtractor(name: String, input: Operator, context: IndexContext): Extractor = MedianColorExtractor(input, this, null, context.local[name] ?: emptyMap()) /** * Generates and returns a new [DenseRetriever] instance for this [MedianColor]. diff --git a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColorExtractor.kt b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColorExtractor.kt index 1773db968..64e9480f0 100644 --- a/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColorExtractor.kt +++ b/vitrivr-engine-module-features/src/main/kotlin/org/vitrivr/engine/module/features/feature/mediancolor/MedianColorExtractor.kt @@ -21,7 +21,7 @@ import org.vitrivr.engine.core.source.file.FileSource * @author Ralph Gasser * @version 1.0.0 */ -class MedianColorExtractor(input: Operator, analyser: MedianColor, field: Schema.Field?) : AbstractExtractor(input, analyser, field) { +class MedianColorExtractor(input: Operator, analyser: MedianColor, field: Schema.Field?, parameters:Map) : AbstractExtractor(input, analyser, field, parameters) { /** * Internal method to check, if [Retrievable] matches this [Extractor] and should thus be processed. * @@ -39,7 +39,7 @@ class MedianColorExtractor(input: Operator, analyser: MedianColor, * @return List of resulting [Descriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.map { (this.analyser as MedianColor).analyse(it).copy(retrievableId = retrievable.id, field = this.field) } } } \ No newline at end of file diff --git a/vitrivr-engine-module-fes/src/main/kotlin/org/vitrivr/engine/base/features/external/common/FesExtractor.kt b/vitrivr-engine-module-fes/src/main/kotlin/org/vitrivr/engine/base/features/external/common/FesExtractor.kt index 879e80fb6..4349f0fc3 100644 --- a/vitrivr-engine-module-fes/src/main/kotlin/org/vitrivr/engine/base/features/external/common/FesExtractor.kt +++ b/vitrivr-engine-module-fes/src/main/kotlin/org/vitrivr/engine/base/features/external/common/FesExtractor.kt @@ -29,10 +29,10 @@ abstract class FesExtractor, D : Descriptor<*>>( input: Operator, field: Schema.Field?, analyser: ExternalFesAnalyser, - protected val parameters: Map, -) : AbstractBatchedExtractor(input, analyser, field, parameters["batchSize"]?.toIntOrNull() ?: 1) { + parameters: Map +) : AbstractBatchedExtractor(input, analyser, field, parameters) { + - private val contentSources = parameters[CONTENT_AUTHORS_KEY]?.split(",")?.toSet() protected val host: String get() = this.parameters[HOST_PARAMETER_NAME] ?: HOST_PARAMETER_DEFAULT @@ -66,12 +66,5 @@ abstract class FesExtractor, D : Descriptor<*>>( } } - fun filterContent(retrievable: Retrievable): List { - val contentIds = this.contentSources?.let { - retrievable.filteredAttribute(ContentAuthorAttribute::class.java)?.getContentIds(it) - } - return retrievable.content.filter { content -> - this.analyser.contentClasses.any { it.isInstance(content) && (contentIds?.contains(content.id) ?: false) } - }.map { it as C } - } + } \ No newline at end of file diff --git a/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonics.kt b/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonics.kt index e10d9b2ea..a18d88d5b 100644 --- a/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonics.kt +++ b/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonics.kt @@ -11,6 +11,7 @@ import org.vitrivr.engine.core.model.content.element.Model3dContent import org.vitrivr.engine.core.model.descriptor.vector.FloatVectorDescriptor import org.vitrivr.engine.core.model.mesh.texturemodel.Mesh import org.vitrivr.engine.core.model.metamodel.Analyser +import org.vitrivr.engine.core.model.metamodel.Analyser.Companion.merge import org.vitrivr.engine.core.model.metamodel.Schema import org.vitrivr.engine.core.model.query.Query import org.vitrivr.engine.core.model.query.basics.Distance @@ -212,7 +213,7 @@ class SphericalHarmonics : Analyser { val minL = field.parameters[MINL_PARAMETER_NAME]?.toIntOrNull() ?: context.getProperty("", "")?.toIntOrNull() ?: MINL_PARAMETER_DEFAULT val maxL = field.parameters[MAXL_PARAMETER_NAME]?.toIntOrNull() ?: context.getProperty("", "")?.toIntOrNull() ?: MAXL_PARAMETER_DEFAULT logger.debug { "Creating new SphericalHarmonicsExtract for field '${field.fieldName}' with parameters ($gridSize, $cap, $minL, $maxL)." } - return SphericalHarmonicsExtractor(input, this, field, gridSize, cap, minL, maxL) + return SphericalHarmonicsExtractor(input, this, field, gridSize, cap, minL, maxL, merge(field, context)) } /** @@ -229,6 +230,6 @@ class SphericalHarmonics : Analyser { val minL = context.getProperty(name, MINL_PARAMETER_NAME)?.toIntOrNull() ?: MINL_PARAMETER_DEFAULT val maxL = context.getProperty(name, MAXL_PARAMETER_NAME)?.toIntOrNull() ?: MAXL_PARAMETER_DEFAULT logger.debug { "Creating new SphericalHarmonicsExtract with parameters ($gridSize, $cap, $minL, $maxL)." } - return SphericalHarmonicsExtractor(input, this, null, gridSize, cap, minL, maxL) + return SphericalHarmonicsExtractor(input, this, null, gridSize, cap, minL, maxL, context.local[name] ?: emptyMap()) } } diff --git a/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonicsExtractor.kt b/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonicsExtractor.kt index 34b486476..35f56f8d9 100644 --- a/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonicsExtractor.kt +++ b/vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/features/sphericalharmonics/SphericalHarmonicsExtractor.kt @@ -27,9 +27,9 @@ class SphericalHarmonicsExtractor( private val gridSize: Int, private val cap: Int, private val minL: Int, - private val maxL: Int -) : - AbstractExtractor(input, analyser, field) { + private val maxL: Int, + parameters : Map +) : AbstractExtractor(input, analyser, field, parameters) { init { require(this.minL < this.maxL) { "Parameter mismatch: min_l must be smaller than max_l. "} @@ -52,7 +52,7 @@ class SphericalHarmonicsExtractor( * @return List of resulting [FloatVectorDescriptor]s. */ override fun extract(retrievable: Retrievable): List { - val content = retrievable.content.filterIsInstance() + val content = this.filterContent(retrievable) return content.flatMap { c -> c.content.getMaterials().flatMap { mat -> mat.materialMeshes.map { mesh -> SphericalHarmonics.analyse(mesh, this.gridSize, this.minL, this.maxL, this.cap).copy(field = this.field) } } } } } \ No newline at end of file