add watermark

Brightcells 9 年 前
コミット
4a8b4f8819

+ 19 - 0
account/migrations/0004_auto_20151207_1811.py

@@ -0,0 +1,19 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.db import models, migrations
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    dependencies = [
10
+        ('account', '0003_auto_20151202_2313'),
11
+    ]
12
+
13
+    operations = [
14
+        migrations.AlterField(
15
+            model_name='lensmaninfo',
16
+            name='lensman_id',
17
+            field=models.CharField(null=True, max_length=255, blank=True, help_text='\u6444\u5f71\u5e08\u552f\u4e00\u6807\u8bc6', unique=True, verbose_name='lensman_id', db_index=True),
18
+        ),
19
+    ]

+ 3 - 0
pai2/settings.py

@@ -150,6 +150,9 @@ REST_FRAMEWORK = {
150 150
 # 唯一标识设置
151 151
 CURTAIL_UUID_LENGTH = 7
152 152
 
153
+# 水印设置
154
+WATERMARK_LOGO = os.path.join(PROJ_DIR, 'static/pai2/img/paiai_96_96.png').replace('\\', '/')
155
+
153 156
 # 域名设置
154 157
 DOMAIN = 'http://xfoto.com.cn'
155 158
 

BIN
pai2/static/pai2/img/paiai_96_96.png


+ 1 - 1
photo/admin.py

@@ -11,7 +11,7 @@ class UUIDInfoAdmin(admin.ModelAdmin):
11 11
 
12 12
 
13 13
 class PhotosInfoAdmin(admin.ModelAdmin):
14
-    list_display = ('lensman_id', 'session_id', 'photo_id', 'photo_name', 'photo_path', 'status', 'created_at', 'updated_at')
14
+    list_display = ('lensman_id', 'session_id', 'photo_id', 'p_photo_path', 'status', 'created_at', 'updated_at')
15 15
     list_filter = ('lensman_id', 'status')
16 16
 
17 17
 

+ 42 - 0
photo/migrations/0005_auto_20151207_1811.py

@@ -0,0 +1,42 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.db import models, migrations
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    dependencies = [
10
+        ('photo', '0004_photosinfo_photo_name'),
11
+    ]
12
+
13
+    operations = [
14
+        migrations.RemoveField(
15
+            model_name='photosinfo',
16
+            name='photo_name',
17
+        ),
18
+        migrations.RemoveField(
19
+            model_name='photosinfo',
20
+            name='photo_path',
21
+        ),
22
+        migrations.AddField(
23
+            model_name='photosinfo',
24
+            name='l_photo_path',
25
+            field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='l_photo_path', blank=True),
26
+        ),
27
+        migrations.AddField(
28
+            model_name='photosinfo',
29
+            name='m_photo_path',
30
+            field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='m_photo_path', blank=True),
31
+        ),
32
+        migrations.AddField(
33
+            model_name='photosinfo',
34
+            name='p_photo_path',
35
+            field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='p_photo_path', blank=True),
36
+        ),
37
+        migrations.AddField(
38
+            model_name='photosinfo',
39
+            name='r_photo_path',
40
+            field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='r_photo_path', blank=True),
41
+        ),
42
+    ]

+ 18 - 6
photo/models.py

@@ -32,8 +32,10 @@ class PhotosInfo(CreateUpdateMixin):
32 32
     lensman_id = models.CharField(_(u'lensman_id'), max_length=255, blank=True, null=True, help_text=u'摄影师唯一标识', db_index=True)
33 33
     session_id = models.CharField(_(u'session_id'), max_length=255, blank=True, null=True, help_text=u'照片组唯一标识', db_index=True)
34 34
     photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'照片唯一标识', db_index=True, unique=True)
