From 02a07be4ca601314ffbc05c18d504bbc8870b245 Mon Sep 17 00:00:00 2001 From: Savvas Dalkitsis Date: Wed, 27 Nov 2024 19:14:34 +0000 Subject: [PATCH 1/2] Adding support for blurhash --- api/directory_watcher.py | 7 +++++++ api/migrations/0076_add_blurhash.py | 14 ++++++++++++++ api/models/photo.py | 13 +++++++++++++ api/serializers/photos.py | 8 ++++++++ api/views/albums.py | 1 + api/views/search.py | 2 ++ requirements.txt | 1 + 7 files changed, 46 insertions(+) create mode 100644 api/migrations/0076_add_blurhash.py diff --git a/api/directory_watcher.py b/api/directory_watcher.py index 2123355307..ac4321f392 100644 --- a/api/directory_watcher.py +++ b/api/directory_watcher.py @@ -195,6 +195,13 @@ def handle_new_image(user, path, job_id, photo=None): job_id, path, elapsed ) ) + photo._get_blurhash() + elapsed = (datetime.datetime.now() - start).total_seconds() + util.logger.info( + "job {}: get blurhash: {}, elapsed: {}".format( + job_id, path, elapsed + ) + ) photo._recreate_search_captions() elapsed = (datetime.datetime.now() - start).total_seconds() util.logger.info( diff --git a/api/migrations/0076_add_blurhash.py b/api/migrations/0076_add_blurhash.py new file mode 100644 index 0000000000..d41a6ee167 --- /dev/null +++ b/api/migrations/0076_add_blurhash.py @@ -0,0 +1,14 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api", "0075_alter_face_cluster_person"), + ] + operations = [ + migrations.AddField( + model_name="Photo", + name="blurhash", + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/api/models/photo.py b/api/models/photo.py index bad3146977..86bc571c71 100644 --- a/api/models/photo.py +++ b/api/models/photo.py @@ -7,6 +7,7 @@ import numpy as np import PIL import requests +import blurhash from django.contrib.postgres.fields import ArrayField from django.core.files.base import ContentFile from django.db import models @@ -73,6 +74,7 @@ class Photo(models.Model): captions_json = models.JSONField(blank=True, null=True, db_index=True) dominant_color = models.TextField(blank=True, null=True) + blurhash = models.TextField(blank=True, null=True) search_captions = models.TextField(blank=True, null=True, db_index=True) search_location = models.TextField(blank=True, null=True, db_index=True) @@ -836,6 +838,17 @@ def _get_dominant_color(self, palette_size=16): except Exception: logger.info("Cannot calculate dominant color {} object".format(self)) + def _get_blurhash(self, palette_size=16): + # Skip if it's already calculated + if self.blurhash: + return + try: + hash = blurhash.encode(self.square_thumbnail_small.path, x_components=4, y_components=4) + self.blurhash = hash + self.save() + except Exception: + logger.info("Cannot calculate blurhash {} object".format(self)) + def manual_delete(self): for file in self.files.all(): if os.path.isfile(file.path): diff --git a/api/serializers/photos.py b/api/serializers/photos.py index e0ef092f76..72f64da649 100644 --- a/api/serializers/photos.py +++ b/api/serializers/photos.py @@ -11,6 +11,7 @@ class PhotoSummarySerializer(serializers.ModelSerializer): id = serializers.SerializerMethodField() dominantColor = serializers.SerializerMethodField() + blurhash = serializers.SerializerMethodField() aspectRatio = serializers.SerializerMethodField() url = serializers.SerializerMethodField() location = serializers.SerializerMethodField() @@ -25,6 +26,7 @@ class Meta: fields = ( "id", "dominantColor", + "blurhash", "url", "location", "date", @@ -84,6 +86,12 @@ def get_dominantColor(self, obj) -> str: else: return "" + def get_blurhash(self, obj) -> str: + if obj.blurhash: + return obj.blurhash + else: + return "" + def get_type(self, obj) -> str: if obj.video: return "video" diff --git a/api/views/albums.py b/api/views/albums.py index 0a36b3d3df..bf498e9234 100644 --- a/api/views/albums.py +++ b/api/views/albums.py @@ -364,6 +364,7 @@ def get_queryset(self): "main_file", "search_location", "dominant_color", + "blurhash", "public", "rating", "hidden", diff --git a/api/views/search.py b/api/views/search.py index 1f961373fb..bce1017508 100644 --- a/api/views/search.py +++ b/api/views/search.py @@ -49,6 +49,7 @@ def list(self, request): "main_file", "search_location", "dominant_color", + "blurhash", "public", "rating", "hidden", @@ -82,6 +83,7 @@ def list(self, request): "main_file", "search_location", "dominant_color", + "blurhash", "public", "rating", "hidden", diff --git a/requirements.txt b/requirements.txt index f05bfd3b2b..8c00d6a0b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -50,3 +50,4 @@ timm==1.0.11 transformers==4.46.2 # Dependencies for mistral quantized llama-cpp-python==0.3.1 +blurhash-python==1.2.2 From b35a7f9192c9d420f7ac94d237b56a89fe9fb55b Mon Sep 17 00:00:00 2001 From: Savvas Dalkitsis Date: Wed, 27 Nov 2024 19:25:52 +0000 Subject: [PATCH 2/2] Fixing lint warnings --- api/directory_watcher.py | 4 +--- api/models/photo.py | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/directory_watcher.py b/api/directory_watcher.py index ac4321f392..aa59093877 100644 --- a/api/directory_watcher.py +++ b/api/directory_watcher.py @@ -198,9 +198,7 @@ def handle_new_image(user, path, job_id, photo=None): photo._get_blurhash() elapsed = (datetime.datetime.now() - start).total_seconds() util.logger.info( - "job {}: get blurhash: {}, elapsed: {}".format( - job_id, path, elapsed - ) + "job {}: get blurhash: {}, elapsed: {}".format(job_id, path, elapsed) ) photo._recreate_search_captions() elapsed = (datetime.datetime.now() - start).total_seconds() diff --git a/api/models/photo.py b/api/models/photo.py index 86bc571c71..e1589f7bca 100644 --- a/api/models/photo.py +++ b/api/models/photo.py @@ -4,10 +4,10 @@ from fractions import Fraction from io import BytesIO +import blurhash import numpy as np import PIL import requests -import blurhash from django.contrib.postgres.fields import ArrayField from django.core.files.base import ContentFile from django.db import models @@ -843,7 +843,9 @@ def _get_blurhash(self, palette_size=16): if self.blurhash: return try: - hash = blurhash.encode(self.square_thumbnail_small.path, x_components=4, y_components=4) + hash = blurhash.encode( + self.square_thumbnail_small.path, x_components=4, y_components=4 + ) self.blurhash = hash self.save() except Exception: @@ -886,7 +888,7 @@ def _set_embedded_media(self, obj): return obj.main_file.embedded_media def __str__(self): - main_file_path = self.main_file.path if self.main_file is not None else "No main file" - return ( - "{} - {} - {}".format(self.image_hash, self.owner, main_file_path) + main_file_path = ( + self.main_file.path if self.main_file is not None else "No main file" ) + return "{} - {} - {}".format(self.image_hash, self.owner, main_file_path)