From d1572fde817f5909ce7c34b86186f2303a0ed27a Mon Sep 17 00:00:00 2001 From: Rayson Date: Mon, 10 Dec 2018 17:45:13 +0800 Subject: [PATCH 1/7] - Added metadata to the images & image set model - Extract metadata and saved during upload --- .../migrations/0018_auto_20181210_0711.py | 23 +++++++++++++++++++ imagetagger/imagetagger/images/models.py | 3 +++ imagetagger/imagetagger/images/views.py | 17 +++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 imagetagger/imagetagger/images/migrations/0018_auto_20181210_0711.py diff --git a/imagetagger/imagetagger/images/migrations/0018_auto_20181210_0711.py b/imagetagger/imagetagger/images/migrations/0018_auto_20181210_0711.py new file mode 100644 index 00000000..07a9ebee --- /dev/null +++ b/imagetagger/imagetagger/images/migrations/0018_auto_20181210_0711.py @@ -0,0 +1,23 @@ +# Generated by Django 2.0.9 on 2018-12-10 06:11 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ('images', '0017_imageset_zip_state'), + ] + + operations = [ + migrations.AddField( + model_name='image', + name='metadata', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict), + ), + migrations.AddField( + model_name='imageset', + name='metadata', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict), + ), + ] diff --git a/imagetagger/imagetagger/images/models.py b/imagetagger/imagetagger/images/models.py index 0f17c340..b96b1af0 100644 --- a/imagetagger/imagetagger/images/models.py +++ b/imagetagger/imagetagger/images/models.py @@ -2,6 +2,7 @@ from django.conf import settings from django.contrib.auth import get_user_model +from django.contrib.postgres.fields import JSONField from django.db import models import os @@ -17,6 +18,7 @@ class Image(models.Model): checksum = models.BinaryField() width = models.IntegerField(default=800) height = models.IntegerField(default=600) + metadata = JSONField(default=dict) def path(self): return os.path.join(self.image_set.root_path(), self.filename) @@ -90,6 +92,7 @@ class ZipState: ) pinned_by = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='pinned_sets') zip_state = models.IntegerField(choices=ZIP_STATES, default=ZipState.INVALID) + metadata = JSONField(default=dict) def root_path(self): return os.path.join(settings.IMAGE_PATH, self.path) diff --git a/imagetagger/imagetagger/images/views.py b/imagetagger/imagetagger/images/views.py index 66697ed5..126aff67 100644 --- a/imagetagger/imagetagger/images/views.py +++ b/imagetagger/imagetagger/images/views.py @@ -19,12 +19,14 @@ from rest_framework.status import HTTP_403_FORBIDDEN, HTTP_200_OK, \ HTTP_201_CREATED, HTTP_202_ACCEPTED, HTTP_204_NO_CONTENT, HTTP_404_NOT_FOUND from PIL import Image as PIL_Image +from PIL.ExifTags import TAGS from imagetagger.images.serializers import ImageSetSerializer, ImageSerializer, SetTagSerializer from imagetagger.images.forms import ImageSetCreationForm, ImageSetCreationFormWT, ImageSetEditForm from imagetagger.users.forms import TeamCreationForm from imagetagger.users.models import User, Team from imagetagger.tagger_messages.forms import TeamMessageCreationForm +import json from .models import ImageSet, Image, SetTag from .forms import LabelUploadForm @@ -173,6 +175,13 @@ def index(request): @login_required @require_http_methods(["POST", ]) def upload_image(request, imageset_id): + def extract_metadata(img): + metadata = dict() + if img._getexif() is not None: + for (tag, value) in img._getexif().items(): + metadata[TAGS.get(tag)] = value + return json.dumps(metadata, default=str) + imageset = get_object_or_404(ImageSet, id=imageset_id) if request.method == 'POST' \ and imageset.has_perm('edit_set', request.user) \ @@ -235,6 +244,8 @@ def upload_image(request, imageset_id): try: with PIL_Image.open(file_path) as image: width, height = image.size + # extracting metadata info + metadata = extract_metadata(image_file) file_new_path = os.path.join(imageset.root_path(), img_fname) shutil.move(file_path, file_new_path) shutil.chown(file_new_path, group=settings.UPLOAD_FS_GROUP) @@ -243,7 +254,8 @@ def upload_image(request, imageset_id): filename=img_fname, checksum=fchecksum, width=width, - height=height + height=height, + metadata=metadata ) new_image.save() except (OSError, IOError): @@ -283,8 +295,11 @@ def upload_image(request, imageset_id): try: with PIL_Image.open(image.path()) as image_file: width, height = image_file.size + # extracting metadata info + metadata = extract_metadata(image_file) image.height = height image.width = width + image.metadata = metadata image.save() except (OSError, IOError): error['damaged'] = True From e6062941b5aa5639a295cdc52ef6aef8c2d0f9cf Mon Sep 17 00:00:00 2001 From: Rayson Date: Mon, 10 Dec 2018 18:44:20 +0800 Subject: [PATCH 2/7] AGRIONE-9 Done initial display of metadata showing in the images --- .../templates/annotations/annotate.html | 599 ++++++++++-------- imagetagger/imagetagger/annotations/views.py | 3 + 2 files changed, 337 insertions(+), 265 deletions(-) diff --git a/imagetagger/imagetagger/annotations/templates/annotations/annotate.html b/imagetagger/imagetagger/annotations/templates/annotations/annotate.html index a7ef7ecf..c3990384 100644 --- a/imagetagger/imagetagger/annotations/templates/annotations/annotate.html +++ b/imagetagger/imagetagger/annotations/templates/annotations/annotate.html @@ -3,299 +3,368 @@ {% load static %} {% block additional_annotation_css %} - + {% endblock additional_annotation_css %} {% block additional_annotation_js %} - - - + + + {% endblock additional_annotation_js %} {% block bodyblock %} -
- - - - - - - - - - - -
-
+
+ + + + + + + + + + + +
+
-
- -
-
- - - - +
+ +
+
+ + + + +
+
+
+ {% for set_image in set_images %} + + {{ set_image.name }} + + {% endfor %} +
+
+
-
-
- {% for set_image in set_images %} - - {{set_image.name}} - - {% endfor %} + -
-
-
- -
-
- Picture {{selected_image.name}} not found!
- -
-
-
-

