刘宇轩 commited on
Commit
3a8ba72
·
1 Parent(s): 8bf2c1e

Initial Commit

Browse files
.gitignore ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ossutil_output
2
+ __pycache__
3
+ .DS_Store
4
+ test.jpg
5
+ test.py
6
+ test.sh
7
+ app.sh
8
+ __pycache__
9
+ r1.jpg
10
+ r2.png
11
+ r11.png
12
+ test.jpg
13
+ temp.py
app.py ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib
2
+ import time
3
+ import uuid
4
+ from urllib.parse import urlencode
5
+ import json
6
+ import requests
7
+ from PIL import Image, ImageOps
8
+ import os
9
+ import gradio as gr
10
+
11
+ import ops
12
+ from watermark import WatermarkApp
13
+ import db_examples
14
+
15
+
16
+ class ApiClient(object):
17
+
18
+ def __init__(self, app_key: str, access_key_id: str, access_key_secret: str, endpoint: str):
19
+ self.app_key = app_key
20
+ self.access_key_id = access_key_id
21
+ self.access_key_secret = access_key_secret
22
+ self.endpoint = endpoint
23
+ self.base_url = 'http://' + self.endpoint + '/api'
24
+ self.timeout = 8000
25
+ self.session = requests.Session()
26
+ self.session.headers.update(
27
+ {
28
+ "Content-Type": "application/json;charset=utf-8",
29
+ "accessKey": self.access_key_id,
30
+ "appKey": self.app_key
31
+ }
32
+ )
33
+
34
+ def send_get(self, headers=None, params=None):
35
+ if headers is None:
36
+ headers = {}
37
+ if params is None:
38
+ params = {}
39
+ args = self.cleanNoneValue(
40
+ {
41
+ "url": self.base_url,
42
+ "headers": self._prepare_headers(headers),
43
+ "params": self._prepare_params(params),
44
+ "timeout": self.timeout
45
+ }
46
+ )
47
+ response = self._dispatch_request("GET")(**args)
48
+ self._handle_exception(response)
49
+ try:
50
+ data = response.json()
51
+ except ValueError:
52
+ data = response.text
53
+ return data
54
+
55
+ def send_post(self, headers=None, params=None):
56
+ if headers is None:
57
+ headers = {}
58
+ if params is None:
59
+ params = {}
60
+ args = self.cleanNoneValue(
61
+ {
62
+ "url": self.base_url,
63
+ "headers": self._prepare_headers(headers),
64
+ "json": params,
65
+ "timeout": self.timeout
66
+ }
67
+ )
68
+ response = self._dispatch_request("POST")(**args)
69
+ self._handle_exception(response)
70
+ try:
71
+ data = response.json()
72
+ except ValueError:
73
+ data = response.text
74
+ return data
75
+
76
+ def _prepare_headers(self, headers):
77
+ headers['requestId'] = str(uuid.uuid1())
78
+ timestamp = int(round(time.time() * 1000))
79
+ headers['timestamp'] = str(timestamp)
80
+ headers['sign'] = self._get_sign(timestamp)
81
+ return headers
82
+
83
+ def _prepare_params(self, params):
84
+ return self.encoded_string(self.cleanNoneValue(params))
85
+
86
+ def _get_sign(self, timestamp):
87
+ key = self.app_key + self.access_key_id + str(timestamp) + self.access_key_secret
88
+ md5hash = hashlib.md5(str.encode(key, 'utf-8'))
89
+ return md5hash.hexdigest()
90
+
91
+ def _dispatch_request(self, http_method):
92
+ return {
93
+ "GET": self.session.get,
94
+ "DELETE": self.session.delete,
95
+ "PUT": self.session.put,
96
+ "POST": self.session.post,
97
+ }.get(http_method, "GET")
98
+
99
+ def _handle_exception(self, response):
100
+ status_code = response.status_code
101
+ if status_code < 400:
102
+ return
103
+ raise Exception(response.text)
104
+
105
+ def encoded_string(self, query):
106
+
107
+ return urlencode(query, True).replace("%40", "@")
108
+
109
+ def cleanNoneValue(self, d) -> dict:
110
+ out = {}
111
+ for k in d.keys():
112
+ if d[k] is not None:
113
+ out[k] = d[k]
114
+ return out
115
+
116
+
117
+ api_key = os.environ['APIKEY']
118
+ ak = os.environ['AK']
119
+ sk = os.environ['SK']
120
+ endpoint = os.environ['ENDPOINT']
121
+ apiname = os.environ['APINAME']
122
+ callbackapiname = os.environ['CALLBACKAPINAME']
123
+ osssigneapiname = os.environ['OSSSIGNAPINAME']
124
+
125
+ client = ApiClient(api_key, ak, sk, endpoint)
126
+ watermark_app = WatermarkApp()
127
+
128
+ def upload_to_oss(file_path, accessid, policy, signature, host, key, callback):
129
+ with open(file_path, 'rb') as f:
130
+ files = {'file': (key, f)}
131
+ data = {
132
+ 'OSSAccessKeyId': accessid,
133
+ 'policy': policy,
134
+ 'Signature': signature,
135
+ 'key': key,
136
+ 'callback': callback,
137
+ }
138
+ response = requests.post(host, files=files, data=data)
139
+ if response.status_code == 204 or response.ok:
140
+ return key
141
+ else:
142
+ print(f"file upload failed: {response.text}")
143
+ return None
144
+
145
+
146
+ def upload_oss_bucket(image_pil):
147
+ headers = {
148
+ 'apiName': osssigneapiname
149
+ }
150
+ params = {
151
+ 'fileType': '1'
152
+ }
153
+ result = client.send_get(headers, params)
154
+ try:
155
+ result = result['data']
156
+ except Exception as e:
157
+ raise ValueError('oss sign error')
158
+ accessid = result['accessid']
159
+ policy = result['policy']
160
+ signature = result['signature']
161
+ host = result['host']
162
+ callback = ''
163
+ oss_key = os.path.join(result['dir'], '{}.jpg'.format(result['expire']))
164
+ file_path = 'test.jpg'
165
+ image_pil.save(file_path)
166
+ oss_key = upload_to_oss(file_path, accessid, policy, signature, host, oss_key, callback)
167
+ if oss_key is None:
168
+ raise ValueError('oss upload error')
169
+ return oss_key
170
+
171
+
172
+ def call_text_guided_relighting(image, mode, prompt, seed, steps):
173
+ headers = {
174
+ 'apiName': apiname
175
+ }
176
+ image = ImageOps.exif_transpose(image).convert('RGB')
177
+ image = ops.resize_keep_hw_rate(image, tar_res=1280)
178
+ image = upload_oss_bucket(image)
179
+ ops.print_with_datetime('start call_text_guided_relighting')
180
+ ops.print_with_datetime(prompt)
181
+ params = {
182
+ 'image': image,
183
+ 'inference_mode': 'free_txt2bg_gen',
184
+ 'mode': mode,
185
+ 'prompt': prompt,
186
+ 'seed': seed,
187
+ 'steps': steps,
188
+ }
189
+ ops.print_with_datetime(f'length params, {len(json.dumps(params))}')
190
+ task_id = client.send_post(headers, params)['data']
191
+ time.sleep(10)
192
+ headers = {
193
+ 'apiName': callbackapiname
194
+ }
195
+ params = {
196
+ 'id': task_id
197
+ }
198
+ flag = True
199
+ while flag:
200
+ result = client.send_get(headers, params)['data']
201
+ if result['status'] != 1:
202
+ flag = False
203
+ else:
204
+ time.sleep(10)
205
+ if result['status'] != 2:
206
+ raise ValueError('something wrong in the process')
207
+
208
+ result_1 = result['sasMyCreationPicVOs'][0]['picUrl']
209
+ result_2 = result['sasMyCreationPicVOs'][0]['maskUrl']
210
+ result_1 = ops.decode_img_from_url(result_1)
211
+ result_1 = watermark_app.process_image(result_1)
212
+ result_2 = ops.decode_img_from_url(result_2)
213
+ return result_1, result_2
214
+
215
+
216
+ def call_image_guided_relighting(image, ref_img, seed, steps):
217
+ headers = {
218
+ 'apiName': apiname
219
+ }
220
+ image = ImageOps.exif_transpose(image).convert('RGB')
221
+ image = ops.resize_keep_hw_rate(image, tar_res=1280)
222
+ image = upload_oss_bucket(image)
223
+
224
+ ref_img = ImageOps.exif_transpose(ref_img).convert('RGB')
225
+ ref_img = ops.resize_keep_hw_rate(ref_img, tar_res=1280)
226
+ ref_img = upload_oss_bucket(ref_img)
227
+
228
+ ops.print_with_datetime('start call_image_guided_relighting')
229
+ params = {
230
+ 'image': image,
231
+ 'inference_mode': 'replica_gen',
232
+ 'mode': 'normal',
233
+ 'ref_img': ref_img,
234
+ 'seed': seed,
235
+ 'steps': steps,
236
+ }
237
+ ops.print_with_datetime(f'length params, {len(json.dumps(params))}')
238
+ task_id = client.send_post(headers, params)
239
+ print(task_id)
240
+ task_id = task_id['data']
241
+ time.sleep(10)
242
+ headers = {
243
+ 'apiName': callbackapiname
244
+ }
245
+ params = {
246
+ 'id': task_id
247
+ }
248
+ flag = True
249
+ while flag:
250
+ result = client.send_get(headers, params)
251
+ result = result['data']
252
+ if result['status'] != 1:
253
+ flag = False
254
+ else:
255
+ time.sleep(10)
256
+ if result['status'] != 2:
257
+ raise ValueError('something wrong in the process')
258
+
259
+ result_1 = result['sasMyCreationPicVOs'][0]['picUrl']
260
+ result_2 = result['sasMyCreationPicVOs'][0]['maskUrl']
261
+ result_1 = ops.decode_img_from_url(result_1)
262
+ result_1 = watermark_app.process_image(result_1)
263
+ result_2 = ops.decode_img_from_url(result_2)
264
+ return result_1, result_2
265
+
266
+
267
+
268
+ quick_prompts = [
269
+ 'warm lighting',
270
+ 'sunshine from window',
271
+ 'neon lighting',
272
+ 'at noon. bright sunlight',
273
+ 'at dusk',
274
+ 'golden time',
275
+ 'natural lighting',
276
+ 'shadow from window',
277
+ 'soft studio lighting',
278
+ 'red lighting',
279
+ 'purple lighting'
280
+ ]
281
+ quick_prompts = [[x] for x in quick_prompts]
282
+
283
+
284
+ quick_content_prompts = [
285
+ 'by the sea',
286
+ 'in the forest',
287
+ 'on the snow mountain',
288
+ 'by the city street',
289
+ 'on the grassy field',
290
+ 'cityscape',
291
+ 'on the desert',
292
+ 'in the living room',
293
+ ]
294
+ quick_content_prompts = [[x] for x in quick_content_prompts]
295
+
296
+
297
+ quick_subjects = [
298
+ 'portrait photography of a woman',
299
+ 'portrait photography of man',
300
+ 'product photography',
301
+ ]
302
+ quick_subjects = [[x] for x in quick_subjects]
303
+
304
+
305
+ with gr.Blocks().queue() as demo:
306
+ gr.HTML("""
307
+ <div style="text-align: center; font-size: 32px; font-weight: bold; margin-bottom: 20px;">
308
+ FreeLighting: relighting model with both text-condition and image-condition
309
+ </div>
310
+ """)
311
+ with gr.Row():
312
+ gr.Markdown("See more information in https://github.com/liuyuxuan3060/FreeLighting")
313
+ with gr.Row():
314
+ gr.Markdown("We use a open source segmentation model to generate image mask")
315
+
316
+ with gr.Tabs():
317
+ with gr.TabItem("text-guided relighting") as t2v_tab:
318
+ with gr.Row():
319
+ with gr.Column():
320
+ with gr.Row():
321
+ image = gr.Image(label="original_image", type="pil", height=480)
322
+ image_mask = gr.Image(label="image_mask", type="pil", height=480)
323
+ with gr.Row():
324
+ prompt = gr.Textbox(value="", label="text prompt")
325
+ with gr.Row():
326
+ mode = gr.Radio(choices=["normal", "uniform-lit"], value='normal', label="uniform-lit mode will use double time", type='value')
327
+ seed = gr.Number(value=12345, label="random seed", precision=0)
328
+ steps = gr.Slider(label="Steps", minimum=1, maximum=100, value=20, step=1)
329
+ button = gr.Button("generate")
330
+
331
+ with gr.Row():
332
+ example_quick_subjects = gr.Dataset(samples=quick_subjects, label='Subject Quick List', samples_per_page=1000, components=[prompt])
333
+ with gr.Row():
334
+ example_quick_prompts = gr.Dataset(samples=quick_prompts, label='Lighting Quick List', samples_per_page=1000, components=[prompt])
335
+ with gr.Row():
336
+ example_quick_content_prompts = gr.Dataset(samples=quick_content_prompts, label='Content Quick List', samples_per_page=1000, components=[prompt])
337
+ with gr.Column():
338
+ relighting_image = gr.Image(label="relighted_image", type="pil", height=480)
339
+
340
+ with gr.Row():
341
+ gr.Examples(
342
+ examples=db_examples.text_guided_examples,
343
+ inputs=[
344
+ image, mode, prompt, seed, steps
345
+ , relighting_image
346
+ ],
347
+ outputs=[relighting_image],
348
+ examples_per_page=1024
349
+ )
350
+
351
+ button.click(
352
+ fn=call_text_guided_relighting,
353
+ inputs=[
354
+ image, mode, prompt, seed, steps
355
+ ],
356
+ outputs=[relighting_image, image_mask],
357
+ )
358
+ example_quick_content_prompts.click(lambda x, y: ', '.join(y.split(', ')[:2] + [x[0]]), inputs=[example_quick_content_prompts, prompt], outputs=prompt, show_progress=False, queue=False)
359
+ example_quick_prompts.click(lambda x, y: ', '.join(y.split(', ')[:2] + [x[0]]), inputs=[example_quick_prompts, prompt], outputs=prompt, show_progress=False, queue=False)
360
+ example_quick_subjects.click(lambda x: x[0], inputs=example_quick_subjects, outputs=prompt, show_progress=False, queue=False)
361
+
362
+ with gr.TabItem("image-guided relighting") as i2v_tab:
363
+ with gr.Row():
364
+ with gr.Column():
365
+ with gr.Row():
366
+ image = gr.Image(label="original_image", type="pil", height=480)
367
+ ref_img = gr.Image(label="reference_image", type="pil", height=480)
368
+ image_mask = gr.Image(label="image_mask", type="pil", height=480)
369
+ with gr.Row():
370
+ seed = gr.Number(value=12345, label="random seed", precision=0)
371
+ steps = gr.Slider(label="Steps", minimum=1, maximum=100, value=20, step=1)
372
+ button = gr.Button("generate")
373
+ with gr.Column():
374
+ relighting_image = gr.Image(label="relighted_image", type="pil", height=480)
375
+
376
+ with gr.Row():
377
+ gr.Examples(
378
+ examples=db_examples.image_guided_examples,
379
+ inputs=[
380
+ image, ref_img, seed, steps
381
+ , relighting_image
382
+ ],
383
+ outputs=[relighting_image],
384
+ examples_per_page=1024
385
+ )
386
+
387
+ button.click(
388
+ fn=call_image_guided_relighting,
389
+ inputs=[
390
+ image, ref_img, seed, steps
391
+ ],
392
+ outputs=[relighting_image, image_mask],
393
+ )
394
+
395
+
396
+ demo.launch()
397
+
398
+
asset/chaojihui-v2.png ADDED
asset/chaojihui.png ADDED
asset/chaojihui45.png ADDED
asset/zhushe.png ADDED
db_examples.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ text_guided_examples = [
2
+ [
3
+ 'images/original_1.jpg',
4
+ 'uniform-lit',
5
+ 'portrait photography of a woman, at noon. bright sunlight, by the sea',
6
+ 1,
7
+ 20,
8
+ 'images/restxt_1.webp',
9
+ ],
10
+ [
11
+ 'images/original_1.jpg',
12
+ 'uniform-lit',
13
+ 'portrait photography of a woman, neon lighting, by the city street',
14
+ 1,
15
+ 20,
16
+ 'images/restxt_2.webp',
17
+ ],
18
+ [
19
+ 'images/original_2.jpg',
20
+ 'normal',
21
+ 'portrait photography of a woman, purple lighting, in the living room',
22
+ 1,
23
+ 20,
24
+ 'images/restxt_3.webp',
25
+ ],
26
+ [
27
+ 'images/original_2.jpg',
28
+ 'normal',
29
+ 'portrait photography of a woman, warm lighting, on the desert',
30
+ 1,
31
+ 20,
32
+ 'images/restxt_4.webp',
33
+ ],
34
+ [
35
+ 'images/original_3.jpg',
36
+ 'uniform-lit',
37
+ 'portrait photography of a woman, at noon. bright sunlight, cityscape',
38
+ 1,
39
+ 20,
40
+ 'images/restxt_5.webp',
41
+ ],
42
+ [
43
+ 'images/original_3.jpg',
44
+ 'normal',
45
+ 'portrait photography of a woman, at noon. bright sunlight, cityscape',
46
+ 1,
47
+ 20,
48
+ 'images/restxt_6.webp',
49
+ ],
50
+ [
51
+ 'images/original_4.jpg',
52
+ 'normal',
53
+ 'portrait photography of a woman, at noon. bright sunlight, cityscape',
54
+ 1,
55
+ 20,
56
+ 'images/restxt_7.webp',
57
+ ],
58
+ [
59
+ 'images/original_4.jpg',
60
+ 'normal',
61
+ 'portrait photography of a woman, neon lighting, by the city street',
62
+ 1,
63
+ 20,
64
+ 'images/restxt_8.webp',
65
+ ],
66
+ ]
67
+
68
+ image_guided_examples = [
69
+ [
70
+ 'images/original_1.jpg',
71
+ 'images/reference_2.jpg',
72
+ 12345,
73
+ 20,
74
+ 'images/resimg_1.webp',
75
+ ],
76
+ [
77
+ 'images/original_3.jpg',
78
+ 'images/reference_4.jpg',
79
+ 12345,
80
+ 20,
81
+ 'images/resimg_2.webp',
82
+ ],
83
+ [
84
+ 'images/original_4.jpg',
85
+ 'images/reference_3.jpg',
86
+ 12345,
87
+ 20,
88
+ 'images/resimg_3.webp',
89
+ ],
90
+ [
91
+ 'images/original_2.jpg',
92
+ 'images/reference_3.jpg',
93
+ 12345,
94
+ 20,
95
+ 'images/resimg_4.webp',
96
+ ],
97
+ ]
98
+
images/dummy.png ADDED
images/original_1.jpg ADDED
images/original_2.jpg ADDED
images/original_3.jpg ADDED
images/original_4.jpg ADDED
images/reference_1.jpg ADDED
images/reference_2.jpg ADDED
images/reference_3.jpg ADDED
images/reference_4.jpg ADDED
images/resimg_1.webp ADDED
images/resimg_2.webp ADDED
images/resimg_3.webp ADDED
images/resimg_4.webp ADDED
images/restxt_1.webp ADDED
images/restxt_2.webp ADDED
images/restxt_3.webp ADDED
images/restxt_4.webp ADDED
images/restxt_5.webp ADDED
images/restxt_6.webp ADDED
images/restxt_7.webp ADDED
images/restxt_8.webp ADDED
ops.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import numpy as np
3
+ import cv2
4
+ import json
5
+ import pickle
6
+ import itertools
7
+ import requests
8
+ from io import StringIO
9
+
10
+ from io import BytesIO
11
+ import re, os
12
+ from PIL import Image
13
+ from datetime import datetime
14
+ import random
15
+
16
+ import torch
17
+
18
+ import socket
19
+ r_b64_prefix = r'^data:image/.+;base64,'
20
+
21
+ from PIL import ImageOps
22
+ import oss2
23
+
24
+
25
+ class OSS2:
26
+ def __init__(self, bucket):
27
+ self.bucket = bucket
28
+
29
+ def get_download(self, object_name, download_name=None, minute=5):
30
+ """
31
+ 获取文件下载地址
32
+ :param object_name: OSS 路径
33
+ :param download_name: 下载重命名
34
+ :param minute: 请求超时时间
35
+ :return: 授权URL
36
+ """
37
+ params = None
38
+ if download_name:
39
+ params = {"response-content-disposition": 'attachment;filename="{0}"'.format(quote(download_name, 'utf-8'))}
40
+ url = self.bucket.sign_url('GET', object_name, minute * 60, params=params)
41
+ return url
42
+
43
+
44
+
45
+
46
+
47
+ def _pil_image_push_ossdir(bucket, image_pil, object_key, quality=85, format='jpeg'):
48
+ import io
49
+ print_with_datetime('upload object_key {}'.format(object_key))
50
+ oss_path = None
51
+ for _ in range(5):
52
+ try:
53
+ img_byte_arr = io.BytesIO()
54
+ image_pil.save(img_byte_arr, format=format, quality=quality) # 根据实际图片格式选择保存格式
55
+ img_byte_arr.seek(0) # 回到开始位置
56
+ bucket.put_object(object_key, img_byte_arr)
57
+ oss_path = object_key
58
+ return oss_path
59
+ except Exception as e:
60
+ print_with_datetime('error, upload oss failed, {}'.format(str(e)))
61
+ return oss_path
62
+
63
+
64
+
65
+ def resize_keep_hw_rate(image, tar_res):
66
+ w, h = image.size
67
+ if w > h:
68
+ tar_w = tar_res
69
+ tar_h = int(tar_w * h / w)
70
+ elif h > w:
71
+ tar_h = tar_res
72
+ tar_w = int(tar_h * w / h)
73
+ else:
74
+ tar_w = tar_res
75
+ tar_h = tar_res
76
+ image = image.resize((tar_w, tar_h), Image.LANCZOS)
77
+ return image
78
+
79
+
80
+ def decode_img_from_url(url):
81
+ import requests
82
+ from io import BytesIO
83
+ for _ in range(5):
84
+ try:
85
+ content = requests.get(url, stream=True, timeout=10).content
86
+ img = Image.open(BytesIO(content))
87
+ img = ImageOps.exif_transpose(img)
88
+ break
89
+ except Exception as e:
90
+ print_with_datetime(f"url decode error {url} {str(e)}")
91
+ img = None
92
+ return img
93
+
94
+
95
+ def make_sub_file_folder(file_path):
96
+ subdir = file_path.split(os.path.basename(file_path))[0]
97
+ os.makedirs(subdir, exist_ok=True)
98
+
99
+
100
+ def get_random_file_name():
101
+ timing_cur = datetime.now().timestamp() * 1000000
102
+ timing_cur = str(int(timing_cur)) + f'-{random.randint(0,99999999)}'
103
+ return timing_cur
104
+
105
+
106
+ def print_with_datetime(string):
107
+ print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: {string}', flush=True)
108
+
109
+
110
+ def b64_pil(base64Data, mode = 'RGB'):
111
+ try:
112
+ base64Data = re.sub(r_b64_prefix, '', base64Data)
113
+ imgData = base64.b64decode(base64Data)
114
+ image = Image.open(BytesIO(imgData))
115
+ except:
116
+ image = None
117
+ return image
118
+
119
+
120
+ def pil_b64(pil_img, fmt='jpeg', quality=75):
121
+ output_buffer = BytesIO()
122
+ pil_img.save(output_buffer, format=fmt, quality=quality)
123
+ byte_data = output_buffer.getvalue()
124
+ base64_str = base64.b64encode(byte_data).decode('utf-8')
125
+ if fmt == 'jpeg':
126
+ fmt_out = 'jpg'
127
+ else:
128
+ fmt_out = fmt
129
+ return f'data:image/{fmt_out};base64,' + base64_str
130
+
131
+
132
+ def get_ip():
133
+ hostname = socket.gethostname()
134
+ ip_address = socket.gethostbyname(hostname)
135
+ return ip_address
watermark.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import oss2
3
+ from PIL import Image
4
+ import gradio as gr
5
+ import numpy as np
6
+ from io import BytesIO
7
+
8
+
9
+
10
+ class WatermarkApp:
11
+ def __init__(self):
12
+ pass
13
+
14
+ def process_image(self, image):
15
+ watermark = Image.open('asset/chaojihui-v2.png')
16
+ # 获取水印的等比例缩放尺寸
17
+ wm_width, wm_height = watermark.size
18
+ target_size = 100
19
+ alpha = 0.3
20
+
21
+ if wm_width > wm_height:
22
+ new_wm_width = target_size
23
+ new_wm_height = int((wm_height / wm_width) * target_size)
24
+ else:
25
+ new_wm_height = target_size
26
+ new_wm_width = int((wm_width / wm_height) * target_size)
27
+
28
+ wm = watermark.resize((new_wm_width, new_wm_height), Image.Resampling.LANCZOS)
29
+
30
+ # 处理透明度
31
+ if alpha < 1.0:
32
+ wm = self.apply_transparency(wm, alpha)
33
+
34
+ # 确保原图是RGBA模式
35
+ if image.mode != 'RGBA':
36
+ image = image.convert('RGBA')
37
+
38
+ # 在整个图片上铺满水印
39
+ for y in range(0, image.height, new_wm_height):
40
+ for x in range(0, image.width, new_wm_width):
41
+ image.alpha_composite(wm, dest=(x, y))
42
+ return image
43
+
44
+ def apply_transparency(self, watermark, alpha):
45
+ """应用透明度"""
46
+ matrix = watermark.split()[-1].point(lambda x: x * alpha)
47
+ watermark.putalpha(matrix)
48
+ return watermark
49
+