35
-    photo_name = models.CharField(_(u'photo_name'), max_length=255, blank=True, null=True, help_text=u'照片存放名称')
36
-    photo_path = models.CharField(_(u'photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')
35
+    p_photo_path = models.CharField(_(u'p_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')
36
+    m_photo_path = models.CharField(_(u'm_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')
37
+    l_photo_path = models.CharField(_(u'l_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')
38
+    r_photo_path = models.CharField(_(u'r_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径')
37 39
 
38 40
     class Meta:
39 41
         verbose_name = _('photosinfo')
@@ -46,9 +48,20 @@ class PhotosInfo(CreateUpdateMixin):
46 48
         return u'{0.pk}'.format(self)
47 49
 
48 50
     @property
49
-    def photo_url(self):
50
-        return u'{0}/media/{1}'.format(settings.DOMAIN, self.photo_path) if self.photo_path else ''
51
-        # return u'{0}/p/{1}'.format(settings.DOMAIN, self.photo_name) if self.photo_name else ''
51
+    def p_photo_url(self):
52
+        return u'{0}/media/{1}'.format(settings.DOMAIN, self.p_photo_path) if self.p_photo_path else ''
53
+
54
+    @property
55
+    def m_photo_url(self):
56
+        return u'{0}/media/{1}'.format(settings.DOMAIN, self.m_photo_path) if self.m_photo_path else ''
57
+
58
+    @property
59
+    def l_photo_url(self):
60
+        return u'{0}/media/{1}'.format(settings.DOMAIN, self.l_photo_path) if self.l_photo_path else ''
61
+
62
+    @property
63
+    def r_photo_url(self):
64
+        return u'{0}/media/{1}'.format(settings.DOMAIN, self.r_photo_path) if self.r_photo_path else ''
52 65
 
53 66
     def _data(self):
54 67
         return {
@@ -56,7 +69,6 @@ class PhotosInfo(CreateUpdateMixin):
56 69
             'user': self.lensman_id,
57 70
             'session': self.session_id,
58 71
             'photo': self.photo_id,
59
-            # 'photo_url': self.photo_url,
60 72
         }
61 73
 
62 74
     data = property(_data)

+ 1 - 1
photo/templates/photo/session_detail.html

@@ -6,7 +6,7 @@
6 6
 </head>
7 7
 <body>
8 8
     {% for photo in photos %}
9
-    <div><img src="{{ photo.photo_url }}"></div>
9
+    <div><img src="{{ photo.p_photo_url }}"></div>
10 10
     {% endfor %}
11 11
 </body>
12 12
 </html>

+ 19 - 12
photo/views.py

@@ -1,5 +1,6 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3
+from django.conf import settings
3 4
 from django.core.files.storage import default_storage
4 5
 from django.db import transaction
5 6
 from django.http import JsonResponse
@@ -12,6 +13,7 @@ from photo.models import UUIDInfo, PhotosInfo
12 13
 from photo.serializers import PhotosInfoSerializer
13 14
 
14 15
 from utils.uuid_utils import curtailUUID
16
+from utils.watermark_utils import watermark_wrap
15 17
 
16 18
 import os
17 19
 
@@ -83,20 +85,25 @@ def upload_photo(request):
83 85
     photo_id = curtailUUID(PhotosInfo, 'photo_id')
84 86
 
85 87
     _, extension = os.path.splitext(photo.name)
86
-    # photo_path = 'photo/{0}/{1}/{2}{3}'.format(lensman_id, session_id, photo_id, extension)
87
-    photo_name = '{0}{1}'.format(photo_id, extension)
88
-    photo_path = 'photo/{0}'.format(photo_name)
88
+    m_photo_path = 'photo/{photo_id}_m{extension}'.format(photo_id=photo_id, extension=extension)
89 89
 
90
-    if default_storage.exists(photo_path):
91
-        default_storage.delete(photo_path)
92
-    default_storage.save(photo_path, photo)
90
+    if default_storage.exists(m_photo_path):
91
+        default_storage.delete(m_photo_path)
92
+    default_storage.save(m_photo_path, photo)
93
+
94
+    p_photo_path = 'photo/{photo_id}_p{extension}'.format(photo_id=photo_id, extension=extension)
95
+    watermark_wrap(
96
+        os.path.join(settings.MEDIA_ROOT, m_photo_path).replace('\\', '/'),
97
+        settings.WATERMARK_LOGO,
98
+        os.path.join(settings.MEDIA_ROOT, p_photo_path).replace('\\', '/')
99
+    )
93 100
 
94 101
     photo, created = PhotosInfo.objects.get_or_create(
95 102
         lensman_id=lensman_id,
96 103
         session_id=session_id,
97 104
         photo_id=photo_id,
98
-        photo_name=photo_name,
99
-        photo_path=photo_path
105
+        p_photo_path=p_photo_path,
106
+        m_photo_path=m_photo_path,
100 107
     )
101 108
 
102 109
     return JsonResponse({
@@ -113,22 +120,22 @@ def session_detail(request, session):
113 120
 
114 121
 def photo_standard(request, photo):
115 122
     photo = PhotosInfo.objects.get(photo_id=photo)
116
-    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
123
+    return render(request, 'photo/photo_detail.html', {'photo_url': photo.p_photo_url})
117 124
 
118 125
 
119 126
 def photo_medium(request, photo):
120 127
     photo = PhotosInfo.objects.get(photo_id=photo)
121
-    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
128
+    return render(request, 'photo/photo_detail.html', {'photo_url': photo.m_photo_url})
122 129
 
123 130
 
124 131
 def photo_large(request, photo):
125 132
     photo = PhotosInfo.objects.get(photo_id=photo)
126
-    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
133
+    return render(request, 'photo/photo_detail.html', {'photo_url': photo.l_photo_url})
127 134
 
128 135
 
129 136
 def photo_raw(request, photo):
130 137
     photo = PhotosInfo.objects.get(photo_id=photo)
131
-    return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url})
138
+    return render(request, 'photo/photo_detail.html', {'photo_url': photo.r_photo_url})
132 139
 
133 140
 
134 141
 class PhotoInfoViewSet(viewsets.ModelViewSet):

BIN
utils/original_CGzC_10a50000c8811190.jpg


BIN
utils/original_CGzC_10a50000c8811190副本.jpg


BIN
utils/paiai_96_96.png


+ 62 - 0
utils/watermark_utils.py

@@ -0,0 +1,62 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+try:
4
+    import Image
5
+    import ImageEnhance
6
+except ImportError:
7
+    from PIL import Image, ImageEnhance
8
+
9
+
10
+def reduce_opacity(im, opacity):
11
+    """Returns an image with reduced opacity."""
12
+    assert 0 <= opacity <= 1
13
+    im = im.convert('RGBA') if im.mode != 'RGBA' else im.copy()
14
+    alpha = im.split()[3]
15
+    alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
16
+    im.putalpha(alpha)
17
+    return im
18
+
19
+
20
+def watermark(im, mark, position, opacity=1):
21
+    """Adds a watermark to an image."""
22
+    if opacity < 1:
23
+        mark = reduce_opacity(mark, opacity)
24
+    if im.mode != 'RGBA':
25
+        im = im.convert('RGBA')
26
+    # create a transparent layer the size of the image and draw the
27
+    # watermark in that layer.
28
+    layer = Image.new('RGBA', im.size, (0, 0, 0, 0))
29
+    if position == 'tile':
30
+        for y in range(0, im.size[1], mark.size[1]):
31
+            for x in range(0, im.size[0], mark.size[0]):
32
+                layer.paste(mark, (x, y))
33
+    elif position == 'scale':
34
+        # scale, but preserve the aspect ratio
35
+        ratio = min(
36
+            float(im.size[0]) / mark.size[0], float(im.size[1]) / mark.size[1])
37
+        w = int(mark.size[0] * ratio)
38
+        h = int(mark.size[1] * ratio)
39
+        mark = mark.resize((w, h))
40
+        layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2))
41
+    else:
42
+        layer.paste(mark, position)
43
+    # composite the watermark with the layer
44
+    return Image.composite(layer, im, layer)
45
+
46
+
47
+def watermark_wrap(im_path, mark_path, save_path=''):
48
+    im, mark = Image.open(im_path), Image.open(mark_path)
49
+    new_im = watermark(im, mark, (50, 50), 0.5)
50
+    new_im.save(save_path or im_path)
51
+
52
+
53
+def watermark_test():
54
+    im, mark = Image.open('original_CGzC_10a50000c8811190.jpg'), Image.open('paiai_96_96.png')
55
+    watermark(im, mark, 'tile', 0.5).show()
56
+    watermark(im, mark, 'scale', 1.0).show()
57
+    watermark(im, mark, (50, 50), 0.5).show()
58
+
59
+
60
+if __name__ == '__main__':
61
+    # watermark_test()
62
+    watermark_wrap('original_CGzC_10a50000c8811190.jpg', 'paiai_96_96.png')