1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-09 23:17:29 +02:00

feat(server): separate face clustering job (#5598)

* separate facial clustering job

* update api

* fixed some tests

* invert clustering

* hdbscan

* update api

* remove commented code

* wip dbscan

* cleanup

removed cluster endpoint

remove commented code

* fixes

updated tests

minor fixes and formatting

fixed queuing

refinements

* scale search range based on library size

* defer non-core faces

* optimizations

removed unused query option

* assign faces individually for correctness

fixed unit tests

remove unused method

* don't select face embedding

update sql

linting

fixed ml typing

* updated job mock

* paginate people query

* select face embeddings because typeorm

* fix setting face detection concurrency

* update sql

formatting

linting

* simplify logic

remove unused imports

* more specific delete signature

* more accurate typing for face stubs

* add migration

formatting

* chore: better typing

* don't select embedding by default

remove unused import

* updated sql

* use normal try/catch

* stricter concurrency typing and enforcement

* update api

* update job concurrency panel to show disabled queues

formatting

* check jobId in queueAll

fix tests

* remove outdated comment

* better facial recognition icon

* wording

wording

formatting

* fixed tests

* fix

* formatting & sql

* try to fix sql check

* more detailed description

* update sql

* formatting

* wording

* update `minFaces` description

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Mert
2024-01-18 00:08:48 -05:00
committed by GitHub
parent 44873b4224
commit 68f52818ae
57 changed files with 1081 additions and 631 deletions

View File

@@ -9,10 +9,11 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**backgroundTask** | [**JobStatusDto**](JobStatusDto.md) | |
**faceDetection** | [**JobStatusDto**](JobStatusDto.md) | |
**facialRecognition** | [**JobStatusDto**](JobStatusDto.md) | |
**library_** | [**JobStatusDto**](JobStatusDto.md) | |
**metadataExtraction** | [**JobStatusDto**](JobStatusDto.md) | |
**migration** | [**JobStatusDto**](JobStatusDto.md) | |
**recognizeFaces** | [**JobStatusDto**](JobStatusDto.md) | |
**search** | [**JobStatusDto**](JobStatusDto.md) | |
**sidecar** | [**JobStatusDto**](JobStatusDto.md) | |
**smartSearch** | [**JobStatusDto**](JobStatusDto.md) | |

View File

@@ -9,9 +9,9 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**enabled** | **bool** | |
**maxDistance** | **int** | |
**maxDistance** | **double** | |
**minFaces** | **int** | |
**minScore** | **int** | |
**minScore** | **double** | |
**modelName** | **String** | |
**modelType** | [**ModelType**](ModelType.md) | | [optional]

View File

@@ -9,10 +9,10 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**backgroundTask** | [**JobSettingsDto**](JobSettingsDto.md) | |
**faceDetection** | [**JobSettingsDto**](JobSettingsDto.md) | |
**library_** | [**JobSettingsDto**](JobSettingsDto.md) | |
**metadataExtraction** | [**JobSettingsDto**](JobSettingsDto.md) | |
**migration** | [**JobSettingsDto**](JobSettingsDto.md) | |
**recognizeFaces** | [**JobSettingsDto**](JobSettingsDto.md) | |
**search** | [**JobSettingsDto**](JobSettingsDto.md) | |
**sidecar** | [**JobSettingsDto**](JobSettingsDto.md) | |
**smartSearch** | [**JobSettingsDto**](JobSettingsDto.md) | |

View File

@@ -14,10 +14,11 @@ class AllJobStatusResponseDto {
/// Returns a new [AllJobStatusResponseDto] instance.
AllJobStatusResponseDto({
required this.backgroundTask,
required this.faceDetection,
required this.facialRecognition,
required this.library_,
required this.metadataExtraction,
required this.migration,
required this.recognizeFaces,
required this.search,
required this.sidecar,
required this.smartSearch,
@@ -28,14 +29,16 @@ class AllJobStatusResponseDto {
JobStatusDto backgroundTask;
JobStatusDto faceDetection;
JobStatusDto facialRecognition;
JobStatusDto library_;
JobStatusDto metadataExtraction;
JobStatusDto migration;
JobStatusDto recognizeFaces;
JobStatusDto search;
JobStatusDto sidecar;
@@ -51,10 +54,11 @@ class AllJobStatusResponseDto {
@override
bool operator ==(Object other) => identical(this, other) || other is AllJobStatusResponseDto &&
other.backgroundTask == backgroundTask &&
other.faceDetection == faceDetection &&
other.facialRecognition == facialRecognition &&
other.library_ == library_ &&
other.metadataExtraction == metadataExtraction &&
other.migration == migration &&
other.recognizeFaces == recognizeFaces &&
other.search == search &&
other.sidecar == sidecar &&
other.smartSearch == smartSearch &&
@@ -66,10 +70,11 @@ class AllJobStatusResponseDto {
int get hashCode =>
// ignore: unnecessary_parenthesis
(backgroundTask.hashCode) +
(faceDetection.hashCode) +
(facialRecognition.hashCode) +
(library_.hashCode) +
(metadataExtraction.hashCode) +
(migration.hashCode) +
(recognizeFaces.hashCode) +
(search.hashCode) +
(sidecar.hashCode) +
(smartSearch.hashCode) +
@@ -78,15 +83,16 @@ class AllJobStatusResponseDto {
(videoConversion.hashCode);
@override
String toString() => 'AllJobStatusResponseDto[backgroundTask=$backgroundTask, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, recognizeFaces=$recognizeFaces, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
String toString() => 'AllJobStatusResponseDto[backgroundTask=$backgroundTask, faceDetection=$faceDetection, facialRecognition=$facialRecognition, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'backgroundTask'] = this.backgroundTask;
json[r'faceDetection'] = this.faceDetection;
json[r'facialRecognition'] = this.facialRecognition;
json[r'library'] = this.library_;
json[r'metadataExtraction'] = this.metadataExtraction;
json[r'migration'] = this.migration;
json[r'recognizeFaces'] = this.recognizeFaces;
json[r'search'] = this.search;
json[r'sidecar'] = this.sidecar;
json[r'smartSearch'] = this.smartSearch;
@@ -105,10 +111,11 @@ class AllJobStatusResponseDto {
return AllJobStatusResponseDto(
backgroundTask: JobStatusDto.fromJson(json[r'backgroundTask'])!,
faceDetection: JobStatusDto.fromJson(json[r'faceDetection'])!,
facialRecognition: JobStatusDto.fromJson(json[r'facialRecognition'])!,
library_: JobStatusDto.fromJson(json[r'library'])!,
metadataExtraction: JobStatusDto.fromJson(json[r'metadataExtraction'])!,
migration: JobStatusDto.fromJson(json[r'migration'])!,
recognizeFaces: JobStatusDto.fromJson(json[r'recognizeFaces'])!,
search: JobStatusDto.fromJson(json[r'search'])!,
sidecar: JobStatusDto.fromJson(json[r'sidecar'])!,
smartSearch: JobStatusDto.fromJson(json[r'smartSearch'])!,
@@ -163,10 +170,11 @@ class AllJobStatusResponseDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'backgroundTask',
'faceDetection',
'facialRecognition',
'library',
'metadataExtraction',
'migration',
'recognizeFaces',
'search',
'sidecar',
'smartSearch',

View File

@@ -26,7 +26,8 @@ class JobName {
static const thumbnailGeneration = JobName._(r'thumbnailGeneration');
static const metadataExtraction = JobName._(r'metadataExtraction');
static const videoConversion = JobName._(r'videoConversion');
static const recognizeFaces = JobName._(r'recognizeFaces');
static const faceDetection = JobName._(r'faceDetection');
static const facialRecognition = JobName._(r'facialRecognition');
static const smartSearch = JobName._(r'smartSearch');
static const backgroundTask = JobName._(r'backgroundTask');
static const storageTemplateMigration = JobName._(r'storageTemplateMigration');
@@ -40,7 +41,8 @@ class JobName {
thumbnailGeneration,
metadataExtraction,
videoConversion,
recognizeFaces,
faceDetection,
facialRecognition,
smartSearch,
backgroundTask,
storageTemplateMigration,
@@ -89,7 +91,8 @@ class JobNameTypeTransformer {
case r'thumbnailGeneration': return JobName.thumbnailGeneration;
case r'metadataExtraction': return JobName.metadataExtraction;
case r'videoConversion': return JobName.videoConversion;
case r'recognizeFaces': return JobName.recognizeFaces;
case r'faceDetection': return JobName.faceDetection;
case r'facialRecognition': return JobName.facialRecognition;
case r'smartSearch': return JobName.smartSearch;
case r'backgroundTask': return JobName.backgroundTask;
case r'storageTemplateMigration': return JobName.storageTemplateMigration;

View File

@@ -23,11 +23,11 @@ class RecognitionConfig {
bool enabled;
int maxDistance;
double maxDistance;
int minFaces;
int minScore;
double minScore;
String modelName;
@@ -85,9 +85,9 @@ class RecognitionConfig {
return RecognitionConfig(
enabled: mapValueOfType<bool>(json, r'enabled')!,
maxDistance: mapValueOfType<int>(json, r'maxDistance')!,
maxDistance: mapValueOfType<double>(json, r'maxDistance')!,
minFaces: mapValueOfType<int>(json, r'minFaces')!,
minScore: mapValueOfType<int>(json, r'minScore')!,
minScore: mapValueOfType<double>(json, r'minScore')!,
modelName: mapValueOfType<String>(json, r'modelName')!,
modelType: ModelType.fromJson(json[r'modelType']),
);

View File

@@ -14,10 +14,10 @@ class SystemConfigJobDto {
/// Returns a new [SystemConfigJobDto] instance.
SystemConfigJobDto({
required this.backgroundTask,
required this.faceDetection,
required this.library_,
required this.metadataExtraction,
required this.migration,
required this.recognizeFaces,
required this.search,
required this.sidecar,
required this.smartSearch,
@@ -27,14 +27,14 @@ class SystemConfigJobDto {
JobSettingsDto backgroundTask;
JobSettingsDto faceDetection;
JobSettingsDto library_;
JobSettingsDto metadataExtraction;
JobSettingsDto migration;
JobSettingsDto recognizeFaces;
JobSettingsDto search;
JobSettingsDto sidecar;
@@ -48,10 +48,10 @@ class SystemConfigJobDto {
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigJobDto &&
other.backgroundTask == backgroundTask &&
other.faceDetection == faceDetection &&
other.library_ == library_ &&
other.metadataExtraction == metadataExtraction &&
other.migration == migration &&
other.recognizeFaces == recognizeFaces &&
other.search == search &&
other.sidecar == sidecar &&
other.smartSearch == smartSearch &&
@@ -62,10 +62,10 @@ class SystemConfigJobDto {
int get hashCode =>
// ignore: unnecessary_parenthesis
(backgroundTask.hashCode) +
(faceDetection.hashCode) +
(library_.hashCode) +
(metadataExtraction.hashCode) +
(migration.hashCode) +
(recognizeFaces.hashCode) +
(search.hashCode) +
(sidecar.hashCode) +
(smartSearch.hashCode) +
@@ -73,15 +73,15 @@ class SystemConfigJobDto {
(videoConversion.hashCode);
@override
String toString() => 'SystemConfigJobDto[backgroundTask=$backgroundTask, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, recognizeFaces=$recognizeFaces, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
String toString() => 'SystemConfigJobDto[backgroundTask=$backgroundTask, faceDetection=$faceDetection, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'backgroundTask'] = this.backgroundTask;
json[r'faceDetection'] = this.faceDetection;
json[r'library'] = this.library_;
json[r'metadataExtraction'] = this.metadataExtraction;
json[r'migration'] = this.migration;
json[r'recognizeFaces'] = this.recognizeFaces;
json[r'search'] = this.search;
json[r'sidecar'] = this.sidecar;
json[r'smartSearch'] = this.smartSearch;
@@ -99,10 +99,10 @@ class SystemConfigJobDto {
return SystemConfigJobDto(
backgroundTask: JobSettingsDto.fromJson(json[r'backgroundTask'])!,
faceDetection: JobSettingsDto.fromJson(json[r'faceDetection'])!,
library_: JobSettingsDto.fromJson(json[r'library'])!,
metadataExtraction: JobSettingsDto.fromJson(json[r'metadataExtraction'])!,
migration: JobSettingsDto.fromJson(json[r'migration'])!,
recognizeFaces: JobSettingsDto.fromJson(json[r'recognizeFaces'])!,
search: JobSettingsDto.fromJson(json[r'search'])!,
sidecar: JobSettingsDto.fromJson(json[r'sidecar'])!,
smartSearch: JobSettingsDto.fromJson(json[r'smartSearch'])!,
@@ -156,10 +156,10 @@ class SystemConfigJobDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'backgroundTask',
'faceDetection',
'library',
'metadataExtraction',
'migration',
'recognizeFaces',
'search',
'sidecar',
'smartSearch',

View File

@@ -21,6 +21,16 @@ void main() {
// TODO
});
// JobStatusDto faceDetection
test('to test the property `faceDetection`', () async {
// TODO
});
// JobStatusDto facialRecognition
test('to test the property `facialRecognition`', () async {
// TODO
});
// JobStatusDto library_
test('to test the property `library_`', () async {
// TODO
@@ -36,11 +46,6 @@ void main() {
// TODO
});
// JobStatusDto recognizeFaces
test('to test the property `recognizeFaces`', () async {
// TODO
});
// JobStatusDto search
test('to test the property `search`', () async {
// TODO

View File

@@ -21,7 +21,7 @@ void main() {
// TODO
});
// int maxDistance
// double maxDistance
test('to test the property `maxDistance`', () async {
// TODO
});
@@ -31,7 +31,7 @@ void main() {
// TODO
});
// int minScore
// double minScore
test('to test the property `minScore`', () async {
// TODO
});

View File

@@ -21,6 +21,11 @@ void main() {
// TODO
});
// JobSettingsDto faceDetection
test('to test the property `faceDetection`', () async {
// TODO
});
// JobSettingsDto library_
test('to test the property `library_`', () async {
// TODO
@@ -36,11 +41,6 @@ void main() {
// TODO
});
// JobSettingsDto recognizeFaces
test('to test the property `recognizeFaces`', () async {
// TODO
});
// JobSettingsDto search
test('to test the property `search`', () async {
// TODO