- Annotations: -

-
- {% for annotation in image_annotations %} -
- {{ annotation.annotation_type.name }}: - -
- {% if annotation.vector is not None %} - {% for key, value in annotation.vector.items %} - {% if forloop.counter0 > 0 %} - • - {% endif %} - {{ key }}: {{ value }} - {% endfor %} - {% else %} - {{annotation.content}} - {% endif %} +
+
+ Picture {{ selected_image.name }} not found!
+ +
- {% endfor %} -
-
{% trans 'No annotations found.' %}
- -
-
-
-
-

{{selected_image.name}}

+
+
+

+ Annotations: +

+
+ {% for annotation in image_annotations %} +
+ {{ annotation.annotation_type.name }}: + +
+ {% if annotation.vector is not None %} + {% for key, value in annotation.vector.items %} + {% if forloop.counter0 > 0 %} + • + {% endif %} + {{ key }}: {{ value }} + {% endfor %} + {% else %} + {{ annotation.content }} + {% endif %} +
+ {% endfor %} +
+
{% trans 'No annotations found.' %}
+ +
+
+ + + + + + + + + {% for data, values in selected_image.metadata.items %} + + + + + {% endfor %} + +
NameDescription
{{ data }}{{ values }}
+
-
-
- {% csrf_token %} - -

- -

-

- - -

- {% if 'annotate' in imageset_perms %} +
+
+
+
+

{{ selected_image.name }}

+
+
+ + {% csrf_token %} +

- - +

-

- - -

-

- - -

-

- - + +

-

The bounding box can be moved and resized with i j k l

-
- {% for field in vector_fields %} -
-
- -
-
- -
-
-
- {% endfor %} -
- {% endif %} -
-
- {% if 'annotate' in imageset_perms %} -
- -
-
- -
-

-
- -
-
- -
-

-
- -
-
- -
- {% else %} -
- -
-
- -
+

+ + +

+
+

+ + +

+

+ + +

+

+ + +

+

+ The bounding box can be moved and resized with i j k l

+
+ {% for field in vector_fields %} +
+
+ +
+
+ +
+
+
+ {% endfor %} +
{% endif %} - {% if 'delete_images' in imageset_perms and not imageset_lock %} -

{{ imageset.image_lock }}

-
- +
+
+ + {% if 'annotate' in imageset_perms %} +
+ +
+
+ +
+

+
+ +
+
+ +
+

+
+ +
+
+ +
+ {% else %} +
+ +
+
+ +
+ {% endif %} + {% if 'delete_images' in imageset_perms and not imageset_lock %} +

{{ imageset.image_lock }}

+
+ +
+ {% endif %}
- {% endif %} -
- + +
-
- - - -{% if 'delete_images' in imageset_perms and not imageset_lock %} -