Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0264.py @ 3330:7b47f48d31f3
plugin XEP-0264: fix orientation of thumbnails:
Rotation of thumbnailis is now modified according to EXIF orientation data. Original image
is not modified as user may want to keep it, and orientation transformation can modify
encoding parameters.
Update `pillow` minimum version to the first one with `exif_transpose` implementation.
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 13 Aug 2020 23:46:18 +0200 |
parents | 15612c0fb421 |
children | 4f1fcee83d36 |
comparison
equal
deleted
inserted
replaced
3329:15612c0fb421 | 3330:7b47f48d31f3 |
---|---|
32 | 32 |
33 from sat.core import exceptions | 33 from sat.core import exceptions |
34 import hashlib | 34 import hashlib |
35 | 35 |
36 try: | 36 try: |
37 from PIL import Image | 37 from PIL import Image, ImageOps |
38 except: | 38 except: |
39 raise exceptions.MissingModule( | 39 raise exceptions.MissingModule( |
40 "Missing module pillow, please download/install it from https://python-pillow.github.io" | 40 "Missing module pillow, please download/install it from https://python-pillow.github.io" |
41 ) | 41 ) |
42 | 42 |
136 @param size(tuple(int)): requested size of thumbnail | 136 @param size(tuple(int)): requested size of thumbnail |
137 @return (unicode): unique id for this image/size | 137 @return (unicode): unique id for this image/size |
138 """ | 138 """ |
139 return hashlib.sha256(repr((image_uid, size)).encode()).hexdigest() | 139 return hashlib.sha256(repr((image_uid, size)).encode()).hexdigest() |
140 | 140 |
141 def _blockingGenThumb(self, source_path, size=None, max_age=None, image_uid=None): | 141 def _blockingGenThumb( |
142 self, source_path, size=None, max_age=None, image_uid=None, | |
143 fix_orientation=True): | |
142 """Generate a thumbnail for image | 144 """Generate a thumbnail for image |
143 | 145 |
144 This is a blocking method and must be executed in a thread | 146 This is a blocking method and must be executed in a thread |
145 params are the same as for [generateThumbnail] | 147 params are the same as for [generateThumbnail] |
146 """ | 148 """ |
150 img = Image.open(source_path) | 152 img = Image.open(source_path) |
151 except IOError: | 153 except IOError: |
152 return Failure(exceptions.DataError("Can't open image")) | 154 return Failure(exceptions.DataError("Can't open image")) |
153 | 155 |
154 img.thumbnail(size) | 156 img.thumbnail(size) |
157 if fix_orientation: | |
158 img = ImageOps.exif_transpose(img) | |
159 | |
155 uid = self.getThumbId(image_uid or source_path, size) | 160 uid = self.getThumbId(image_uid or source_path, size) |
156 | 161 |
157 with self.host.common_cache.cacheData( | 162 with self.host.common_cache.cacheData( |
158 PLUGIN_INFO[C.PI_IMPORT_NAME], uid, MIME_TYPE, max_age | 163 PLUGIN_INFO[C.PI_IMPORT_NAME], uid, MIME_TYPE, max_age |
159 ) as f: | 164 ) as f: |
160 img.save(f, SAVE_FORMAT) | 165 img.save(f, SAVE_FORMAT) |
166 log.debug(f"fixed orientation for {f.name}") | |
161 | 167 |
162 return img.size, uid | 168 return img.size, uid |
163 | 169 |
164 def generateThumbnail(self, source_path, size=None, max_age=None, image_uid=None): | 170 def generateThumbnail( |
171 self, source_path, size=None, max_age=None, image_uid=None, fix_orientation=True): | |
165 """Generate a thumbnail of image | 172 """Generate a thumbnail of image |
166 | 173 |
167 @param source_path(unicode): absolute path to source image | 174 @param source_path(unicode): absolute path to source image |
168 @param size(int, None): max size of the thumbnail | 175 @param size(int, None): max size of the thumbnail |
169 can be one of self.SIZE_* | 176 can be one of self.SIZE_* |
170 None to use default value (i.e. self.SIZE_SMALL) | 177 None to use default value (i.e. self.SIZE_SMALL) |
171 @param max_age(int, None): same as for [memory.cache.Cache.cacheData]) | 178 @param max_age(int, None): same as for [memory.cache.Cache.cacheData]) |
172 @param image_uid(unicode, None): unique ID to identify the image | 179 @param image_uid(unicode, None): unique ID to identify the image |
173 use hash whenever possible | 180 use hash whenever possible |
174 if None, source_path will be used | 181 if None, source_path will be used |
182 @param fix_orientation(bool): if True, fix orientation using EXIF data | |
175 @return D(tuple[tuple[int,int], unicode]): tuple with: | 183 @return D(tuple[tuple[int,int], unicode]): tuple with: |
176 - size of the thumbnail | 184 - size of the thumbnail |
177 - unique Id of the thumbnail | 185 - unique Id of the thumbnail |
178 """ | 186 """ |
179 | |
180 d = threads.deferToThread( | 187 d = threads.deferToThread( |
181 self._blockingGenThumb, source_path, size, max_age, image_uid=image_uid | 188 self._blockingGenThumb, source_path, size, max_age, image_uid=image_uid, |
189 fix_orientation=fix_orientation | |
182 ) | 190 ) |
183 d.addErrback( | 191 d.addErrback( |
184 lambda failure_: log.error("thumbnail generation error: {}".format(failure_)) | 192 lambda failure_: log.error("thumbnail generation error: {}".format(failure_)) |
185 ) | 193 ) |
186 return d | 194 return d |