annotate sat/tools/image.py @ 3252:54934ee3f69c

tools (image): added a guess_type method to guess media type: media type is guessed first from filename, then if it's not enough, the file is opened to try to guess type. If it still can be determined, None is returned.
author Goffi <goffi@goffi.org>
date Tue, 14 Apr 2020 20:29:37 +0200
parents 4fbea7f1e012
children f300d78f08f3
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/env python3
3137
559a625a236b fixed shebangs
Goffi <goffi@goffi.org>
parents: 3136
diff changeset
2
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
3 # SàT: an XMPP client
3136
9d0df638c8b4 dates update
Goffi <goffi@goffi.org>
parents: 3066
diff changeset
4 # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
5
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 # This program is free software: you can redistribute it and/or modify
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # it under the terms of the GNU Affero General Public License as published by
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # the Free Software Foundation, either version 3 of the License, or
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # (at your option) any later version.
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 # This program is distributed in the hope that it will be useful,
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # GNU Affero General Public License for more details.
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
15
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 # You should have received a copy of the GNU Affero General Public License
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
18
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
19 """Methods to manipulate images"""
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
20
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 import tempfile
3252
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
22 import mimetypes
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 from PIL import Image
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 from pathlib import Path
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 from twisted.internet import threads
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
26
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
27
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
28 def check(host, path, max_size=None):
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
29 """Analyze image and return a report
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
30
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 report will indicate if image is too large, and the recommended new size if this is
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 the case
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
33 @param host: SàT instance
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
34 @param path(str, pathlib.Path): image to open
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
35 @param max_size(tuple[int, int]): maximum accepted size of image
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
36 None to use value set in config
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 @return dict: report on image, with following keys:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
38 - too_large: true if image is oversized
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 - recommended_size: if too_large is True, recommended size to use
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
40 """
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 report = {}
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 image = Image.open(path)
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
43 if max_size is None:
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
44 max_size = tuple(host.memory.getConfig(None, "image_max", (1200, 720)))
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 if image.size > max_size:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
46 report['too_large'] = True
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
47 if image.size[0] > max_size[0]:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
48 factor = max_size[0] / image.size[0]
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
49 if image.size[1] * factor > max_size[1]:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
50 factor = max_size[1] / image.size[1]
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 else:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 factor = max_size[1] / image.size[1]
3157
8b4354b5c05f tools (images): fixed type for recommended_size.
Goffi <goffi@goffi.org>
parents: 3137
diff changeset
53 report['recommended_size'] = [int(image.width*factor), int(image.height*factor)]
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 else:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
55 report['too_large'] = False
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
56
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 return report
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
58
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
59
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
60 def _resizeBlocking(image_path, new_size, dest=None):
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 im_path = Path(image_path)
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 im = Image.open(im_path)
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 resized = im.resize(new_size, Image.LANCZOS)
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
64
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
65 if dest is None:
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
66 dest = tempfile.NamedTemporaryFile(suffix=im_path.suffix, delete=False)
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
67 elif isinstance(dest, Path):
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
68 dest = dest.open('wb')
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
69
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
70 with dest as f:
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
71 resized.save(f, format=im.format)
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
72
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 return Path(f.name)
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
74
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
75
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
76 def resize(image_path, new_size, dest=None):
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
77 """Resize an image to a new file, and return its path
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
78
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
79 @param image_path(str, Path): path of the original image
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
80 @param new_size(tuple[int, int]): size to use for new image
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
81 @param dest(None, Path, file): where the resized image must be stored, can be:
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
82 - None: use a temporary file
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
83 - Path: path to the file to create/overwrite
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
84 - file: a file object which must be opened for writing in binary mode
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
85 @return (Path): path of the resized file.
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
86 The image at this path should be deleted after use
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 """
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
88 return threads.deferToThread(_resizeBlocking, image_path, new_size, dest)
3252
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
89
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
90
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
91 def guess_type(source):
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
92 """Guess image media type
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
93
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
94 @param source(str, Path, file): image to guess type
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
95 @return (str, None): media type, or None if we can't guess
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
96 """
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
97 if isinstance(source, str):
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
98 source = Path(source)
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
99
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
100 if isinstance(source, Path):
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
101 # we first try to guess from file name
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
102 media_type = mimetypes.guess_type(source, strict=False)[0]
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
103 if media_type is not None:
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
104 return media_type
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
105
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
106 # file name is not enough, we try to open it
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
107 img = Image.open(source)
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
108 try:
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
109 return Image.MIME[img.format]
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
110 except KeyError:
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
111 return None
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
112
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
113