reykit 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
reykit/rimage.py ADDED
@@ -0,0 +1,261 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2023-04-22 17:27:47
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Image methods.
9
+ """
10
+
11
+
12
+ from typing import Any, Union, Optional
13
+ from io import BytesIO
14
+ from qrcode import make as qrcode_make
15
+ from qrcode.image.pil import PilImage
16
+ from PIL.Image import open as pil_open, LANCZOS
17
+ from captcha.image import ImageCaptcha
18
+
19
+ from .rexception import catch_exc
20
+ from .rmonkey import monkey_path_pil_image_get_bytes
21
+ from .ros import RFile
22
+ from .rrandom import randchar
23
+
24
+ try:
25
+ from pyzbar.pyzbar import decode as pyzbar_decode
26
+ except:
27
+ *_, pyzbar_decode, _ = catch_exc()
28
+
29
+
30
+ __all__ = (
31
+ 'RImage',
32
+ 'encode_qrcode',
33
+ 'decode_qrcode',
34
+ 'compress_image',
35
+ 'to_pimage',
36
+ 'generate_captcha_image'
37
+ )
38
+
39
+
40
+ # Monkey path.
41
+ monkey_image_type = monkey_path_pil_image_get_bytes()
42
+ RImage = monkey_image_type
43
+
44
+
45
+ def encode_qrcode(text: str, path: Optional[str] = None) -> bytes:
46
+ """
47
+ Encoding text to QR code image.
48
+
49
+ Parameters
50
+ ----------
51
+ text : Text.
52
+ path : File save path.
53
+ - `None`: Not save.
54
+
55
+ Returns
56
+ -------
57
+ Image bytes data.
58
+ """
59
+
60
+ # Encode.
61
+ image: PilImage = qrcode_make(text)
62
+
63
+ # Extract.
64
+ bytes_io = BytesIO()
65
+ image.save(bytes_io, 'JPEG')
66
+ file_bytes = bytes_io.getvalue()
67
+
68
+ # Save.
69
+ if path is not None:
70
+ rfile = RFile(path)
71
+ rfile.write(file_bytes)
72
+
73
+ return file_bytes
74
+
75
+
76
+ def decode_qrcode(image: Union[str, bytes]) -> list[str]:
77
+ """
78
+ Decoding QR code or bar code image.
79
+
80
+ Parameters
81
+ ----------
82
+ image : Image bytes data or image file path.
83
+
84
+ Returns
85
+ -------
86
+ QR code or bar code text list.
87
+ """
88
+
89
+ # Check.
90
+ if isinstance(pyzbar_decode, BaseException):
91
+ raise pyzbar_decode
92
+
93
+ # Handle parameter.
94
+ if image.__class__ in (bytes, bytearray):
95
+ image = BytesIO(image)
96
+
97
+ # Decode.
98
+ image = pil_open(image)
99
+ qrcodes_data = pyzbar_decode(image)
100
+
101
+ # Convert.
102
+ texts = [
103
+ data.data.decode()
104
+ for data in qrcodes_data
105
+ ]
106
+
107
+ return texts
108
+
109
+
110
+ def compress_image(
111
+ input_image: Union[str, bytes],
112
+ ouput_image: Optional[str] = None,
113
+ target_size: float = 0.5,
114
+ rate: int = 5,
115
+ reduce: bool = False,
116
+ max_quality: int = 75,
117
+ min_quality: int = 0
118
+ ) -> Optional[bytes]:
119
+ """
120
+ Compress image file.
121
+
122
+ Parameters
123
+ ----------
124
+ input_image : Input source image data.
125
+ - `str`: Source image read file path.
126
+ - `bytes`: Source image bytes data.
127
+ output_image : Output compressed image data.
128
+ - `None`: Return compressed image bytes data.
129
+ - `str`: Compressed image file save path, no return.
130
+ target_size : Compressed target size.
131
+ - `value < 1`: Not more than this size ratio.
132
+ - `value > 1`: Not more than this value, unit is KB.
133
+ rate : Compressed iteration rate of quality and resolution.
134
+ reduce : If target size is not completed, whether reduce image resolution for compression.
135
+ max_quality : Iteration start image quality rate.
136
+ min_quality : Iteration cutoff image quality rate.
137
+
138
+ Returns
139
+ -------
140
+ Compressed image bytes data.
141
+ """
142
+
143
+ # Handle parameter.
144
+ if input_image.__class__ == str:
145
+ rfile = RFile(input_image)
146
+ input_image = rfile.str
147
+ now_size = len(input_image)
148
+ if target_size < 1:
149
+ target_size = now_size * target_size
150
+ else:
151
+ target_size *= 1024
152
+
153
+ # Read image.
154
+ bytesio = BytesIO(input_image)
155
+ image = pil_open(bytesio)
156
+ image = image.convert('RGB')
157
+
158
+ # Step compress.
159
+ quality = max_quality
160
+ while now_size > target_size and quality >= min_quality:
161
+ bytesio = BytesIO()
162
+ image.save(bytesio, 'JPEG', quality=quality)
163
+ now_size = len(bytesio.read())
164
+ quality -= rate
165
+
166
+ # Step reduce.
167
+ if reduce:
168
+ ratio = 1 - rate / 100
169
+ while now_size > target_size:
170
+ bytesio = BytesIO()
171
+ resize = image.size[0] * ratio, image.size[1] * ratio
172
+ image.thumbnail(resize, LANCZOS)
173
+ image.save(bytesio, 'JPEG', quality=min_quality)
174
+ now_size = len(bytesio.read())
175
+ ratio -= rate / 100
176
+
177
+ # Return.
178
+ content = bytesio.read()
179
+
180
+ ## Return file bytes data.
181
+ if ouput_image is None:
182
+ return content
183
+
184
+ ## Save file and return path.
185
+ else:
186
+ rfile = RFile(ouput_image)
187
+ rfile(content)
188
+
189
+
190
+ def to_pimage(image: Union[str, bytes]) -> RImage:
191
+ """
192
+ Get `Image` instance of `PIL` package.
193
+
194
+ Parameters
195
+ ----------
196
+ image : Image source data.
197
+ - `str`: Image file path.
198
+ - `bytes`: Image bytes data.
199
+
200
+ Returns
201
+ -------
202
+ `Image` instance.
203
+ """
204
+
205
+ # File path.
206
+ if image.__class__ == str:
207
+ pil_image = pil_open(image)
208
+
209
+ # Bytes data.
210
+ if image.__class__ in (bytes, bytearray):
211
+ bytes_io = BytesIO(image)
212
+ pil_image = pil_open(bytes_io)
213
+
214
+ return pil_image
215
+
216
+
217
+ def generate_captcha_image(
218
+ text: Optional[Union[int, str]] = None,
219
+ path: Optional[str] = None,
220
+ **kwargs: Any
221
+ ) -> bytes:
222
+ """
223
+ Generate captcha image, based `captcha` package.
224
+
225
+ Parameters
226
+ ----------
227
+ text : Text, contains digits and Uppercase letters and lowercase letters.
228
+ - `None`: Random five characters.
229
+ - `int`: Given length Random characters.
230
+ - `str`: Given characters.
231
+ path : File save path.
232
+ - `None`: Not save.
233
+ kwargs : `ImageCaptcha` Parameters.
234
+
235
+ Returns
236
+ -------
237
+ Captcha image bytes data.
238
+ """
239
+
240
+ # Get parameter.
241
+ text = text or 5
242
+ if text.__class__ == int:
243
+ text = randchar(text, False)
244
+
245
+ # Generate.
246
+ default_kwargs = {
247
+ 'width': 240,
248
+ 'height': 90,
249
+ 'font_sizes': (61, 75, 84)
250
+ }
251
+ default_kwargs.update(kwargs)
252
+ icaptcha = ImageCaptcha(**default_kwargs)
253
+ image: RImage = icaptcha.generate_image(text)
254
+ file_bytes = image.get_bytes()
255
+
256
+ # Save.
257
+ if path is not None:
258
+ rfile = RFile(path)
259
+ rfile.write(file_bytes)
260
+
261
+ return file_bytes